Android 的消息机制

来源:互联网 发布:电视直播软件安装包 编辑:程序博客网 时间:2024/06/03 11:17
第一次写博客,其实也不算自己写的,算是对 [Android开发艺术探索] 这本书的 一个笔记吧.下面的内容 全部来自于这本书,就当自己加深印象,总觉归纳.

Android 的消息机制

android的消息机制,主要是指Handler的运行机制

  • Handler底层
    • MessageQueue 消息队列
      • 它内部存储了一组消息,以队列的形式对外提供插入和删除工作
      • 虽然叫队列,但是它是单链表结构存储
    • Looper 轮询器
      • 它是消息的无限循环.它会一直以无限循环的方法去查看有没有新的消息,如果有就去处理消息,没就一直循环等待
      • 线程默认是没有Looper的,如果要使用Handler,就必须创建它.主线程也就是UI线程他是ActivityThread,在ActivityThread中会初始化Looper,这就是主线程可以使用Handler的原因.
      • ThreadLocal 它不是线程,它的作用是在每个线程中存储消息.它可以在不同的线程互不干扰地存储数据,通过ThreadLocal可以获得每个线程的Looper

简要概述

android的消息机制主要是指Handler的运行机制和它所附带的MessageQueue与Looper的工作过程.而平时开发主要只使用了Handler的上层接口.
  • 主线层 UI线程
    • 可以进行UI更新,但是不能进行耗时操作
  • 子线程
    • 可以进行耗时操作,但是不能更新,修改UI
    • 子线程操作更新UI会抛出异常.由ViewRootImpl的checkThread方法来决定的
void checkThread() {        if (mThread != Thread.currentThread()) {            throw new CalledFromWrongThreadException(                    "Only the original thread that created a view hierarchy can touch its views.");        }    }
为什么系统不允许子线程中访问UI
  • 因为Android的UI控件不是线程安全
  • 为什么不安全?
    1. 如果加锁变安全,会让UI访问逻辑变得想当复杂
    2. 降低了UI访问的效率,锁会阻塞某些线程执行
所以在更新UI和主线程ANR上,使用到Handler
Handler工作
  1. 创建完毕后,内部的Looper和messageQueue已经开始协同工作了
  2. 通过Handler的post方法将一个Runnable投递到Handler内部的Looper;也可以通过send方法发送一个消息到Looper.(其实post方法最终也是send方法完成)
  3. 当send方法调用时,会调用MessageQueue的enqueueMessage方法翻入到消息队列中,让looper轮询到时,会去处理这个消息.
  4. 最终消息中的Runnable或者Handler的handlerMessage方法调用.Looper是运行在Handler的线程中.

ThreadLocal的工作原理

Threadlocal 是一个线程内部的数据存储类
  • 它可以在指定的线程中存储,数据存储以后,只能在指定线程中读取,其它线程无法获取.
  • Looper,ActivityThread,AMS中用到ThreadLocal
  • 开发中使用:多个线程用到一个数据,但是每个线程需要独立的值,或者说独立的域.比如监听器的传递.还有Handler获取looper.

    案例

     final ThreadLocal<Boolean> mThreadLocal = new ThreadLocal<>();    mThreadLocal.set(true);    Log.i(TAG, "run: " + mThreadLocal.get());//true    new Thread("Thread1") {        @Override        public void run() {            mThreadLocal.set(false);            Log.i(TAG, "run: " + mThreadLocal.get());//false        }    }.start();    new Thread("Thread 2") {        @Override        public void run() {            Log.i(TAG, "run: " + mThreadLocal.get());//null        }    }.start();
    上诉案例 在主线中new出一个ThreadLocal<Boolean> 泛型为boolean类 然后在主线中给他设置一个true值 然后打印log get值true再new 出第二个线程 设置一个fslae 然后打印log get值为false有new 出第三个线程 不设置值 然后打印log get值为null

    ** 通过上面的测试结果 它们共同使用一个Threadlocal 但是都有自己的域 自己的值
    其它线程无法获取,无法修改 **

原码分析<开发艺术> 377页

消息队列的工作原理

android的消息队列是指MessageQueue
名称虽然叫队列,但数据结构是链表,链表在插入与删除的效率有优势
  • 插入
    1. enqueueMessage方法
    2. 在消息队列的链表中插入一条消息
  • 读取
    1. next方法
    2. 读取消息队列中的一条信息,并把它从消息队列的链表中移除
    3. 它是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里,当有新的消息到来是就执行next方法.
原码分析<开发艺术> 380页

Looper的工作原理

Looper在Android的消息机制中扮演着消息循环的角色.具体来说它会不停地从MessageQueue中查看是否有新的消息,有则立即处理,没有则一直阻塞在那里,查看它的构造方法中会创建Message
private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }
如何在线程中获得Looper和开启Looper

Handler的工作需要Looper,没有Looper线程就会报错,可以通过Looper.perpare()方法创建一个Looper,再使用Loop.loop();开启消息循环.

