1. 前言
- 详细介绍Android异步处理以及相关的
Handler
、Message
、MessageQueue
、Looper
等源码分析。 - 本文略长,源码分析较多,请多注意代码中的注解。
- 本文资料来源网络公开资源,并根据个人实践见解纯手打整理,如有错误请随时指出。
- 本文主要用于个人积累及分享,文中可能引用其他技术大牛文章(仅引用链接不转载),如有侵权请告知必妥善处理。
2. 异步、UI主线程
2.1. 异步
- 多数情况下,耗时操作均应在子线程执行,在
UI主线程
处理结果或操作UI对象展示结果等,这就是Android常见的异步模式; - 如果直接在
UI主线程
处理耗时操作,将导致UI线程的阻塞,报错ANR
(application not responding
); - 如果在子线程处理耗时操作,同时又在子线程操作UI对象,这同样是android系统所不允许的,将导致
CalledFromWrongThreadException
。
2.2. UI主线程
的同步和不安全性
- app启动时,系统会为它创建一个进程,并创建一个
UI主线程
,这个主线程将一直存在直到该app退出并销毁。Android中所有的涉及UI的操作,都必须在UI主线程
操作。UI主线程
更新UI时,最终调用的是invalidate()
方法,而这个方法是线程不安全的,故UI主线程
也是不安全的。 - 你可以把这种状态理解为刷新方法
invalidate()
没有同步锁,如果Android不限制必须在UI主线程
操作UI对象,这时在多个子线程对同一UI对象进行修改,将导致多次UI对象的刷新,并且不保证中间某一和最终刷新结果符合预期。故Android对操作UI的限制,可以保证UI线程
的操作同步。
3. 消息处理机制Handler
、Message
、MessageQueue
、Looper
3.1. 形象释义
在这里为便于理解,我可以先简单地将把它们的关系形象化到一个虚构的邮政业务中(做初步的了解用,后面会有更加详细的分析)。可以想象出一个这样的世界,他们的世界很小,只有一个邮局,所有的邮件、信件业务都由这个邮局承担,并且每个人都是聋子和哑巴,只能通过邮局传递消息:
Handler
Handler
可以理解为一个发和收的装置,邮政局发件窗口就是发的装置,收件窗口就是收的装置,某人通过发件窗口发邮件到其他人或者政府单位,其他人或者政府单位通过收件窗口收取邮件Message
Message
就是邮件,这个邮件可以是一张明信片、信件,也可以是衣服、食物、杂物的包裹,由发件窗口发出,将来会由收件窗口接收MessageQueue
MessageQueue
可以理解为邮政局的仓库,这里每天都会有来自发件窗口新的邮件自动加入,又有邮件被邮递员检出Looper
Looper
可以理解为专门检出和发信的邮递员,他会不断的从仓库MessageQueue
中检出信件,并放到收件窗口
在这个例子中每个人,可以看做是一个子线程,而这个世界的统一政府可以看做是UI主线程
,邮局就是这么一套消息处理机制。
3.2. 专业释义
Handler
Handler
将Message
实例发送到MessageQueue
中,并消息机制处理后的Looper
分发给它的Message
实例Message
是线程间通讯的消息载体(?持疑,请看 3.3.2.2. 第2条MessageQueue
的enqueueMessage(...)
方法的源码分析)MessageQueue
是承载Message
的队列(?持疑,请看 3.3.2.2. 第2条MessageQueue
的enqueueMessage(...)
方法的源码分析),每个线程只有一个MessageQueue
,并由唯一的Looper
管理,遵循先进先出原则,Looper
无限循环获取其中的Message
,如没有Message
,Looper
将暂时阻塞Looper
被线程创建并且每个线程只有一个Looper
,管理MessageQueue
,并通过Looper.loop()
方法中循环获取MssageQueue
中得msg
,然后msg.target.dispatchMessage(msg)
发送msg
给对应的Handler
实例(msg.target
在消息处理过程中被赋值为发送这条msg
的Handler
的实例)
3.3. 源码分析
3.3.1. 示例
注意,这里有2种写法,主要是创建Handler
时所在的线程不同,但本质是相同的。后面会有详细说明。
public class MainAsyncActivityA extends AppCompatActivity {
@Bind(R.id.execute_btn)
protected Button execute_btn;
@Bind(R.id.show_tv)
protected TextView show_tv;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_async_act_a);
ButterKnife.bind(this);
execute_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
thread01.start();
//thread02.start();
}
});
}
//-------------------------------------第1种写法------------------------------
Thread thread01 = new Thread(new Runnable() {
@Override
public void run() {
//假设这里做了耗时操作......
String resultStr = "子线程01的处理结果";
//创建Message
Message message = Message.obtain();
message.obj = resultStr;
//Handler的实例handler01负责发送消息
handler01.sendMessage(message);
}
});
//在主线程创建Handler
Handler handler01 = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//接收消息
show_tv.setText(msg.obj.toString());
}
};
//-------------------------------------第2种写法------------------------------
Thread thread02 = new Thread(new Runnable() {
@Override
public void run() {
//假设这里做了耗时操作......
String resultStr = "子线程01的处理结果";
//创建Message
Message message = Message.obtain();
message.obj = resultStr;
//获取主线程的Looper实例
Looper looper = Looper.getMainLooper();
//在子线程创建Handler,传入主线程的Looper实例,则此Handler的接收者即为指定的主线程
Handler handler = new Handler(looper){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Logger.d("handler01-handleMessage()");
show_tv.setText(msg.obj.toString());
}
};
//Handler的实例handler01负责发送消息
handler.sendMessage(message);
}
});
}
3.3.2. 源码分析
主要分析Handler
、MessageQueue
、Looper
被创建 —> handler01.sendMessage(message)
发出消息 —> super.handleMessage(msg)
接收消息这中间的过程。
3.3.2.1. Handler
、MessageQueue
、Looper
创建过程
第1种写法,在
UI主线程
创建Handler实例Handler
实例在Activity
中被创建,即在UI主线程
被创建,而UI主线程
实际是由ActivityThread
类管理的。当一个Acttivity
被attach
(附着\绑定)在window
的时候,ActivityThread
实例同时被传入当前Activity
,用于管理Activity
的主线程操作,如下源码片段:public class Activity extends ContextThemeWrapper implements ...... { ...... final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window) { ...... //ActivityThread aThread赋值给mMainThread mMainThread = aThread; ...... }
ActivityThread
在app启动时被创建,并调用了Looper
类中的prepareMainLooper()
来创建一个唯一的主线程Looper
实例,如下代码片段://ActivityThread.java中的main入口方法,启动了主线程的Looper public static void main(String[] args) { ...... //调用了主线程专用的prepareMainLooper() Looper.prepareMainLooper(); ...... //调用了loop()启动looper功能 Looper.loop(); ...... }
//Looper.java中,看prepareMainLooper()方法的注释即可明白 /** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
当
UI主线程
Looper
被创建(prepareMainLooper()
)时,调用了Looper
的prepare(false)
,在prepare
中,new
了一个Looper
并放入ThreadLocal<Looper> sThreadLocal
中,如下片段://Looper.java中 /** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ 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)); }
new Looper
的过程中,创建了一个MessageQueue
的实例,并持有它//Looper.java中 private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
最后,创建
Handler
时,Handler
构造函数中会调用Looper.myLooper()
获取当前线程(UI主线程
)的Looper
实例,并赋值给Handler
的属性mLooper
,同时,获取了已创建的mLooper.mQueue
赋值给Handler
的mQueue
,持有以备使用。//Handler.java中 ...... public Handler(Callback callback, boolean async) { ...... mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } ......
第2种写法,在子线程创建属于
UI主线程
的Handler
实例
通过调用Looper.getMainLooper()
,获取的即为ActivityThread
所创建的主线程Looper
实例sMainLooper
,然后将这个looper
传入Handler
构造函数中,同样会赋值给Handler
的属性mLooper
,同时,获取了已创建的mLooper.mQueue
赋值给Handler
的mQueue
,持有以备使用。//Handler.java中 ...... public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } ......
3.3.2.2. Handler
发出消息 –> 接收消息
下面来了解下Handler
发出消息 –> 接收消息中间的详细过程。因为上文中有两种写法,并这两种写法本质相同,就以第1种写法讲解了。
发出消息,
handler01.sendMessage(message)
,进入Handler
类可以看到,最终调用的是Handler
的enqueueMessage(...)
方法,而Handler
的enqueueMessage(...)
方法最终调用的是MessageQueue的enqueueMessage(...)
方法,并给msg.target
赋值当前Handler
的实例,如下片段://Handler.java中 ...... 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实例来源于looper的属性 //(创建Looper实例时被创建的MessageQueue实例) 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); } ......
调用
MessageQueue
的enqueueMessage(...)
方法,是MessageQueue
队列添加Message
实例的过程,略复杂,包含了消息队列的排队原理,很重要,请看下面代码片段和中文注解将详细分析://MessageQueue.java中 ...... boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; //将属性mMessages赋值给新的Message p,多数情况下为null Message p = mMessages; boolean needWake; // 这里开始区分2种情况: // 1,是队列中无任何msg或者队列中的msg(或Message链) // 有被指定的发送时间,而这个时间大于新的msg,则给新的msg的 // next(是一个Message)赋值为队列中原有的msg(或Message链) // ,将生成的新的msg链(包含了next中的原有的msg或msg链) // 赋值给mMessages属性。 // 2,是如果队列中有之前的msg(或Message链)滞留在队列的头部 // 没有发出,如某msg指定了发送时间when而时间未到滞留 // 在队列中,那么就需要将最新添加进来的msg添加到即刻 // 发出的时间最近的(when < 新msg的when)原有的msg的next中, // 将原有的msg链中非即刻发出的还需要滞留在队列中的msg //(或Message链),添加在最新添加进来的msg的next中,相当于 // 重新排序了一下。 if (p == null || when == 0 || when < p.when) { //第1种情况: // 无任何msg或者队列中的msg(或Message链)有被指定的发送时间,而这个时间大于新的msg // 新的msg的next(Message)赋值为原有的msg,形成新的消息链 msg.next = p; // 再将MessageQueue的属性mMessages赋值为新的msg mMessages = msg; needWake = mBlocked; } else { //第2种情况: needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; //队列中有msg(或Message链),链中第1个msg的when已经过 //上面的判断必小于新的msg,则这个第1个msg还排在第1位 //循环找到when大于新msg.when的那个消息(或为null)为止, //将新的msg的next设置为找到的这个msg(包含它的链), //也就是将新的msg添加在整个队列中“即刻将发出”(when比新msg小的) //的msg的后面,而“非即刻将发出”(when比新msg大的) //或是为null的msg(或Message链)则排在新msg的后面,形成这么一个新的消息链 for (;;) { //第1个msg还排在第1位 prev = p; //原有的第2或第n个msg p = p.next; //找到when大于新msg.when的那个消息(或为null)为止 if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } //最后“非即刻将发出”(when比新msg大的)或是 //为null的msg(或Message链),排在新msg后面 msg.next = p; // invariant: p == prev.next //新msg排在“即刻将发出”(when比新msg小的)的msg(或Message链)的后面 prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; } ......
- 在这段代码中,我们可以发现,
Message
这个类并不像所想象的或之前了解的那么单纯,它承担了消息队列形成一个链条的关键作用,主要通过其next
属性(next
的类型就是Message
)构建消息队列中消息的排队。 - 反而是
MessageQueue
并没有发现它存在承担队列这个“实体”作用的代码,所以可以认为,Message
是一个队列链条的“实际承担者”(如,排队买票,队伍里的人和队伍本身),而MessageQueue
则是对Message
队列链条做排队
或整理
或被取用
等的操作类(如,队伍排队现场秩序的指挥员)。
- 在这段代码中,我们可以发现,
消息已准备好了,那么一直在运行的looper将怎么处理消息呢,请看
Looper.loop()
,由于主线程ActivityThread.java
中已启用了Looper
(Looper.loop()
方法),在msg
已加入MessageQueue
队列的情况下,looper
在不停的循环获取其中的消息,并分发给接收者handler
实例,我们看一下Looper
检出和分发功能的loop()
方法源码://Looper.java中 ...... /** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { //获取当前looper final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } //获取之前创建Looper时,创建的MessageQueue实例 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 (;;) { //queue.next()返回一条消息,其中其实是从msg链中取 //第1位的msg,也是无限循环的获取过程, //要么返回null,要么返回一个即刻需要发送的 //或指定时间已到的排第一位的msg 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.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { //这里就是最终的分发消息的调用,这里的msg.target //即绑定的handler,前面有提到 //Handler的enqueueMessage(...)方法中, //给msg.target赋值当前Handler实例 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
到需要发送的msg
后,调用msg.target.dispatchMessage(msg)
,又回到了Handler
中,最终调用了Handler
的handleMessage(Message msg)
方法,这样消息就给到了你定义的Handler
实例中重写的handleMessage(...){}
中,的如下代码片段://Handler.java中 ....... /** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { } /** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } .......
3.4. 其他写法
创建Handler
时,传入Callback
,消息机制和上面的没有区别,只是接收消息多了一个方式(Callback的handleMessage
):
Handler handler03 = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//如果这里为false,下面Handler的handleMessage将同样收到消息;
//如果返回true,则Handler的handleMessage不会收到消息
return false;
}
})
//下面是Handler的handleMessage
{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Logger.d("handler03-handleMessage()");
}
};
3.5. 子线程之间的消息传递
原理同子线程到主线程的消息传递相同。
简单描述下:如子线程A发送消息给子线程B,子线程A中只要持有子线程B的Looper
创建的Handler
实例,就可以正常调用handler.sendMessage
(message
)发送消息到子线程B,子线程B将在Handler
中接收消息并执行需要的任务。这种情况下,在子线程B中就必须在run
开头主动调用Looper.prepare()
,中间代码创建Handler
用于接收消息,以及在run
结尾调用Looper.loop()
启动looper
,同时,在任务彻底完成后,需要主动调用looper.quit()
退出looper
任务,则是当前子线程B将会自动销毁。