Android消息传递机制---Handler,MessageQueue,Looper.

来源:互联网 发布:雷霆队数据 编辑:程序博客网 时间:2024/05/20 14:24

1.Android消息机制概述以及背景


(1)Looper、Handler、Messagequeue三者共同实现了android系统里线程间通信机制。
如在A、B两个子线程之间需要传递消息,首先给每个子线程绑定一套handler、looper、Messagequeue机制,然后这三个对象都与其所属线程对应。然后A线程通过调用B线程的Handler对象,发送消息。这个消息会被Handler发送到B线程的Messagequeue中,而属于B线程的Looper对象一直在for循环里无限遍历MessageQueue,一旦发现该消息队列里收到了新的消息,就会去对消息进行处理,处理过程中会回调自身Handler的heandleMessage方法,从而实现了不同线程间通信。


(2)Android规定UI操作只能在主线程中进行,当在子线程中更新UI时,ViewRootImpl的checkThread方法就会抛出异常。不允许子线程访问UI是因为Android的UI控件不是线程安全的,如果在多线程中并发访问会使UI控件处于不可预期的状态。既然如此可能有读者会问那给UI控件的访问加上锁不就行了吗。事实是一:加锁会使UI访问的逻辑变得复杂,二是会降低UI访问的效率,锁机制会阻塞某些线程的执行。综上使用单线程来对UI控件进行访问无疑是比较好的。而Handler的主要作用是将一个任务切换到某个指定的线程中去执行,用在这里再合适不过了。



2.Android消息机制分析

(1)ThreadLocal工作原理
1.ThreadLocal是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,同时也只能在指定线程中获取存储到的数据。一般来讲,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑使用ThreadLocal。
2.ThreadLocal的set方法和get方法都是从调用该方法的线程里取出一个数组,然后再从数组中根据当前ThreadLocal的索引去查找出对应的value值,不同线程中的数组是不同的,这就是为什么通过ThreadLocal可以在不同线程中维护一套数据的副本并且彼此互不干扰。
3.ThreadLocal数据的存储规则是ThreadLocal的值在table数组中的存储位置总是ThreadLocal的索引+1的位置。
了解了ThreadLocal的原理后,在回头重新看看上面的概述中AB线程的通信,可知道Looper的底层工作原理正是基于ThreadLocal实现的。

(2)MessageQueue的工作原理
1.MessageQueue其实是通过单链表来维护消息列表的,但是是以队列的形式对外提供插入和删除消息操作,它只是一个消息队列。它包含两个主要操作enqueueMessage和next,前者是插入消息,后者是取出一条消息并移除。
2.next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里。当有新消息到来时,next方法会返回这条消息并将它从链表中移除。

(3)Looper的工作原理
1.Looper通过prepare方法来创建,通过loop方法来开启循环,它以无限循环的形式去查找是否有新消息,如果有的话就去处理消息,否则就一直等待着。prepareMainLooper方法主要是给主线程也就是ActivityThread创建Looper使用的,本质也是调用了prepare方法。
2..Looper的loop方法会调用MessageQueue的next方法来获取新消息,而next是一个阻塞操作,当没有消息时,next方法会一直阻塞着在那里,这也导致了loop方法一直阻塞在那里。如果MessageQueue的next方法返回了新消息,Looper就会处理这条消息:msg.target.dispatchMessage(msg),其中的msg.target就是发送这条消息的Handler对象。
3.Looper通过quit和quitSafely方法来终止,他们的区别是:前者会直接退出Looper,后者只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后才安全地退出。Looper退出之后,通过Handler发送的消息就会失败,这个时候Handler的send方法会返回false。如果没有终止Looper,子线程就会一直处于等待的状态,而如果退出Looper以后,这个线程就会立刻终止,因此建议不需要的时候终止Looper。

(4)Handler的工作原理
1.Handler就是用来处理消息的发送和接收之后的处理。它依赖于当前线程的Looper来构建内部的消息循环系统,如果当前线程中不存在Looper的话就会报错。Handler可以用post方法将一个Runnable投递到消息队列中,也可以用send方法发送一个消息投递到消息队列中,其实post最终还是调用了send方法。
它的源代码如下:
public void dispatchMessage(Message msg) {    if (msg.callback != null) {        handleCallback(msg);//当message是runnable的情况,也就是Handler的post方法传递的参数,这种情况下直接执行runnable的run方法    } else {        if (mCallback != null) {//如果创建Handler的时候是给Handler设置了Callback接口的实现,那么此时调用该实现的handleMessage方法            if (mCallback.handleMessage(msg)) {                return;            }        }        handleMessage(msg);//如果是派生Handler的子类,就要重写handleMessage方法,那么此时就是调用子类实现的handleMessage方法    }}private static void handleCallback(Message message) {        message.callback.run();}
disoatchMessage方法正是我们在Looper的工作原理分析里面提到的msg.target.dispatchMessage(msg),可以看到该方法对不同的情况作了判断并做出相应的操作,具体可见代码后的注释。



好了,Android的消息机制大致就是上面这样了。


1 0
原创粉丝点击