new Thread() {            @Override            public void run() {                Looper.prepare();                Looper.loop();            }        }.start();
Looper的perpareMainLooper()方法
public static void prepare() {        prepare(true);    }    private static void prepare(boolean quitAllowed) {        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper(quitAllowed));    }
这个方法是给主线程创建Looper使用的,本质也是通过prepare方法来实现.它在存在是为了在任何地方获得主线程的Looper,使用getMainLooper.
Looper的quit()方法和quitSafely()方法
//Looper中的方法public void quit() {        mQueue.quit(false);    }//MessageQueue中的方法        void quit(boolean safe) {        if (!mQuitAllowed) {            throw new IllegalStateException("Main thread not allowed to quit.");        }        synchronized (this) {            if (mQuitting) {                return;            }            mQuitting = true;            if (safe) {                removeAllFutureMessagesLocked();            } else {                removeAllMessagesLocked();            }            // We can assume mPtr != 0 because mQuitting was previously false.            nativeWake(mPtr);        }    }
  • quit() 退出
    • 直接退出Looper
//Looper的方法public void quitSafely() {        mQueue.quit(true);    }
  • quitSafely() 安全退出
    • 只是设置一个退出标记,只有所以消息全部处理完毕以后,才会退出.
  • Looper退出后,通过handler发送消息会失败,这是handler的send()方法会返回一个false.如果是子线程创建出来的Looper,就应该在所以消息处理完毕后调用quit()来终止消息循环,不然子线程会一直在阻塞等待状态.只要终止Looper,线程就会立即终止.
Looper的loop()方法
/**     * Run the message queue in this thread. Be sure to call     * {@link #quit()} to end the loop.     */    public static void loop() {        final Looper me = myLooper();        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }        final MessageQueue queue = me.mQueue;        // Make sure the identity of this thread is that of the local process,        // and keep track of what that identity token actually is.        Binder.clearCallingIdentity();        final long ident = Binder.clearCallingIdentity();        for (;;) {            Message msg = queue.next(); // might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }            // This must be in a local variable, in case a UI event sets the logger            final Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            final long traceTag = me.mTraceTag;            if (traceTag != 0) {                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));            }            try {                msg.target.dispatchMessage(msg);            } finally {                if (traceTag != 0) {                    Trace.traceEnd(traceTag);                }            }            if (logging != null) {                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);            }            // Make sure that during the course of dispatching the            // identity of the thread wasn't corrupted.            final long newIdent = Binder.clearCallingIdentity();            if (ident != newIdent) {                Log.wtf(TAG, "Thread identity changed from 0x"                        + Long.toHexString(ident) + " to 0x"                        + Long.toHexString(newIdent) + " while dispatching to "                        + msg.target.getClass().getName() + " "                        + msg.callback + " what=" + msg.what);            }            msg.recycleUnchecked();        }    }
  • loop()方法是开启Looper的轮询,也就是真正启动Looper
  • loop()方法就是一个死循环,跳出循环的方式是MessageQueue的next()方法返回一个null.

退出Looper

当Looper调用quit()方法调用时,Looper就会调用MessageQueue的quit()方法和quitSafely()方法,来通知消息退出队列 使得next()返回一个null.否则Looper就一直循环下去

处理消息Looper

当next得到了新的消息,Looper就会 msg.target.dispatchMessage(msg) ,实质上是msg.target就是发送消息的对象 对象调用了dispatchMessage(msg)这样的一个方法,特别就是这个对象调用的方法正在Looper中执行.就打到了将代码逻辑换到指定的线程执行.

Handler的工作原理

  • Handler 主要是进行发送/接受消息(处理消息)

发送消息

public final boolean sendMessage(Message msg)    {        return sendMessageDelayed(msg, 0);    }public final boolean sendMessageDelayed(Message msg, long delayMillis)    {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }public boolean sendMessageAtTime(Message msg, long uptimeMillis) {        MessageQueue queue = mQueue;        if (queue == null) {            RuntimeException e = new RuntimeException(                    this + " sendMessageAtTime() called with no mQueue");            Log.w("Looper", e.getMessage(), e);            return false;        }        return enqueueMessage(queue, msg, uptimeMillis);    }    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }
看到以上代码 发送消息就是send系列方法层层调用,post一系列方法也是通过send来实现的. 整个过程其实就是往消息队列中插入了一条消息.

处理消息

public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }    private static void handleCallback(Message message) {        message.callback.run();    }    public interface Callback {        public boolean handleMessage(Message msg);    }
  1. 收到消息交给dispatchMessage()进行分发
    • 判断msg.callback是否为null //callback就是Runnable对象 Handler通过post传入的Runnable参数
      • 如果不为null 就直接调用 handleCallback(msg);
      • 如果为null 在判断本类的 mCallback 是否为null
        • 不为空就 调用mCallback.handleMessage(msg) 其实这个方法相当于 平时我们创建一个Handler 重写handlerMessage来处理消息
    • 最后调用重写handlerMessage来处理消息

主线程的消息循环

android的主线就是ActivityThread
public static void main(String[] args) {        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");        SamplingProfilerIntegration.start();        // CloseGuard defaults to true and can be quite spammy.  We        // disable it here, but selectively enable it later (via        // StrictMode) on debug builds, but using DropBox, not logs.        CloseGuard.setEnabled(false);        Environment.initForCurrentUser();        // Set the reporter for event logging in libcore        EventLogger.setReporter(new EventLoggingReporter());        // Make sure TrustedCertificateStore looks in the right place for CA certificates        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());        TrustedCertificateStore.setDefaultUserDirectory(configDir);        Process.setArgV0("<pre-initialized>");        Looper.prepareMainLooper();        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        // End of event ActivityThreadMain.        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }
ActivityThread类中有main方法在main方法中调用了Looper.prepareMainLooper()和Looper.loop()来创建Looper和开启looper在ActivityThread类中还有一个名为H内部类 他就是Handler 内部定义了一组消息类型 包含了四大组件的启动和停止过程.  
1 0