Handler机制对于初学者来说,是比较容易发懵的一个机制,但是实际上Handler相关的源码内容相对来说较少,也比较容易理解,对于作为源码旅程的切入点,还是非常合适的。
要讨论Handler这个话题,不可避免要先提到一个问题,什么是Handler?关于什么是Handler,互联网上资料也比较多,老铁们也可以参考阅读。咱们今天看看官方是怎么说的,我们打开android API 31 Handler类的源码,可以看到顶部有如下的注释,
源码里的注释,值得细读一下,毕竟没有什么比这个更具备说服力了,
A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread's {@link MessageQueue}.
先从Hanlder能干什么说起,Handler能干2件事情,发送(send)和处理(process),能发送和处理2样东西,Message和Runnable,可以发送很多的Message和Runnable,处理需要时间(是异步的),会存在不能及时处理的情况,为了应对这种情况,所以需要用一个队列来管理,就是上面说的MessageQueue(通过后面的分析会了解到,一个线程只能有一个Looper,MessageQueue就在这个Looper里面,所以注释说a thread’s MessageQueue,就是因为存在这层关系)。
Each Handler instance is associated with a single thread and that thread's message queue.
一个Handler只能与一个线程关联,上面已经提到线程里有MessageQueue。
When you create a new Handler it is bound to a {@link Looper}.
创建Handler的时候,会和Looper绑定,Looper和线程是一伙的,所以其实也是和线程绑定的。
It will deliver messages and runnables to that Looper's message queue and execute them on that Looper's thread.
Handler会把Message和Runnable递交给Looper的MessageQueue,然后在Looper所在的线程里进行执行(execute or process)。
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
Handler有2个主要用途,(1)在未来某个时间点执行一个操作,就是延时操作。(2)在自己的线程,让别的线程来执行一个操作。
读这篇博客的老铁,相信这两个用途已经玩的灰常灰常灰常6了。第1个就是延时执行一个操作,第2个最常见的就是从子线程(非UI线程)来更新UI线程,平时所说主线程和UI线程是等同的。咱通过一个实例作为切入点,实例咱跟平时大部分情况反着来,即从主线程发消息到子线程。Demo code如下,
package com.test.handlerdemo;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int MSG_TYPE1 = 0, MSG_TYPE2 = 1;
private Button btn_delay, btn_inter_thread;
private TextView tv_delay_content, tv_inter_thread_content;
private Handler threadHandler, uiHandler;
private Thread thread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_delay = findViewById(R.id.btn_delay);
btn_inter_thread = findViewById(R.id.btn_inter_thread);
tv_delay_content = findViewById(R.id.tv_delay_content);
tv_inter_thread_content = findViewById(R.id.tv_inter_thread_content);
//创建方法1,方法已经废弃,不推荐使用
uiHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
/*
//创建方法2
uiHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
//创建方法3
uiHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
*/
thread = new Thread(() -> {
Looper.prepare();
threadHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
Log.i(TAG, "threadHandler handleMessage thread id: " + Thread.currentThread().getId());
}
};
Looper.loop();
});
thread.start();
btn_inter_thread.setOnClickListener(v -> {
Log.i(TAG, "sendEmptyMessage thread id: " + Thread.currentThread().getId());
threadHandler.sendEmptyMessage(0);
});
}
}
咱先看下Handler的创建,这里声明了2个Handler: threadHandler, uiHandler, 前者用来往子线程发消息,后者用来往主线程发消息。先从uiHandler的创建来说起,这里用了3种方式来创建,第1种是老铁们熟知的,不过Android API 31当中已经标记为不推荐使用。先来看第1种,通过无参的构造方法调用到了this(null, false)即如下的构造:
这里对mLooper和mQueue进行赋值,这里也可以看出,Handler对应的Looper和MessageQueue只有一份,且MessageQueue是包含在Looper当中的。
注意到这段:
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
这段代码说,如果mLooper为空,就抛出一个异常:不能在没有调用Looper.prepare()的情况下在xxx线程里创建handler。但是我们实际这样new Handler的时候,并没有去调用Looper.prepare(),但是却没有抛出这个异常,这个是为什么呢,因为有人已经帮咱们调用过Looper.prepare(),就是主线程管理类ActivityThread已经帮咱们prepare过了,在ActivityThread.java的main方法中。先记住创建Handler必须要先Looper.prepare()(只是在UI线程中这一步已经做过,所以可以不用再做)。
第2种和第3种构造,从Handler层面来看没有区别,指定一个Looper,然后去构造,最终调用的是如下的构造方法:
咱们看下Looper.myLooper()和Looper.getMainLooper()有什么区别,我们先来看Looper.getMainLooper():
这里返回了sMainLooper对象,再看看sMainLooper对象是何时赋值的,在Looper类中只有一处赋值,就是prepareMainLooper的时候,
看到这里就很清楚了,在主线程中, getMainLooper()等同于myLooper()。上面提到过,主线程管理类ActivityThread已经帮咱们prepare过了,看过ActivityThread的男人一定知道,在ActivityThread的main方法中,就是调用了这个prepareMainLooper。
好的,能耐心看到这里,相信老铁一定是一个坚挺且持久的男人。总结一下结论:
1. Handler与线程,是多对一的关系,即一个Handler只能属于一个线程,但是一个线程可以有很多Handler。
2. 线程,Looper,MessageQueue,这三者是一一对应的,互相之间均不存在一对多的关系。
3. 在创建Handler之前,要先对Handler所属的线程的Looper进行prepare(其实就是创建Looper对象)。
时间仓促,水平有限,欢迎老铁同行们批评指正,讨论交流,敬请关注后续。