在面试的过程中,反复的被问到Handler机制,在此对其做一个深入的剖析.下面Handler的源码都是参照API28的。
目录
Google源码对Handler的定义
看Handler源码开关注释:
Handler允许你发送和处理和线程的MessageQueue相关的Message和Runnable对象,每一个Handler对象都对应一个单一的线程。
一.先看看我们平时怎么使用Handler
平时我们Handler用的最多的就是子线程刷新UI,在Activity里new一个Handler,实现handleMessage方法,然后在子线程里调用handler实例发送消息,handleMessage处理消息,刷新UI.
二.那怎么Handler就能实现刷新UI的呢
就是,怎么就从子线程切到主线程了呢?建议大家有空看看操作系统的入门书籍,理解下线程和进程的概念.(我给大家推荐一本吧<<操作系统真象还原>>,第9章,详细介绍了线程和进程的概念和区别)
1.Handler创建
1)通过构造方法创建
第1种,不带Looper参数。
public Handler(Callback callback, boolean async)
第2种,不带Looper参数。
public Handler(Looper looper, Callback callback, boolean async)
注意这个Callback的实现方法handleMessage的返回值为true,则Handler的handleMessage不会被执行。看下面的这个源码就能明白:
/**
* 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);
}
}
-
子线程中创建Handler
在子线程中,不可直接调用Handler的构造函数创建Handler对象,否则会报错:
Can't create handler inside thread xxx that has not called Looper.prepare()
看源码可以知道Handler的构造方法获取的Looper为空就会报这个错
mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); }
那么疑问来了,我们平时使用Handler的时候,直接就是在Activity里new Handler,也没有什么设置Looper的操作,为啥就没有报错呢?请往下看。
-
在Activity里直接创建Handler
看Looper.myLooper()方法,官方的解释就是获取和当前线程关联的Looper。
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
只有Looper.prepare()方法被调用了,sThreadLocal才会有值。
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
那么就找到一个线索了,主线程肯定某个地方调用了Looper.prepare(),so where is it?
强烈建议详细(滴水不漏)看一遍ThreadLocal和Looper的源码,答案自然就揭晓了。
ThreadLocal的源码分析
ThreadLocal类并非是android特有的类,而是Java解决线程问题的一种手段。
- AtomicInteger (原子自增操作,线程安全)
https://blog.csdn.net/a260724032/article/details/81940785
https://blog.csdn.net/fanrenxiang/article/details/80623884 -
ThreadLocal定义
https://www.jianshu.com/p/6fc3bba12f38 (推荐)
https://www.jianshu.com/p/69ae8c213b30
https://www.imooc.com/article/45196
https://blog.csdn.net/u011860731/article/details/48733073看了这么多文章,也没有对ThreadLocal这个类完完全全地理解。大致的意思就是ThreadLocal通过变量备份的方法解决了线程安全问题。
ThreadLocal类源码分析:
1)get方法:获取当前线程t,然后获取当前线程t的ThreadLocalMap变量,通过传入当前ThreadLocal变量作为key,获取value。
**在Looper类中,ThreadLocal就起着为线程存放记录Looper的作用。**
Looper的源码分析
-
官方定义
为一个线程启动消息循环,线程默认是没有消息循环的。 -
ActivityThread
没有错,主线程的Looper就是在此绑定的。这就是为什么在主线程里可以直接new Handler的原因,系统已经给我们执行了Looper.prepare()和Looper.loop()方法。如果你勤学好问,那么,请看ActivityThread这个类,它并不是继承自Thread,更不是我们常说的主线程(UI线程)。Android主线程到底是个什么,你真正明白吗?
ActivityThread是如何启动的,可以参照我的这篇博客: (Android源码个个击破之Launcher) -
什么是主线程
https://blog.csdn.net/u011631275/article/details/47337385
在此我们需要明白主线程和Thread类没有什么关联,因为一旦使用了Thread,它就是一个子线程了。上面的博客,作者说到了主线程是如何启动的
所以要弄懂主线程的启动,你必须了解操作系统、Linux操作系统、进程、线程的概念,了解从手机开机到Lancher启动的全部流程。 -
Looper的其它作用场景
有的代码,它是要一直循环不断地执行的。比如操作系统,一启动,就会开启“死”循环,监视管理各个进程。
在Android里,很多处都会用到Looper消息循环机制, 如SystemServer的run方法,还有Activity的各个生命周期的方法都是通过Looper机制回调的。
可以这么说,Looper机制保证了线程永远不会执行完,是Looper给了线程生命。
-
loop方法
Looper的loop方法就是负责不断地从消息队列里取出消息,然后交给Handler分发处理。for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } try { msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } }
-
Looper.loop死循环为什么不会卡死?
https://blog.csdn.net/cshao888/article/details/78386593
涉及到Linux底层 pipe/epoll机制知识
https://blog.csdn.net/yangwen123/article/details/14118733
所以说android有庞大的知识框架,怎么学也学不完,学了java还要学Linux。
2)通过静态方法创建
注意8.0之前的Handler是没有静态方法来创建的。
静态方法最终调用的还是构造方法
//9.0开始有此方法
@NonNull
public static Handler createAsync(@NonNull Looper looper) {
}
//9.0开始有此方法
@NonNull
public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
}
//8.0开始有此方法
/** @hide */
@NonNull
public static Handler getMain() {
}
//8.0开始有此方法
/** @hide */
@NonNull
public static Handler mainIfNull(@Nullable Handler handler) {
}
2.Handler使用
上面创建了Handler,现在我们就来使用Handler
1)执行Runnable
/**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*
* @param r The Runnable that will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
看看官方的注释,执行runnable就是在Handler关联的线程里执行runnable。
可以看到postXXX各种方法,最终还是通过getPostMessage方法将Runnable封装在Message里
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
然后执行sendMessageXXX方法,最终也就是执行sendMessageAtTime方法。
post与sendMessage的区别就在于post,Handler的handleMessage是没有回调的,至于为什么没有回调下面会讲到。
/**
* 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);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
可见Handler的post方法,只是执行了一下Runnable的run方法。
2)发送Message
下面看sendMessageAtTime方法的源码
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
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);
}
这个queue是在Handler创建的时候,通过Looper获取的,在Looper的构造方法里实例化的:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
msg.target = this就是表示消息要给送给这个Handler。
下面看看queue.enqueueMessage这个方法
MessageQueue(难点)
https://blog.csdn.net/u013749540/article/details/79403851 (贴了不少源码,很难看懂)
- 官方定义
持有由Looper分发的消息列表的低级类,消息并不是直接添加到MessageQueue,而是通过和Looper相关联的Handler。 - enqueueMessage方法 (将消息加入消息队列)
1)如果和Handler绑定的线程死了,还去调用Handler发送消息就会报这个错误。
2) - next方法
用到了很多的Native方法。
3.言归正传
上面研究了这么多,已经讲明白了Handler是如何发送消息又是如何接受的消息-消息循环机制。
- Handler怎么就把线程跨了呢?
https://www.jianshu.com/p/465329c76770 (言简意赅的说明问题)
线程、Looper、Handler三者绑定,looper的loop()方法一直在绑定的这个线程里运行,并且解析执行Message,无论你Handler是在哪个线程发送消息,handleMessage始终在绑定的线程里执行。
记住这个定律:
1)决定跨线程的是Looper类,只有Looper与线程相关。
2)Handler类只是最顶层的一个接口,负责与Looper的消息队列交互。Handler与线程无关
4.错误的理解Handler
一般Handler在Activity(主线程)里直接new,在子线程里要开启Looper.prepare,然后再new Handler。
所以不知不觉我就形成了一种错觉:Handler在哪个线程里new的,它的消息就会分发到哪个线程。
带着这个“错觉”,然后去了小米面试,面试官就提出了问题:如果在主线程里创建Handler,然后使用子线程的Loopper呢?
确实,Handler有个带Looper的构造方法:
/**
* Use the provided {@link Looper} instead of the default one.
* @param looper The looper, must not be null.
*/
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
看注释里“the default one”,确定,如果我new无参的Handler,“the default one”就是线程已经绑定好的Looper。所以我的错觉“Handler在哪个线程里new的,它的消息就会分发到哪个线程。”如果加上一个限定,就是new调用的是Handler的无参构造方法就没啥毛病了。
验证带参Handler
首先定义一个子线程
public class TestThread extends Thread {
private static final String TAG = "TestThread";
private OnLooperPreparedListener onLooperPreparedListener;
public TestThread(OnLooperPreparedListener onLooperPreparedListener) {
this.onLooperPreparedListener = onLooperPreparedListener;
}
@Override
public void run() {
super.run();
Log.e(TAG, "TestThread name : " + getName());
Looper.prepare();
if(onLooperPreparedListener != null){
onLooperPreparedListener.onLooperPrepare(Looper.myLooper());
}
Looper.loop();
}
public interface OnLooperPreparedListener{
void onLooperPrepare(Looper looper);
}
}
在activity里调用
public class HandlerTestActivity extends AppCompatActivity {
private static final String TAG = "HandlerTestActivity";
private class MyCallBack implements Handler.Callback{
@Override
public boolean handleMessage(@NonNull Message msg) {
Log.e(TAG, "handleMessage: " + msg.what + "," + Thread.currentThread().getName());
return false;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
TestThread thread = new TestThread(new TestThread.OnLooperPreparedListener() {
@Override
public void onLooperPrepare(Looper looper) {
Handler handler = new Handler(looper,new MyCallBack());
handler.sendEmptyMessage(0);
}
});
thread.start();
}
}
输出日志
TestThread: TestThread name : Thread-2766
HandlerTestActivity: handleMessage: 0,Thread-2766
Handler的处理确实是在子线程。
好吧,到这里自己打自己脸了。所以,科学要严谨,要多做验证测试。
0 条评论