我对HandlerThread的理解是实现了一套消息通信机制的Thread。和它有关的两个重要概念是Looper(消息泵)和Handler(消息句柄),Looper可以理解成消息泵,当Thread运行起来后,Looper不断地从消息队列(MessageQueue)中取出消息并进行解释,若消息队列中没有消息可解释的消息(无消息或者有消息,但是消息还没到可解释的时间),则线程阻塞,有可解释的消息时(新的即时消息或者定时消息到了解释时间),线程被唤醒,继续解释消息。Handler用来操作线程的Looper,往Looper中添加消息,同时也负责消息的解释。所以两个HandlerThread可以通过Handler来完成线程通信。
因为Handler需要操作Looper,所以Handler需要持有Looper,Handler持有哪个线程的Looper,Handler就可以往哪个线程上发送消息和解释消息。HandlerThread消息的来源可能有多处,所以Looper可以被多个Handler持有,当需要往HandlerThread发送消息时,只需要用该HandlerThread的Looper实例化一个Handler,用该Handler就可以往HandlerThread发送消息。
每个线程都有自己独立的Looper(消息泵),所以Looper看起来像Thread的成员变量,但是Thread中没有一个叫Looper的成员变量,ThreadLocal<Looper>帮我们实现了这个需求,即:ThreadLocal<Looper>往Thread中添加了一个逻辑上的成员变量Looper。(也可以理解成每个Thread有自己独立的Looper副本),但我更喜欢逻辑成员变量这种理解方式。
HandlerThread 源码分析
HandlerThread类的代码相对简单,下面来看下比较重要的几个方法:
public class HandlerThread extends Thread {
@Override
public void run() {
mTid = Process.myTid();
//准备自己的消息泵
Looper.prepare();
synchronized (this) {
//获取自己的消息泵
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
//Looper准备好时的回调
onLooperPrepared();
//消息泵启动,不断接收消息,解释消息,直到消息泵退出,该方法会阻塞线程(等待消息到来)。
Looper.loop();
//走到这里说明消息泵结束,线程即将终止。
mTid = -1;
}
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
/** 获取线程的消息泵,
* 有了Looper,就可以new一个Handler来操作Looper,往该线程发送消息并解释消息。请区别getThreadHandler方法。
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
//返回looper
return mLooper;
}
//停止消息泵,线程终止,消息泵中未执行的消息会得不到执行。
//方法执行后往消息泵中添加消息会失败。
//详细请看MessageQueue.quit
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
//消息泵退出
looper.quit();
return true;
}
return false;
}
//消息泵中的消息执行完成后,退出消息泵,线程终止。
//队列中now之后的消息得不到执行,now以前的消息可以得到执行
//方法执行后往消息泵中添加消息会失败。
//详细请看MessageQueue.quitSafely
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
//消息泵安全退出
looper.quitSafely();
return true;
}
return false;
}
/**获取该线程的Handler,因为这个Handler由HandlerTherad生成,
*所以用该Handler进行消息通信时,发送方需要注意HandlerTherad是否认识这个消息,
*该handler用来发送HandlerTherad本身定义好的那些消息。
*一般会重写这个方法,返回一个能够处理一些了特定消息的Handler。
*/
// public Handler getThreadHandler() {
// if (mHandler == null) {
// mHandler = new Handler(getLooper()){
// @Override
// public void handleMessage(@NonNull Message msg) {
// switch (msg.what) {
// case 1:
// //do some thing;
// break;
// case 2:
// //do some thing;
// break;
// }
// }
// };
// }
// return mHandler;
// }
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
}
Looper实现原理
首先来看Looper 的初始化及获取,prepare方法利用ThreadLocal,在Thread类中添加了一个逻辑成员变量,并赋值,对ThreadlLocal不了解的可以看我另一篇文章。
一种新的方式去理解ThreadLocal及源码解析
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//初始化当前线程的Looper,looper可结束。
public static void prepare() {
prepare(true);
}
//quitAllowed 表示十分运行线程终止,主线程的Looper不允许终止。
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
//Looper只能初始化一次。
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
//looper构造方法
private Looper(boolean quitAllowed) {
//初始化消息队列
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
//获取当前线程的looper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
//初始化MainLooper
public static void prepareMainLooper() {
//初始化main looper,main looper不可终止。
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//获取MainLooper,用该Looper实例化一个handler可以往主线程发送消息。
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
下面来看Looper工作及结束时用到的方法:
final MessageQueue mQueue;//消息队列 待追查0
//启动当前线程的消息泵,主要代码节选。
public static void loop() {
//获取当前线程的Looper
final Looper me = myLooper();
if (me == null) {
//如果没有Looper则抛异常,提示先初始化当前线程的Looper
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//获取Looper中的消息队列。
final MessageQueue queue = me.mQueue;
//死循环
for (;;) {
//若queue中无消息,则线程阻塞
Message msg = queue.next(); // might block
if (msg == null) {
//走到这里说明调用了quit或quitSafely方法,队列正在退出,线程即将终止。
return;
}
try {
//解释消息 待追查1
msg.target.dispatchMessage(msg);
}
//将该消息放入消息池 待追查2
msg.recycleUnchecked();
}
}
//队列退出
public void quit() {
mQueue.quit(false);
}
//队列安全退出
public void quitSafely() {
mQueue.quit(true);
}
追查下待追查1,msg.target是个Handler,看一下Handler的dispatchMessage方法。
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
//若msg有callback,则执行callback,不需要handler再对msg进行解释。这种消息称为Callback消息。
handleCallback(msg);
} else {
//走到这里的消息称为what消息,handler需要根据不同的what来做不同的事情。消息的种类在讲解Message时介绍。
//若有mCallback成员变量,则msg由mCallback解释。
//mCallback返回false,则继续由handler的handleMessage方法解释。
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//handler的handleMessage方法解释msg。
handleMessage(msg);
//这样写的原因是解释消息的方式可以有两种,
//一种是继承Handler,然后重写handleMessage方法,
//一种是构造Handler时传入Callback,由Callback对msg完成解释。
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
//继承方式解释msg
public void handleMessage(@NonNull Message msg) {
}
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(@NonNull Message msg);
}
//构造方法里传入的Callback解释msg
final Callback mCallback;
所以Looper类相对简单,主要是用来初始化当前线程Looper,启动当前线程Looper,和退出当前线程Looper,主要用到的知识点是ThreadLocal,可以发现Looper类中的好多方法都是static的,想一想为什么。
下面来看消息队列中的元素Message和与Looper交互的Handler。
Message
public final class Message implements Parcelable {
//定义了消息,因为消息由Handler解释,所以每个Handler有自己的消息空间,不用担心消息冲突。
public int what;
//消息参数1
public int arg1;
//消息参数2
public int arg2;
//消息参数obj,目前没发现它的用处
public Object obj;
//消息处理完后可以给Messenger回复消息,可实现双向通信。
public Messenger replyTo;
//消息的执行时间,milliseconds since boot, not counting time spent in deep sleep.
public long when;
//区别于arg1 arg2 用于传输更多数据。
Bundle data;
public void setData(Bundle data) {
this.data = data;
}
//解释该消息的handler
Handler target;
//消息的callback,有callback的消息不需要what,
//对此类消息的解释只是执行该callback,追查待追查1时可以看到。
Runnable callback;
//下一个消息,消息回收和消息队列会用到。
Message next;
//消息池的同步锁
public static final Object sPoolSync = new Object();
//消息池的头部节点
private static Message sPool;
//消息池的大小
private static int sPoolSize = 0;
//消息池最大大小
private static final int MAX_POOL_SIZE = 50;
//表示msg在用
static final int FLAG_IN_USE = 1 << 0;
//表示msg是异步msg
static final int FLAG_ASYNCHRONOUS = 1 << 1;
-
一个Message必须有target,该消息由target指定的handler来解释(同时handler指定了消息解释发生在哪个线程),若Message无target则该消息是个屏障消息(待追查0)。
-
通过对Handler的dispatchMessage方法的分析,若Message有callback(Runnable类型),则不需要指定what及其他参数(arg1,arg2,bundle,obj),应用场景是指定在某个线程上运行一段代码,如 mainHandler.post(Runnable r) ,这类消息我称之为callback类Message。
-
若Message无callback,则需要指定what,其他参数是可选项。该类消息由Handler的mCallback成员或着handleMessage方法解释,这类消息我称之为what类Message,what类Message发送方和接收方都需要明白what的含义。
读到这里应该可以领悟HandlerThread中的getThreadHandler方法和getLooper方法使用场景的区别。由getLooper方法返回的Looper构造出的Handler一般只用来发射callback类Message。
Message复用
由于Message在HandlerThread中使用频繁,所以一般不要自己去new Message,而是从Message池获取,这样可以省去频繁构造释放Message,从而提高性能。Message的获取一般使用Message的obtain方法,obtain有很多重载方法,这里只讲两个。
//用来获取what类Message
public static Message obtain(Handler h, int what) {
Message m = obtain();
m.target = h;
m.what = what;
return m;
}
//用来获取callback类Message
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
//Message池中获取Message对象。
public static Message obtain() {
//Message池同步锁
synchronized (sPoolSync) {
if (sPool != null) {
//若存在Message池,返回sPool指向的Message对象,
//sPool指向下一个Message
Message m = sPool;
sPool = m.next;
//清理msg
m.next = null;
m.flags = 0; // clear in-use flag
//Message池长度-1
sPoolSize--;
return m;
}
}
//若不存在Message池,则构造一个Message返回,
//当该Message被会回收时,若回收池未满则该Message会被回收进回收池,再需要Message时,可从Message池中取Message,
return new Message();
}
//将Message回收,追查待追查2
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
// 回收过程中标识该消息在用,清除其它信息。
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
//若回收池未满,则将Message放入Message池。
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
Handler
Handler主要有三个功能,
- 获取消息
- 往Looper的消息队列中添加消息或者删除消息
- 解释消息(追查待追查1已分析)
Handler的构造方法主要有三个参数,Callback、Looper、和async。Callback用来解析Message(请回看追查待追查1)。Looper用来指定Handler操作的Looper,进而指定解析消息时发生在哪个线程,若构造Handler时未指定Looper,则直接获取当前线程的Looper。async用来标识发送的消息是否是异步消息,待追查0。
public Handler(@Nullable Callback callback, boolean async) {
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;
}
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
获取消息、发送消息及删除消息:
//获取消息
public final Message obtainMessage(int what, int arg1, int arg2) {
//从消息池中获取消息
return Message.obtain(this, what, arg1, arg2);
}
//发送消息
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
//发送延时消息
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
//runnable生成callback类Message
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
//发送消息
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
//即时消息 when = SystemClock.uptimeMillis()
//定时消息 when = SystemClock.uptimeMillis() + delayMillis
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//发送定时消息
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);
}
//将消息放入消息队列
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
//如果是异步消息 待追查0,设置异步标志
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
//删除消息
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
}
//在队列头部插入一个消息,该消息的when=0
public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
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, 0);
}
还有一个方法可以打印出队列里的消息,可以用于调试。
public final void dump(@NonNull Printer pw, @NonNull String prefix) {
pw.println(prefix + this + " @ " + SystemClock.uptimeMillis());
if (mLooper == null) {
pw.println(prefix + "looper uninitialized");
} else {
mLooper.dump(pw, prefix + " ");
}
}
当目前为止还有追查0没有追查,涉及到MessageQueue,屏障消息(target=null的Message)和异步消息,这些内容在介绍MessageQueue时介绍。
MessageQueue
消息队列是一个比较成熟的概念,模型应该不难理解,MessageQueue在数据结构上使用的是有序单向链表,每次往队列插入消息时,会根据消息的时间将消息插入合适的位置,链表头的消息最先得到解释(一般是即时消息,或者定时消息到了解释时间,Message.when值最小),链表尾的消息最后得到解释(一般是定时消息,消息的解释时间未到,Message.when值最大)。每次取消息时,总是取链表头部的消息,若链表无可执行的消息(链表无消息,或者链表里的消息还没到该执行的时间)线程进入阻塞,直到有新的消息需要解释(队列里有消息到了执行时间,或者有新的即时消息被添加进来),线程唤起(唤起方式有两种,新的即时消息被添加进来是手动唤醒,定时消息到了执行时间是定时唤醒),线程唤起后将继续解释消息。当调用quit时,会将队列里的所有消息清除,消息队列退出,线程退出。当调用quitSafely时,会将队列里的未来消息(when>now)清除,执行完剩下的消息后,消息队列退出,线程退出。MessageQueue的工作原理并不复杂,万事就怕但是,但事情也并没有那么简单。
MessageQueue大部分时间的工作方式如上述所说,但是主线程有些消息有插队的需求,比如主线程每隔16ms需要重新渲染ui,当渲染ui的消息来到时,若不能插队,等之前的消息解释完之后再去渲染ui,难免会造成ui的卡顿。所以MessageQueue有两套机制来实现消息插队。
-
第一种是设置when=0的消息,然后将这个消息放在队列头部,(一般的消息when=now>0),Handler.sendMessageAtFrontOfQueue方法可以发送该类型消息,when=0的消息比即时消息先得到执行。
-
第二种是异步消息,异步消息的优先级大于同步消息,MessageQueue在取消息时若发现头部是个屏障消息(target=null)的消息,则只从队列中取异步消息进行解释,若无可解释的异步消息(无异步消息,或异步消息未到解释时间),则线程阻塞。只有将队列的消息屏障移除之后MessageQueue才会去取同步消息进行解释。异步消息相当于贵族,同步消息相当于平民,消息屏障相当于信号灯,当信号灯亮时,只允许贵族通过,平民等待。信号灯灭后,平民继续通过。
情况就是这么个情况,下面来看取消息操作的next方法
Message next() {
final long ptr = mPtr;
//pter=0表示队列退出了,返回null,回看下Looper.loop。
if (ptr == 0) {
return null;
}
//pendingIdleHandlerCount 队列空闲回调数量
int pendingIdleHandlerCount = -1; // -1 only during first iteration
//阻塞时长,
int nextPollTimeoutMillis = 0;
for (;;) {
//不知道是干嘛的(未来再看)
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞nextPollTimeoutMillis毫秒后唤醒我,
//nextPollTimeoutMillis=0表示不阻塞,
//nextPollTimeoutMillis=-1表示永久阻塞,队列中无消息,等待手动唤醒(新的即时消息到来)
//nextPollTimeoutMillis>0表示阻塞nextPollTimeoutMillis毫秒后自动唤醒该线程,发生在队列头部的消息未到解释时间,
//当队列头部的消息到解释时间了再叫醒我,我再来干活。
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) {
if (now < msg.when) {
//该消息未到解释时间,则计算阻塞时长,线程将阻塞
//nextPollTimeoutMillis毫秒后被唤醒(定时唤醒)
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//消息到了解释时间
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 {
//若没有消息,则设置阻塞时长为永久,只有手动唤醒线程(新即时消息到来)
nextPollTimeoutMillis = -1;
}
//队列退出,清理工作,mPtr会置空,返回空
if (mQuitting) {
dispose();
return null;
}
//计算idleHandlerListener的数量
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//若没有idleHandler,则不执行idleHandler的回调,回到for头部
//此时nextPollTimeoutMillis!=0,
//回到for循环头部执行nativePollOnce(ptr, nextPollTimeoutMillis)
//线程进入阻塞状态,等待唤醒
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue;
}
//将mIdleHandlers中的listener拷贝到新的数组。拷贝操作是为了尽早退出临界区
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
//回调IdleHandler
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
//注意这里退出了临界区。
//pendingIdleHandlerCount赋值0,在一个for循环内(一次next方法中),IdleHandler只会执行一次。
pendingIdleHandlerCount = 0;
//因为在执行IdleHandler的过程中(未在临界区)可能有新的消息被放入队列,
//所以不阻塞,回到for循环看看有没有消息。
nextPollTimeoutMillis = 0;
}
}
//队列退出,清理工作
private void dispose() {
if (mPtr != 0) {
nativeDestroy(mPtr);
mPtr = 0;
}
}
public static interface IdleHandler {
/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/
boolean queueIdle();
}
//存放IdleHandler,当队列为空时会回调queueIdle方法。
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
private IdleHandler[] mPendingIdleHandlers;
下面来看添加消息的enqueueMessage方法,屏障消息的添加不走这里(postSyncBarrier用来添加屏障消息)。
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) {
//若队列已经退出,则不能添加消息
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;
//将消息添加到队列头
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
//是否需要唤醒取决于是否已经阻塞,这里适用于普通消息
needWake = mBlocked;
} else {
//将消息添加到队列中部(头以后)
//走到这里对于同步消息来说,不需要唤醒线程,因为头部消息已经设置了定时唤醒。
//对于异步消息来说若头部是屏障消息且该消息的when小于消息队列中所有异步消息的when,则需要唤醒,
//唤醒后线程进入next方法的for循环,会重新计算休眠时长或直接解释该消息。
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//将消息插入到合适的位置
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
//找到消息添加的位置p
break;
}
if (needWake && p.isAsynchronous()) {
//若队列中存在when更小的异步消息,则不需要唤起线程
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
//若需要唤醒线程,则唤醒线程(这里是手动唤醒)。
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
来看消息屏障的添加及删除:
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
//消息屏障的添加不需要唤醒线程
synchronized (this) {
//token用来标识消息屏障,需要用该token来删除消息屏障
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
//消息屏障也有时间
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
//找到合适的位置
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
//插入到头部
msg.next = p;
prev.next = msg;
} else {
//插入到中部
msg.next = p;
mMessages = msg;
}
return token;
}
}
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
//查找屏障的位置
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
//未找到屏障,不科学,抛异常
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
//若屏障在队列中部,则不需要唤醒线程
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
//若屏障在头部
//新队列为空或者新队列头部不是屏障消息则唤醒线程
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
//回收消息
p.recycleUnchecked();
//队列未退出且需要唤醒线程则唤醒线程。
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
下面来看两个退出方法:
void quit(boolean safe) {
//若不允许退出,则抛异常
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
//
synchronized (this) {
//若已经退出,则返回
if (mQuitting) {
return;
}
//设置退出标志位
mQuitting = true;
if (safe) {
//安全退出
//清除所有未来消息
removeAllFutureMessagesLocked();
} else {
//不安全退出
//清除所有消息
removeAllMessagesLocked();
}
//唤醒线程做清理工作
nativeWake(mPtr);
}
}
//清除所有未来消息
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
//都是未来消息,则全部清除
removeAllMessagesLocked();
} else {
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
//n是未来消息的起点,
p.next = null;
//回收未来消息
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
//清除所有消息
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
//回收消息
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
主要方法讲完了,看看其它不是很重要的方法
//判断队列中是否有某个handler的消息
boolean hasMessages(Handler h) {
if (h == null) {
return false;
}
synchronized (this) {
Message p = mMessages;
while (p != null) {
if (p.target == h) {
return true;
}
p = p.next;
}
return false;
}
}
//删除消息
void removeMessages(Handler h, Runnable r, Object object) {
if (h == null || r == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
//在头部删除
while (p != null && p.target == h && p.callback == r
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
//在中部删除
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.callback == r
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
说到这里,突然想起来个问题,既然UI绘制走的是高优先级的异步消息,为啥卡顿还会发生呢?
卡顿产生的原因:16ms内未完成ui的绘制
两个原因:
- ui太复杂,16ms确实没绘制完成,一般不会有这么复杂的ui吧?
- 16ms的区间内,留给ui绘制的时间太少。不是走的特殊通道吗,为啥还会太少?虽然走的特殊通道,但绘制ui的消息最快也要等到当前消息解释完成后才能得到解释。如果当前消息的解释花费了很长时间,那么势必会挤占16ms的绘制区间。所以如果一个操作需要花费几毫秒以上的话,将它放到work thread执行吧。
突然又想到一个问题,主线程会阻塞吗?主线程如果阻塞了怎么办?
我的猜想,主线程也会阻塞,android是事件驱动的,当有事件来临时(如点击事件),应该会唤起主线程(其它线程用主线程的handler发送消息)。点击事件应该由系统传过来,通过binder到达binder线程,binder线程唤起主线程?只是猜想,后面再跟进这个问题。
nativePollOnce是个native方法,搜了一下底层是用epoll实现的,后面还需要再跟进一下这个知识点。
今天就先到这儿了。
关于消息插队的应用场景请看这篇文章,写的很好:
android屏幕刷新机制
网友评论