「这是我参与2022首次更文挑战的第13天,活动详情查看:2022首次更文挑战」。
主线程的消息处理过程
首先我们先看一下handler的通常使用方法
public class MainActivity extends AppCompatActivity {
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
Log.d("message",(String)msg.obj);
return false;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.obj = "abc";
handler.sendMessage(message);
}
}).start();
}
}
这里handler所在的是主线程,所以比较特殊不需要创建Looper并且开启循环,我们发现只需要在其他线程发送消息给主线程,主线程就可以拿到消息进行处理
如图可知在主线程中拿到了message
接着我们结合之前说过的ThreadLocal,MesssageQueue,Looper,一起来说说主线程和一般线程通常情况下的消息传递机制。
先看主线程,我们在其他线程用sendMessage方法,这个方法经过一系列的检测加工最终会到sendMessageAtTime这个方法
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);
}
其实在这之前可能还有一个疑问,那就是既然在其他线程中能拿到主线程的handler,为什么还要用sendMessage这种方法,直接调用handler里的CallBack的handleMessage方法进行处理不就好了?其实这样是不太好的,因为当有多个线程都需要主线程去处理消息的话,如果直接调用handleMessage必然会涉及到线程间的同步,如果某个Message的处理比较复杂,那么其他线程必然会一直被阻塞,所以采用消息队列作为缓冲是非常有必要的
我们接着说sendMessageAtTime方法,这个方法首先拿到当前线程的MessageQueue,前面介绍Looper的时候说过MessageQueue是存储在Looper中的,接着我们看看mQueue是怎么来的
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
查找源代码我们发现mQueue的确来自Looper,接着我们看看Looper又是怎么来的
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
查看源代码发现是Looper.myLooper(),这个方法之前我们在Looper这一节讲过,是从当前线程的ThreadLocal.ThreadLocalMap中取出的。 接着看sendMessageAtTime中的enqueueMessage方法
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这个方法对msg进行了进一步包装,然后往MessageQueue中插入了一条消息,至此handler的消息发送阶段结束。接着我们看消息的接收处理阶段
之前讲过,Looper会不断地从消息队列中取出消息,那么Looper取出消息后又是如何处理的呢?
查找源代码发现有这么一行代码
msg.target.dispatchMessage(msg)
原来Looper最终是调用了Handler的dispatchMessage方法
接着看
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
按照上面的写法,这里会走mCallback.handleMessage(msg),最终也就到了定义处理msg的地方了。