最近正好也在复习一些知识,正好整理一下,handler的流程。mac上没有source insight,看源码还是不太方便,暂时没有好的工具。
先上一张流程图吧,图是随便画的,有时间在优化一下。
一般来说,很多人都会把handler的机制看传送带。这里我们直接从代码上开始看。分为两条线。 先看看如何使用:
class HandlerActivity : Activity() {
private val handler = object : Handler(Looper.myLooper()!!) {
//接收message
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
1 -> Toast.makeText(this@HandlerActivity, "handler msg", Toast.LENGTH_SHORT).show()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_handler)
Thread {
//发送message
val message = Message.obtain()
message.what = 1
handler.sendMessage(message)
}.start()
}
}
一、 先从发送开始: 从handler.sendMessage点进方法,回调到handler类中的
//跳到sendMessage
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
//跳到sendMessageDelayed
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
//参数不能小于0 delayMillis代表延时
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//创建messagequeue对象,并赋值 直接往下看调用了enqueueMessage
public boolean sendMessageAtTime(@NonNull 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.enqueueMessage 去看Message.enqueueMessage
* */
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//这句很重要,this代表handler,将handler作为参数传递,也是引起GC不可达的原因
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
//赋值为同步消息,跟同步屏障有关
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这个方法我们来张图吧,我们直接看msg跑到哪里去了。可以看红色的框。代码里进行了一些判断,主要对延时参数进行了判断,如果when比取出的消息队列头的when小,就直接添加,否者会遍历找到一个合适的位置添加进去。 至此,消息已经被加入队列了。
二、 接下来我们就直接看handler是如何拿到消息的回调的。可以用反推。但是一般大家都知道是从ActivityThread中开始的,那我们就直接看代码。直接看main中两个最主要的方法
//looper进行准备工作,调用了prepare(false),直接把代码拿过来
@Deprecated
public static void prepareMainLooper() {
//这里通过sThreadLocal不为null就会报错。这里说明sthreadlocal只能存放一个对象。
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//set方法,将looper对象传入我们直接去看threadlocal的set方法
sThreadLocal.set(new Looper(quitAllowed));
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//同理,通过localthreadmap,去取线程为key的值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void set(T value) {
//得到当前线程实例对象
Thread t = Thread.currentThread();
//将线程实例作为key,去查询map是否已经存值
ThreadLocalMap map = getMap(t);
if (map != null)
//没有的话,将线程实例作为key,looper作为value存储
//这里可以看出,一个线程对应一个looper对象
map.set(this, value);
else
createMap(t, value);
}
此时已经准备工作已经基本做完了。直接看looper。looper方法非常长,我就直接拿关键的代码说。
首先看loop方法中首先拿到了Messagequeue消息队列。然后第二个框,可以看到通过queue.next将消息取出来。 这里我们只看流程,不管休眠、唤醒、内存屏障之类的。可以看下面messagequeue的代码,for循环最后取出消息返回。那我们在看looper的代码,拿到msg之后做了什么。
可以在looper.loop中看到一句非常重要的代码,前面我们说过target就是handler对象,可以看出loop方法中,调用了handler.dispatchMessage将消息传递回去。
由下图可见,进入了handlermessage的回调。此时进入了接收消息。我们的流程就走完了。消息此时可以被接收方接收。
流程走完。能时间的尽量走一下流程,加深印象。