目录
继上一篇 Handler(上)那些事儿之后,我们已经了解了ThreadLocal、MessageQueue、Looper三者之间的联系,带着上一遍的疑问,我们来了解一下Handler源码部分。
Handler
Handler 构造方法
上诉构造方法来自与api28,不过没什么大区别.相比大家在开发中用到做多的应该是无参构造,我们先来看一看源码
public Handler() {
this(null, false);
}
public Handler(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;
}
我们可以看到无参构造方法内部其实调用了另一个构造方法.主要就是给mLooper mQueue mCallback mAsynchronous 赋值,分别表示当前线程绑定的Looper,当前线程绑定的Looper内部的MessageQueue消息队列,回调,是否异步.(异步本章节不作讲解)
乘胜追击,我们再看来来其他的构造方法:
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(boolean async) {
this(null, async);
}
我们可以看到内部其实最后都是调用Handler(CallBack,async)/Handler(Looper,CallBack,async)这两个构造方法,该方法主要就是进行mLooper mQueue mCallback mAsynchronous的赋值.
总结就是
- 若mLooper为空则默认过去当前线程绑定的Looper,不为空则用传入的Looper
- mQueue则是通过Looper内部的MessageQueue获取的
- mCallback默认为null
- mAsynchronous默认为false
所以这就解释了平时在子线程和主线程通信的时候,对于Handler的使用我们就只是通过无参构造方法创建,因为内部会获取当前线程也就是主线程的Looper,所以可以通过Handler去改变UI.
sendMessage/postxxx
通过sendMessage/postxxx方法可以去分发消息,但是Handler给我们提供了很多sendMessage和postxxx方法,有的作用是延时作用,有的时候传一个empty类型的Message,在此都不会一一分析,大家可以自行看看源码,但是所有的方法最后都会去调用sendMessageAtTime我们来看看.
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
便于理解保留sendMessageAtTime调用前的一个方法,便于理解.
- sendMessageAtTime(message,uptimeMillis),uptimeMillis表示具体执行的时间
- 所以可以看到sendMessageDelayed方法最终调用了sendMessageAtTime,传入的第二参数表示时间就是当前时间+加上延时的时间(若有延时则加,无则不加)
- sendMessageAtTime内部又调用了enqueueMessage,将message添加到队列中,通过调用MessageQueue#enqueueMessage方法.
那么后面的工作就交给了Looper#loop方法,去循环的调用MessageMessage#next方法.
在讲解Looper#loop的时候,提到将MessageMessage#next返回的Message,通过message.target#dispatchMessage方法回调该信息,那么我们来看看dispatchMessage方法.
dispatchMessage
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
其实dispatchMessage主要决定消息回调的先后顺序,
- 若msg.callback不为空则回调handleCallBack
- 若mCallBack不为空则回到mCallBack.handleMessage
- 最后都不会掉则回调Handler的handleMessage
msg.callback其实就是在构建Message的时候传入的Runnable
mCallBack其实就是Handler构造方法中传入的Callback
所以通过dispatchMessage,我们可以知道去获取Message的途径.
使用实例
public class MainActivity extends AppCompatActivity {
Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread() {
@Override
public void run() {
super.run();
Looper.prepare();
handler = new Handler(Looper.myLooper(), callback);
Looper.loop();
}
}.start();
findViewById(R.id.btn_add).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message message = Message.obtain();
message.obj = "hello";
handler.sendMessage(message);
}
});
System.out.println("main " + Thread.currentThread().getName());
}
Handler.Callback callback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
System.out.println("THread " + msg.obj.toString() + Thread.currentThread().getName());
return false;
}
};
}
上诉代码就是一种子线程Handler的使用. 那么有人会问为什么平时主线程Handler用不到Looper#prepare和Looper#loop? 因为Activity在创建的时候就帮我做了.
总结
这么一来整一个Handler的消息机制就都了解了,最后咱们还是要总结归纳整个流程.
- 首先需要通过Looper#prepare去初始化该线程的MessageQuene
- 通过创建Handler并获取当前线程的Looper,从而获取Looper的MessageQuene
- 通过sendMessage,将所要分发的Message添加到MessageQueue中.
- 由于Looper#loop会去不断的循环调用MessageQueue#next方法去获取可以分发的Message
- 最后通过Message.target#dispatchMessage去回调Message.
为了便于理解整个过程,我将Handler过程视为一个传送带的工作流程.
Handler就是工人,负责货物(Message)的组装,将货物(Message)拖放到传送带(MessageQuene)中,传送带(MessageQuene)被动力(Looper)驱使着去传送货物(Message),最终传送到终点再有工人(Handler)去组装.
进阶部分
储存Message是通过什么方式存储的.
- 存储结构是以单链表的形式存储
- 存储是依据执行Message的最终实际时间的先后去存储的.
MessageQueue线程唤醒/休眠
我们先来看看MessageQueue的next方法
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
(1) if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
if (mQuitting) {
dispose();
return null;
}
(2) if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
...
}
注:
mBlocked:表示线程是否休眠,该变量会在enqueueMessage中用到。
mMessages:表示当前执行的Message。
我们来看看(1)处代码
- 判断当前msg.when也就是执行message时间和now(当前时间)进行对比
- 若now>msg.when,则返回当前msg,并且mBlocked设置为false,因为当前时间大于可执行时间,所以不需要休眠等待,故线程当前状态为未休眠。
- 若now<msg.when则会记录下nextPollTimeoutMillis(距离执行msg的时间差),进行线程休眠。
那么记录下nextPollTimeoutMillis(时间差)有什么用呢? 大家知道looper.loop内部是有一个死循环去获取msg的,并且msg单链的排列又是按照时间排列,所以只有执行完当前msg再回轮到下一个,那么已知下一个msg执行时间和当前时间的差距,系统过让线程休眠,休眠的时间则为执行的时间差,nativePollOnce方法就是让线程休眠nextPollTimeoutMillis的时间,这样就避免的资源的浪费.
我们可以看到(2)处代码
- 若当前没有可执行的message或者是执行message的时间未到,没有可执行的任务所以需要将mBlocked赋为true。
大家想一想这一种情况,若当前时间为12点,下一个执行的msgA为12.30,因为有时间差所以线程休眠,那么此时我添加一个msgB他的执行时间为12.15,可是线程还休眠着无法执行msgB,所以需要唤醒线程,将msgB插入到msgA前,然后修改休眠时间进行休眠.那么唤醒这一步骤在enqueueMessage.
那么我们来看看enqueueMessage中线程唤醒的部分
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
(1) if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
(2) if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
(3) } else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
(4) if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
我们来看看3处重要代码。
- 若当前msg任务停止,则会抛出异常再回收资源。
- 我们可以看到needWake 和mMessages赋予了p,主要就是判断当前的mMessages是否为null,null就表示这没有可执行的Message;当前需要添加的Message的执行时间早于mMessages,需要插入到队列中,然后将当前线程状态赋予needWake。
- 这一块表示异步的msg,异步这一块不作讲解,后面会专门开一篇说异步。
- 最后根据当前线程是否休眠,来调用netiveWake去唤醒线程。
可以总结一下
- 若当前无可执行的msg,则线程会去休眠,此时mBlocked=true
- 若当前执行的msg的时间未到,则会记录下时间差nextPollTimeoutMillis,并调用nativePollOnce方法让线程休眠,此时mBlocked=true.
- 若在线程休眠的时候,插入新的msg,则会去唤醒线程,重新排列msg的顺序,去执行重新排列后下一个该执行的msg.
通过上诉总结,我们可以回答面试中的一种类似的题目.
1.为什么sendMessage加了延迟,线程不会堵塞?
2.因为当前可执行msg时间未到,系统进行休眠,那么插入新的msg如何操作?
这些类似的问题都可以从休眠堵塞,以及唤醒这一块解释即可.
为什么在Activity中的Handler直接new,而子线程中会报错且提示需要looper.prepare?
5240 Looper.prepareMainLooper();
5241 //创建ActivityThread 对象
5242 ActivityThread thread = new ActivityThread();
5243 thread.attach(false);
5244
5245 if (sMainThreadHandler == null) {
5246 sMainThreadHandler = thread.getHandler();
5247 }
5248
5249 if (false) {
5250 Looper.myLooper().setMessageLogging(new
5251 LogPrinter(Log.DEBUG, "ActivityThread"));
5252 }
5253
5254 Looper.loop();
上述代码在ActivityThread.java#main方法中,main方法是整个APP初始化的入口,可以看到它已经帮我们做了Looper.prepare 和 Looper.loop,主要是在主线程中,handler直接new即可,不需要再Looper.prepare 和 Looper.loop
在消息机制中,那么多方法内进行死循环,为什么系统不会卡死?
这个问题得要从ActivityThread.java开始说起,整一个APP的初始化在ActivityThread的main方法执行,在一个线程的生命在代码执行完后结束,那么如果执行完了这不就意味着这个线程就会消失,那么在main方法中不就意味着APP结束?所以就需要一个死循环或者是像MessageQueue中的线程休眠唤醒这样的流程,来保存整个main线程长久的生命周期。从而在MessageQueue的中对线程休眠和唤醒的流程。当然整一个app也不止一个消息机制,会有很多机制,所以会通过创建线程去执行。
下集预告
OkHttp的源码解读