1 概述
Handler机制是Android的异步消息处理机制,用于在线程间传递消息,主要涉及到四部分:Handler、Looper、Message和MessageQueue。其中Handler是消息的发送者和处理者;Message是消息主体;MessageQueue是消息队列,Handler发送的消息都会放入MessageQueue中等待处理;Looper是MessageQueue的管理者,里面有一个loop方法,无限循环的从MessageQueue中取出需要处理的消息(如果有),并交个Handler处理。
这里有几个概念,一个线程只能有一个Looper对象和MessageQueue对象,而一个线程可以创建多个Handler对象,每一个Handler发送的消息只能由自己进行处理。通常的使用时主线程创建Handler对象,子线程使用这个Handler对象发送消息,由于这个Handler绑定的是主线程的Looper,所以会进入主线程的MessageQueue,也会由主线程的Looper分发给这个Handler回调他的handleMessage方法对消息进行处理。其中的原理看完下面的源码分析就可以理解了。
2 Handler对象创建问题
Android的UI线程是应用的主线程,是非线程安全的。Android规定,所有关于UI的更新都必须在主线程中执行,如果尝试在子线程中执行UI的更新操作会导致程序崩溃。
public class MainActivity extends AppCompatActivity {
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();
}
}
上面尝试在主线程和子线程中都创建一个Handler的实例,运行程序后会发现程序崩溃,日志打印如下:
E/AndroidRuntime: FATAL EXCEPTION: Thread-2
Process: com.xy.handlertest, PID: 3524
java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:227)
at android.os.Handler.<init>(Handler.java:129)
at com.xy.handlertest.MainActivity$1.run(MainActivity.java:21)
at java.lang.Thread.run(Thread.java:923)
报错显示无法在子线程中创建handler,原因是因为该线程中没有调用Looper.prepare()方法。
接下来可以进行改造,在子线程中调用该方法。
public class MainActivity extends AppCompatActivity {
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() {
Looper.prepare();
handler2 = new Handler();
}
}).start();
}
}
发现程序正常运行。接下来探究一下为什么需要调用Looper.prepare()方法后,才能够创建Handler的实例。可以查看Handler类的无参构造方法:
@Deprecated
public Handler() {
this(null, false);
}
可以看到,这里调用的是有两个参数重载的构造函数。同时,这里标志了废除的注解,说明这个方法是不建议使用的,原因就像是上面的例子一样,如果该线程没有调用过Looper的prepare()方法,就会出现程序崩溃,所以这里不建议使用无参构造方法。至于Looper的prepare()方法中做了什么,接着往下看。
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass(<