本文已授权微信公众号"Android技术杂货铺" 发布。转载请申明出处。
Android异步消息处理机制主要是指Handler的运行机制以及Hanlder所附带的MessageQueue和Looper的工作过程。
本文将通过结合源码(api-28)的形式,全面解析Handler和MessageQueue、Looper的关系。并分析Android异步消息机制的相关原理。在分析之前,先给出结论性的东西,便于在分析过程中有一个主脉络。
一.Handler
在分析Handler源码之前,我们尝试在程序中创建两个Handler对象,一个在主线程中创建,一个在子线程中创建,代码如下所示:
//代码片1
public class MainActivity extends Activity {
private Handler handler1;
private Handler handler2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler1 = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
handler2 = new Handler();
}
}).start();
}
}
运行代码之后,会报错: java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()
并且提示是第16行代码报错.说是不能在没有调用Looper.prepare() 的线程中创建Handler。
那我们在第16行代码前面尝试先调用一下Looper.prepare()呢,即代码如下:
//代码片2
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handler2 = new Handler();
}
}).start();
运行后发现的确没有报错了。
或许我们有疑问,为什么在子线程中加了Looper.prepare() 就不再报错了呢? 并且为什么第12行代码没有加Looper.prepare() 却没有报错。
先带着第一个问题(即 子线程加了Looper.prepare() 后不报错),来看看Handler的源码吧:
//代码片3
public Handler(Callback callback, boolean async) {
......
//仅贴出核心代码
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到,在第5行调用了Looper.myLooper()方法获取了一个Looper对象,如果Looper对象为空,则会抛出一个运行时异常,提示的错误正是 Can’t create handler inside thread that has not called Looper.prepare()!那什么时候Looper对象才可能为空呢?这就要看看Looper.myLooper()中的代码了,如下所示:
//代码片4
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
这个方法非常简单,就是从sThreadLocal对象中调用get()方法。
从名字我们可以大胆猜测一下,这是从sThreadLocal取出Looper,如果没有Looper存在自然就返回空了。
既然有get,应该也有set()方法,我们搜索一下(在Looper.java类中) sThreadLocal.set,果然有:
代码如下:
//代码片5
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()方法。
可以看到,首先判断sThreadLocal中是否已经存在Looper了,如果还没有则创建一个新的Looper设置进去。这样也就完全解释了为什么我们要先调用Looper.prepare()方法,才能创建Handler对象。同时也可以看出每个线程中最多只会有一个Looper对象。
现在我们来通过源码分析 代码片1第12行代码没有添加Looper.myLooper() 方法也没有报错 的原因。这是由于在程序启动的时候,系统已经帮我们自动调用了Looper.prepare()方法。查看ActivityThread中的main()方法,代码如下所示:
//代码片6
public static void main(String[] args) {
.....
//核心代码如下
Looper.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 unexpectedly exited");
}
可以看到,在第5行调用了Looper.prepareMainLooper()方法,