本篇文章介绍Android的消息机制,消息机制用于Android中的线程切换以及数据处理。所谓的消息机制,就是Handler的运行机制。Handler是消息机制的上层接口,我们开发中只需要使用Handler即可,除了Handler以外,底层还需要MessageQueue和Looper的支持。还有一个类ThreadLocal也会介绍下,下面将介绍整个消息的运行机制。
1-1、Android为什么会有消息机制 ?
在Android中,特定的线程只能去完成一些特定的操作,比较典型场景就是,我们需要在子线程中进行耗时的I/O操作,可能是读取文件获取进行网络访问,当耗时操作完成以后需要在UI上进行改变,由于Android的限制,不能够在子线程中更新UI,这个时候就需要消息机制把更新UI操作切换到主线程去。
1-2、子线程为什么不能更新UI ?
在Android中,UI视图控件是线程不安全的,多个线程并发访问可能会导致UI控件出现异常。
那为什么不可以通过加锁来解决这个问题呢?
加锁缺点:加锁机制会让UI访问逻辑变得负责,其次是会降低UI的访问效率。
1-3、Looper内部无限循环为什么不会造成程序ANR ?
1-3-1、为什么会ANR?
一些耗时操作造成了主线程阻塞一定的时间,比如Activity在5s内不能响应用户事件,或者BroadcastReceiver的onReceive方法执行超过10s。
1-3-2、为什么Looper阻塞主线程却没有造成ANR?
主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。
2-1、Handler 消息机制上层接口,用于发送接收处理消息
Handler通post()和sendMessage()方法发送消息。最终调用的都是enqueueMessage方法,就是向我们的消息队列中去插入此消息。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler处理消息方法
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
2-2、Looper 消息轮询器,查询删除消息纷发给Handler处理
//创建Looper对象
Looper.prepare();
//轮询器查询消息队列的消息
Looper.loop();
2-3、MessageQueue 消息队列,单链表存储消息
MessageQueue主要包含两个操作:插入和读取消息分别使用如下两个方法。使用单链表方式,对于插入和删除操作更加的方便。
boolean enqueueMessage(Message msg, long when) {
}
Message next(){
}
2-4、ThreadLocal 线程数据存储器
内部使用数组来模拟Map来存储线程的数据,数据存储后,只可以在指定的线程中获取到存储的数据。消息机制中存储的是当前线程的Looper对象。
下面以主线程中创建Handler为例,来看下消息机制的源码,以及通过源码看整个系统的运行过程。
Hanlder类:
步骤1、Handler-构造函数:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//看步骤2 -> 主线程中会自动创建Looper对象,这里获取Looper对象,由Looper循环消息纷发给Handler处理
mLooper = Looper.myLooper();
// 没有Looper对象会抛异常
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 消息队列MessageQueue对象 作为Looper的成员变量
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
步骤5、Handler纷发消息,并处理消息(自己实现)
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 处理消息
handleMessage(msg);
}
}
/**
* 系统处理消息为空方法,这里使用时要重写该方法
*/
public void handleMessage(Message msg) {
}
上面2-1介绍,Handler发送消息时,是向MessageQueue队列中插入消息
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 向消息队列中插入消息并且返回是否插入成功
return queue.enqueueMessage(msg, uptimeMillis);
}
Looper类:
我们知道在主线程中创建Handler对象,系统默认创建Looper对象,以及执行Looper.prepare(),和Looper.loop()方法,就是说主线程中是默认有Looper对象的。
步骤2、Looper 初始化,创建Handler时候调用
// 创建Looper对象,并存到当前线程的ThreadLocal中
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));
}
// 从ThreadLocal中获取当前线程的Looper对象
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
// 用于存储Looper的TreadLocal对象
static final ThreadLocal sThreadLocal = new ThreadLocal();
步骤3、Looper轮询器不断查询消息队列中消息
public static void loop() {
// 获取Looper对象
final Looper me = myLooper();
// 没有Looper对象 抛异常
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
...................省略
for (;;) {
// 轮询消息队列 不断的取消息
Message msg = queue.next(); // might block
// 当消息队列中没有消息时候,跳出无限循环
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
.......................省略
try {
//步骤5 -> msg.target就是Handler对象,纷发消息
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
........................省略
msg.recycleUnchecked();
}
}
Android中用到消息机制的最常用的就是AsnycTask,一个异步任务执行框架。大家可以移步这篇博客。
Android多线程系列(一) AsyncTask基本使用以及源码解析