综述
Android的消息机制主要是指Handler的运行机制。Handler的运行需要底层的MessageQueue和Looper的支撑。Looper中存在一个特殊的概念:ThreadLocal,它并不是线程,作用是可以在每个线程中存储数据。Handler创建的时候会采用当前线程的Looper来构造消息循环系统,Handler如何获取?使用ThreadLocal。注意:线程默认没有Looper,如果需要使用Handler就必须为线程创建Looper。主线程也叫UI线程,是ActivityThread,ActivityThread被创建时就会初始化Looper,这也是在主线程中默认使用Handler的原因。
概述
- Android为什么要提供Handler消息机制?
Android规定访问UI只能在主线程中进行,如果在子线程中访问UI,程序会抛出异常。ViewRootImpl通过checkThread()方法来对UI操作做验证。
系统通过提供Handler来解决在子线程中无法访问UI的矛盾。
- 系统为什么不允许在子线程中访问UI?
因为Android的UI控件不是线程安全的。多线程并发访问UI控件会导致UI处于不可预期的状态。
为什么不对UI控件的访问加上锁机制?缺点有两个:1)会让UI访问的逻辑变得复杂;2)降低UI访问效率
- 工作原理
Handler创建会采用当前线程的Looper来构建内部的消息循环系统,如果当前线程没有Looper(UI线程默认有),那么会报错。
FATAL EXCEPTION : Thread-43484
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
可通过Handler的post方法将一个Runnable投递到Handler内部的Looper中处理;也可以通过Handler的send方法来发送一个消息,这个消息同样会在Looper中处理。
分析
-
ThreadLocal的工作原理
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储后,只有在指定线程中可以获取到存储的数据,对于其他的线程来说则无法获取到数据。
使用场景:当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,可以考虑使用ThreadLocal;另外一个使用场景是复杂逻辑下的对象传递,比如监听器的传递。
ThreadLocal的set和get方法,它们所操作的对象都是当前线程的localValues对象的table数组,因此在不同线程中访问同一个ThreadLocal的set和get方法,它们对ThreadLocal所作的读写操作仅限于各自线程的内部。 -
消息队列的工作原理
消息队列在Android中指的是MessageQueue,有两个操作:1)插入 enqueueMessage; 2)读取 next 从消息队列中取出消息并将其从消息队列中移除。通过一个单链表的数据结构来维护消息列表。 -
Looper的工作原理
构造方法:Handler的工作需要Looper,没有Looper线程会报错。
为一个线程创建Looper:Looper.prepare();
new Thread(name){
@override
public void run(){
Looper.prepare();
Handler handler = new Handler();
Looper.loop()//开启消息循环
}
}.start();
Looper提供的方法:prepare():为线程创建Looper;prepareMainThread() 给主线程创建Looper;Looper可以退出,quit() 直接退出; quitSafely()设定一个退出标记,把消息队列中的消息处理完毕后才安全的退出。Looper推出后,Handler的send方法会返回false。建议不需要的时候终止Looper。loop(),只有调用了loop()后,消息循环系统才真正的起作用,是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回了null。
4. Handler工作原理
Handler发送消息的过程:向MessageQueue中插入一条消息,MessageQueue的next方法返回这条消息给Looper,Looper收到消息后开始处理,最终消息由Looper交由Handler处理,即Hanlder的dispatchHandler方法会被调用。
Handler的默认构造方法:
通过默认的构造器解释了在没有Looper的子线程中创建Handler会引发程序异常的原因。
主线程的消息循环
Android的主线程是ActivityThread,主线程的入口是main;
public static void main(String[] args) {
...
Process.setArgV0("<pre-initialized>");
Loop.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null){
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false){
Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexceptedly exited");
}
主线程的消息循环开始以后,ActivityThread还需要一个Handler来和消息队列进行交互。
这个Handler就是ActivityThread.H , 内部定义了一组消息类型,主要包含四大组件的启动和停止等过程。