面试中经常会被问到handler的运行机制,如果你没认真研究过,可能只回答个大概。
比如,你会说这个过程涉及到的元素有:Handler,Message,MessageQueue,Looper,
Handler往MessageQueue发送Message,Looper不停的从MessageQueue取出Message进行处理。
这只是一个大概的过程,再细节的内容可能你也不了解了,今天我们就好好研究一下Handler,研究不透不放手的那种。
为什么我们要知道原理呢,会用不就行了吗?光会用还真不行,了解原理能帮助我们更好的使用,也能定位使用过程中遇到的问题,通过阅读源码,我们也可以学习源码的设计方式。

Handler往MessageQueue发送消息,消息入队列,Looper.prepare创建线程的looper,loop方法开启循环不断从MessageQueue中获取消息执行,最后回调给Handler处理。
Looper中用到了ThreadLocal来存储当前线程对象的Looper对象,ThreadLocal很神奇,注意用法。
Looper#loop方法会调用MessageQueue的next方法,因为next方法会阻塞,导致loop方法也会阻塞。
MessageQueue中通过单链表的数据结构来维护消息队列。它的next方法是一个无限循环的方法,如果消息队列中没有消息,则next方法一直阻塞在这里。当有新消息到来时,next方法会返回这条消息并将其从单链表中移除。

sendMessage实际是往MessageQueue插入一条消息,Handler#post方法其实也是调用sendMessage,只是要post的Runnable被作为Message的callback一起发送了,在处理消息时会先判断Message的callback是否为空,如下:
/**
* 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);
}
}
其中,dispatchMessage的逻辑如上,如果Message的callback不空,则执行callback,这个callback就是post的Runnable,如果Handler的callback不空,则执行callback的handleMessage,如果它返回false,则继续执行handler的handleMessage。
1、Activity a 启动一个Handler定时操作,启动Activity B,Activity B destroy时调用Handler#removeCallbacksAndMessages,Activity A中的handler操作是否继续?
答案是肯定的,继续操作。
Message中持有Handler,移除消息时判断了当前Handler和Message的Handler是否是同一个Handler。
也就是说对于主线程中new出来的多个Handler而言,他们都对应同一个Looper,同一个MessageQueue,但因为是不同的Handler对象,所以消息不会出现混乱。
2、Handler使用注意事项
private Handler mMainHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
// do something
}
};
通常我们在代码中使用Handler都是这样使用的,但是这样存在内存泄露的风险。
为什么会存在内存泄露的风险?
因为非静态的内部类或匿名内部类会持有外部类的引用,被延迟执行的Message中持有Handler的引用,Handler又持有Activity或Fragment的引用,导致Activity或Fragment不能正常释放,导致泄露。
解决方法:静态内部类+弱引用
private static class MyMainHandler extends Handler {
private final WeakReference<Activity> mActivity;
public MyMainHandler(Activity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
// do something
}
}
MyMainHandler handler = new MyMainHandler(this);
Activity或Fragment销毁时最好调用一下Handler#removeCallbacksAndMessages来移除队列中的消息。
3、主线程Looper创建过程
主线程的Looper是在ActivityThread的main方法中创建的。创建方法是调用Looper#prepareMainLooper方法,然后可以通过Looper#getMainLooper在任意地方获取Main Looper。
4、子线程中使用Handler的方式
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
}
}).start();
在子线程中如果所有事情都完成以后应该调用Looper#quit方法终止消息循环,否则这个子线程就会一直处于等待状态,无法结束。
Looper的quit方法实际也是调用MessageQueue的quit方法,MessageQueue#quit方法被调用之后next方法会返回null,这样Looper#loop方法会结束。
5、Looper不停的运行,为什么主线程没卡死
Looper#loop方法会调用MessageQueue#next方法,next方法时阻塞的死循环操作,有消息时处理返回新消息,否则阻塞。这样保证主线程不会退出。并且ActivityThread会开启Binder线程执行其他操作,比如和AMS的交互,生命周期相关的事件最终都会发送到主线程的消息队列中进行处理。
参考:https://blog.csdn.net/ZytheMoon/article/details/105850489
6、为什么子线程不能更新UI
Android的UI相关操作均没有加锁处理,不能处理多线程并发操作。实际是一种单线程模型来保证线程安全。
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
刷新UI时会在ViewRootImpl的requestLayout中检查当前线程。
参考:https://luisliu.cn/post/android/android-ui_thread/
网友评论