一、前言
Handler,大家基本上多多少少都了解吧。我一开始只是用它来做发消息接收消息处理的功能,对它内部的运作还没去解析。最近看了下源码,算是弄懂了吧,来总结和记录下Handler内部的运作过程,本文章字数较多,只要耐心看完后,大家都对它都会有很深的了解。
二、主线程和子线程的使用
先来说说我们最常见的用法吧。主要分为两种情况:一是主线程使用,二是在子线程使用。
先看看主线程中的使用
//主线程中使用
class HandlerActivity: AppCompatActivity() {
private val mHandler = MyHandler()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 通过自定义的 Handler 发消息
mHandler.sendEmptyMessageDelayed(1, 1000)
}
// 自定义一个 Handler
class MyHandler: Handler() {
override fun handleMessage(msg: Message) {
Log.i("HandlerActivity", "主线程:handleMessage: ${msg.what}")
}
}
}
分析:
Handler主要是有发消息和接收消息后处理的作用
① mHandler = MyHandler(),看到MyHandler(),它是一个自定义的Handler,其中重写了handleMessage(msg)方法,不难理解,这是接收到消息后,对消息进行想要的出来里,这一部分就是处理消息。
② 这个mHandler是在HandlerActivity里面初始化的,没做处理的话默认是表示这个mHandler是主线程中初始化的,后面再细说handler跟线程的关系。
③ onCreate()中有,mHandler.sendEmptyMessageDelayed(1, 1000),这一部分就是Handler发消息的作用了。第一个参数是int类型的,是msg.what,这个后续再细讲message中的属性;第二个参数是这个消息延迟时间,1000表示是1s。
所以在这里的流程是,onCreate时,mHandler发了一个消息,msg.what是1,延迟1s执行,之后1s后,打印了"HandlerActivity", "主线程:handleMessage: 1"。
再看看子线程的使用
//子线程中调用
class HandlerActivity: AppCompatActivity() {
private var mHandler: Handler? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//创建一个子线程
Thread {
mHandler = MyHandler()
mHandler?.sendEmptyMessageDelayed(1, 1000)
}.start()
}
class MyHandler(): Handler() {
override fun handleMessage(msg: Message) {
Log.i("HandlerActivity", "子线程:handleMessage: ${msg.what}")
}
}
}
按照刚才在主线程中方法,子线程也这么做,看起来逻辑没啥,运行一下,发现报错了,看下日志:Can't create handler inside thread that has not called Looper.prepare(),翻译一下,大概就是说我们不能在一个没有调用Looper.prepare()的线程去创建Handler对象。
那为什么主线程我们不需要去手动调用Looper.prepare()就可以直接使用Handler呢?原来是启动App时,系统帮我们创建好了,App的入口,是ActivityThread.main方法,创建后主线程就可以正常运作了。
//ActivityThread.java
public static void main(String[] args) {
//......
Looper.prepareMainLooper();
//
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
//
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
重点看三个部分吧
1、Looper.prepareMainLooper(),点进源码发现最终调用了Looper.prepare()
2、创建Handler对象
3、调用Looper.loop()
回到刚才子线程使用handler,发现少了Looper.prepare()和调用Looper.loop(),加上去再试试
//子线程中调用
class HandlerActivity: AppCompatActivity() {
private var mHandler: Handler? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//创建一个子线程
Thread {
Looper.prepare()
mHandler = MyHandler()
mHandler?.sendEmptyMessageDelayed(1, 1000)
Looper.loop()
}.start()
}
class MyHandler(): Handler() {
override fun handleMessage(msg: Message) {
Log.i("HandlerActivity", "子线程:handleMessage: ${msg.what}")
}
}
}
发现不会报错了,此时handler就可以在子线程处理消息了。那Looper是什么东西呢,有什么作用,后面会讲到的。
小结一下:子线程使用handler时,要在两头添加Looper.prepare()和调用Looper.loop()。
三、Handler 内部是如何运转的?
Handler
发消息的入口是什么呢?就是刚才说的Handler,但实际上它有好多种构造方式,看下Handler.java的源码吧
//Handler.java
//构造一
public Handler() {
this(null, false);
}
//构造二
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;
}
//构造三
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
//构造四
public Handler(@NonNull Looper looper, @Nullable Callback callback) {
this(looper, callback, false);
}
//构造五
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
分析:
① 构造一最终是调用了构造二;构造三、构造四最终调用了构造五
② 两种构造的差别就是有无Looper传入。
③ 当没有Looper传入时,会调用mLooper = Looper.myLooper(),来看看Looper.myLooper()是啥,点进去
//Looper.java
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
看到ThreadLocal就可以知道,looper是和线程相关的,同时也说明了一个线程中只会有一个Looper。再看到注释,翻译下就是:如果要获取一个非空的Looper,那么必须在这个线程中调用一次Looper.prepare()。最终返回个Looper对象回去。
④ 当传入Looper时,只需要将传入的Looper保存起来,但是要注意的是,Looper不能为null。Handler创建时传入的Looper,一般是传Looper.myLooper(),就是跟线程有关,可以理解成,你在哪个线程创建Handler,就跟looper所在线程有关,looper在子线程,handler就在子线程;looper在主线程,handler就在主线程。只不过在传入前,要确定已经调用了Loop.prepare()。
知道了怎去创建Handler后,接着看看怎么它是怎么发送消息的吧
Handler发消息有很多种方法,看下源码
// Handler.java
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
public final boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean postAtTime(@NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
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();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
不要看它有那么发送消息的方法就怕了,仔细看下,到最后都会去到enqueueMessage方法中,最终去到了MessageQueue(消息队列)中的enqueueMessage方法,我们只要分析这个方法就可以了,注意,两个类的方法名都相同,不要混淆了,只要记住是从Handler到MessageQueue就可以了。至于MessageQueue的具体作用,后续会讲到,一步步来,Handler从创建到发消息基本已经讲完了,接下来看看之前说的Looper到底是啥!
Looper
Looper是啥?其实它就是个轮询器,我们从handler发送的消息,最终一般都会来到这个Looper(中间其他细节先省略,先看下Looper怎么处理消息),然后对消息进行分发处理。先看下刚才一直说的Looper.prepare()。
// Looper.java
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
分析:
① 只要调过Looper.prepare()后,再掉此方法,会报异常,也就是说一个线程只能调用一次Looper.prepare()。
② 第一次调用时,sThreadLocal.set(new Looper(quitAllowed)),会new一个Looper实例出来,其中会新建一个消息队列MessageQueue,还有会把当前线程记录下来。注意:在Looper创建时,就对应的生成一个消息队列
小结一下:至此的顺序,先是Looper.prepare()后,新建了个Looper,同时生成这个Looper的消息队列MessageQueue,并把当前线程记录下来,后续创建Handler时,就可以把Looper传进去或者调用Looper.myLooper()。
看完准备工作后,接下来看看Looper.loop()
// Looper.java
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// 省略一些代码...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
//省略一些代码...
} catch (Exception exception) {
//省略一些代码...
throw exception;
} finally {
//省略一些代码...
}
//省略一些代码...
msg.recycleUnchecked();
}
}
分析:
① 先得到当前线程的Looper对象,判断是否Looper.prepare(),然后再取到当前线程创建好的MessageQueue对象,之后跳进死循环不断的调用MessageQueue中的next方法取出消息队列中的Message消息,注意,当MessageQueue中没消息时,next方法会阻塞,导致线程挂起,并不是返回null,只有返回null的时候才return出死循环,后续会讲到这点。
② 如果msg不为空时,会调用它的target的dispatchMessage方法,这个方法在Handler中,最终都会处理Handler中发送的消息,只是分发处理的形式有三种,target后续在MessageQueue中会一起讲到
// Handler.java
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(@NonNull Message msg) {
}
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);
}
这里其实就是把你当前的消息分发出去,有三种情况:
1、msg.callback不为null时,直接调handleCallback方法,其实就是执行msg.callback里面的代码,那msg.callback是什么呢?还记得我们用Handler用post方法发消息时,传了个Runnable对象,可以理解成一些操作,比如打印日志或者弹吐司...,然后这个对象呢又给了getPostMessage()处理
//Message.java
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
首先新建一个消息m,再把传入的Runnbale对象保存在消息m的callback属性里,这样,消息的callback就不为null了,所以,一般我们用post方式发的消息,都会执行Runnable里面的代码,这里的msg主要用来存放这些代码。
2、msg.callback为null时,判断mCallback是否为null。注意,这个mCallback和上面的callback不同,mCallback它是Handler里面的接口,而callback是msg.callback里面的属性。还记得在构建Handler时,我们传入一个Handler.Callback对象吗?传入的Handler.Callback对象对应的就是这里的mCallback,也就是说,在使用Handler的时候,并不一定要继承Handler来处理消息,也可以通过实现Callback这个接口,来处理消息。有传则mCallback.handleMessage(msg)处理消息,注意,这个方法有返回值,如果返回了true,表示已经处理 ,不再调用Handler的handleMessage方法(第3点);如果mCallback为空,或者不为空但是它的handleMessage返回了false,则会继续调用Handler的handleMessage方法,直接走到第3点。
3、msg.callback为null且mCallback为null时,直接走handleMessage(msg),这个方法也就是我们在继承Handler时复写的方法,也就是说,发送的消息将在这里面处理。
③msg.recycleUnchecked(),处理后的消息,将走到这里,将消息回收到回收池里面,并标记这个msg.flags = FLAG_IN_USE,表示被使用了,然后消息里面其他信息都被清除。
注:Loop是一个循环器,它也有停止循环的功能,之前在第①点说到了,消息队列MessageQueue中next返回null时才退出循环,Looper提供了quit和quitSafely方法来停止Looper,后续在MessageQueue中会分析下流程。
小结一下:Handler是用来发消息和处理消息的,而Looper是一个轮询器,只要有用的消息都会把消息丢回给Handler通知去分发处理。简单点说,只要有Handler发消息,中间一系列操作后(MessageQueue接下来会将),消息Message会来到Looper里面,Looper分析完这个消息Message可以用后,就把消息丢回给Handler,按照Handler创建的方式去分发处理消息。
MessageQueue
前面一直说的MessageQueue,究竟是啥?其实它就是一个单向链表队列的数据结构,用来存放消息的一个队列,都叫它消息队列,每个单位都存有一个Message消息,也就是用Handler发送的消息,基本都会来到这个队列里面去排序,而排序的标准就是按照发送过来带有的延迟时间。还记得Handler发送消息的时候,最后调用的是MessageQueue的enqueueMessage吗?来看看这个方法
// MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
// 省略一些代码...
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// 一个Message,只能发送一次
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");
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
//【1】拿到队列头部
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;
} else {
//【3】消息插到队列中间
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;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
分析:
① 消息进来后,首先,判断了Message是否已经使用过了,如果使用过,则直接抛出异常,这是可以理解的,如果MessageQueue中已经存在一个Message,但是还没有得到处理,这时候如果再发送一次该Message,可能会导致处理前一个Message时,出现问题。接着标记一下msg.markInUse()表示已经使用了,再把延迟时间when给了msg.when,再接着拿到队列的头部消息mMessage。
注:msg.target是消息在Handler到MessageQueue之前,先把当前Handler与消息绑定的,如果msg.target为null时,会直接抛出异常。
//Handler.java
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);
}
② 判断消息队列是不是空的,是则将当前的消息放到队列头部;如果当前消息不需要延时,或当前消息的执行时间比头部消息早,也是放到队列头部。
③ 如果不是以上情况,说明当前队列不为空,并且队列的头部消息执行时间比当前消息早,需要将它插入到队列的中间位置。这里也有个死循环,首先把头消息取出来给prev,再把头消息的下一个消息给到p,进行判断,假如p == null,在这里就是头消息后面没有消息了,跳出循环,把当前消息插到头结点;或者when < p.when,当前这个传入的消息比p的延迟时间还要短时,跳出循环,把当前消息插到p的前面。如果没有符合条件的位置,就一直循环从头消息一个个找下去,找到符合的条件的位置插入消息。
④ 插入消息后,会去判断当前线程处于挂起的状态,则需要唤醒。nativeWake是NDK里面的方法,用于唤醒挂起的线程,看下needWake这个标志是怎么取到最终的值的:
1、【队列为空 || 消息无需延时 || 或消息执行时间比队列头部消息早】 && 【线程处于挂起状态时(mBlocked = true)】就会唤醒线程。
2、【线程挂起(mBlocked = true)&& 消息循环处于同步屏障状态】,这时如果插入的是一个异步消息,则需要唤醒。(同步屏障状态后续会讲到)
小结一下:handler发送消息后,会把消息传到消息队列MessageQueue,先检查消息是否异常,无异常标记消息被使用后继续判断消息应该插入的位置,插入成功最后判断是否要唤醒线程。
接下来说一下之前Looper.loop()里面提到的,MessageQueue的next方法,Loop.loop()会在这里面把消息取出来(next返回了Message)
// MessageQueue.java
Message next() {
//····
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
//【1】调用 NDK 层方法,线程阻塞挂起,进入等待
// nextPollTimeoutMillis = -1 时,进入无限等待,直到有人唤醒
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//【2】判断队列是否插入了同步屏障,是则只执行异步消息
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//【3】消息没到时间,重新计算等待时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//【4】消息时间已到,重新拼接链表,并返回该消息
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
// 没有消息,进入无限等待
nextPollTimeoutMillis = -1;
}
if (mQuitting) {
dispose();
return null;
}
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
// 只有for循环的第一次为 -1 ,
// 执行一次以后,pendingIdleHandlerCount变成 0,
// 不再执行空闲监听回调
if (pendingIdleHandlerCount <= 0) {
// mBlocked 为true,表明线程阻塞挂起
mBlocked = true;
continue;
}
// 这里设置为0,则上面的空闲监听器不再执行了
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0;
}
}
分析:
① 开始设定了(空闲监听器)pendingIdleHandlerCount = -1,nextPollTimeoutMillis = 0,进入死循环,首先调用了本地方法的nativePollOnce方法,用于阻塞MessageQueue,使其进入等待状态。
1、如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
2、如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
3、如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果其间有程序唤醒会立即返回。
这里的状态是0,所以刚进来时不会阻塞。
② 接着到了msg != null && msg.target == null,这里是判断队列是否插入同步屏障(后续会讲到,可以先跳过这一点),如果插入了则只执行异步的消息,同步屏障存在时,同步消息均无法执行,只有异步消息可以。
③ 取到了消息,先判断消息是否为null,如果不是则进入第④点,如果是则表明当前消息队列里面没消息,进入无限等待,nextPollTimeoutMillis = -1。
④ 消息不为null时,判断是否now < msg.when,如果是则时间没到,重新计算等待时间,如果不是则时间已经到了,mBlocked = false取消挂起状态,重新拼接链表,最后把消息返回出去。
⑤ 如果消息没被处理返回出去,接着往下走,判断mQuitting,为true则返回null回去,Looper.loop()取到null后,跳出loop()的死循环,也就结束了轮询,之前有提到,next里面取到的消息为null时结束轮询。Looper可以调quit和quitSafely
//Looper.java
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
//MessageQueue.java
void quit(boolean safe) {
synchronized (this) {
if (mQuitting) {
return;
}
// MessageQueue正在停止,用于next方法退出死循环
mQuitting = true;
if (safe) {
// 删除MessageQueue中的延时消息
removeAllFutureMessagesLocked();
} else {
// 删除MessageQueue中的所有消息
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
private void dispose() {
if (mPtr != 0) {
nativeDestroy(mPtr);
mPtr = 0;
}
}
首先把mQuitting设置为true,主要用于MessageQueue的next方法退出死循环,然后通过safe去判断逻辑逻辑,这里就可以看出Looper的quit和quitSafely的区别了
1、quit: 删除MessageQueue中所有消息
2、quitSafely: 删除MessageQueue中的延时消息
这样mQuitting为true就会return null出去了。再看下dispose(),其实就是调用了nativeDestroy方法,它是一个native方法,用于在底层停止MessageQueue。
⑥ 接着看,一般都会满足pendingIdleHandlerCount <= 0条件,然后mBlocked设置为true,继续continue,之后又设置了pendingIdleHandlerCount = 0,nextPollTimeoutMillis = 0,用于下个消息进来时用。
小结一下:next的操作主要是从队列里面取消息用于Looper里面回调给Handler分发处理,next中如果有符合条件的Message则会结束next里面的死循环,并返回Message,如果没有合适的消息则会一直循环去取,直到取到;若next返回了null出去,则Looper里面取到null,之后Looper也结束它的轮询(死循环)。
消息池
网上常有文章提到,在生成消息的时候,最好是用Message.obtain()来获取一个消息,这是为什么呢?先来看看这个方法:
// Message.java
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
可以看到,这是一个消息池,如果消息池不为空,就会从池中获取一个消息,达到复用的效果。那消息是如何被回收到池子里面的呢?之前在Looper.loop()里面对消息分发处理完成后,会做msg.recycleUnchecked()
// Message.java
private static final int MAX_POOL_SIZE = 50;
void recycleUnchecked() {
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) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
原来,消息池也是一个消息链表队列,除去了消息所有的信息以后,就会把消息加入队列头部。同时可以看到,消息池的最大缓存数量为 50 。消息池,可以避免大量的消息被创建,节省内存。所以,尽量通过 Message.obtain() 来创建消息。
三、Handler同步消息屏蔽
前面在MessageQueue.next()里面有说到了判断同步消息屏蔽,如果设置了则之后的只要是同步消息都会被屏蔽掉,只有异步消息才能执行。我们平时默认的发出的消息都是同步消息,那怎么设定异步消息呢?还记得在创建Handler时,要传入一个boolean值,传的话系统默认传false,这个Boolean表示是否设定为异步消息。
// Handler.java
public Handler(@Nullable Callback callback, boolean async) {
//···
// 设置为异步
mAsynchronous = async;
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//···
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
异步:async = true -> mAsynchronous = true -> msg.setAsynchronous(true)
启动/清除同步消息屏障
//MessageQueue.java
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
synchronized (this) {
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;
}
}
同步屏障是通过MessageQueue的postSyncBarrier方法开启的。
分析:
① 获取屏障的的唯一标示,标示从0开始,自加1。
② 从Message消息对象池中获取一个msg,设置msg为正在使用状态,并且重置msg的when和arg1,arg1的值设置为token值。但是这里并没有给tareget赋值。所以msag的target是否为空是判断这个msg是否是屏障消息的标志。
③ 创建变量pre和p,为下一步做准备。其中p被赋值为mMessages,mMessages指向消息队列中的第一个元素,所以此时p指向消息队列中的第一个元素。
④ 通过对队列中的第一个Message的when和屏障的when进行比较,决定屏障消息在整个消息队列中的位置,因为消息队列中的消息都是按时间排序的。
⑤ prev != null,代表不是消息的头部,把msg插入到消息队列中。
⑥ prev == null,代表是消息队列的头部,把msg插入消息的头部。
小结一下:开启屏障需要msg.target为null,postSyncBarrier中从Message消息对象池中获取一个msg,插入到消息队列中,这个msg的target == null,所以这个消息的作用就是开启同步消息屏蔽。
//MessageQueue.java
public void removeSyncBarrier(int token) {
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);
}
}
}
同步屏障的移除是在MessageQueue.java的removeSyncBarrier()方法。
删除屏障消息的方法很简单,就是不断遍历消息队列,知道找到屏障消息,退出循环的条件有两个,一是p.target == null,说明是屏障消息,二是p.arg1 == token,也说明p是屏障消息,因为在屏障消息入队的时候,设置过 msg.arg1 = token。找到屏障消息后,把它从消息队列中删除并回收。
四、总结
Handler机制最主要的部分有:
1、Handler:用来发送和接收处理消息的,一个线程可以有多个Handler对象。
2、Looper:轮询器,用于消息的遍历,使用Handler前需Looper.prepare(),创建一个Looper和一个MessageQueue,再把当前线程与Looper绑定。一个线程最多只有一个Looper。
3、MessageQueue:消息队列,用于存放Handler发送的消息,对消息进行时间排序,供Looper循环取消息,一个线程最多只有一个MessageQueue。
4、Message:信息的携带者,持有了Handler,存在MessageQueue中,一个线程可以有多个
五、关于Handler的经典问题
Handler的内存泄漏
通常 Handler 导致的内存泄漏,都是引起 Activity 的内存泄漏。可能日常是这样使用 Handler 的:
class HandlerActivity: AppCompatActivity() {
private val mHandler = MyHandler()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mHandler.sendEmptyMessageDelayed(1, 1000)
}
// 自定义一个 Handler
class MyHandler: Handler() {
override fun handleMessage(msg: Message) {
Log.i("HandlerActivity", "handleMessage: ${msg.what}")
}
}
}
在进入页面以后,发送了一个延时 1s 的消息,如果 HandlerActivity 在 1s 内退出了,由于 Handler 会被 Message 持有,保存在其 target 变量中,而 Message 又会被保存在消息队列中,这一系列关联,导致 HandlerActivity 在退出的时候,依然会被持有,因此不能被 GC 回收,这就是内存泄漏!
那么当这个 1s 延时的消息被执行完以后,HandlerActivity 会被回收吗?答案是会的!
因为消息被执行以后,Message 在放入缓存池之前,target 已经被清除,所以持有链就断开了,最终 HandlerActivity 将会被回收。
但这样并不意味着我们就可以不管内存泄漏的问题,内存泄漏始终是内存泄漏,如果是占用内存很大的页面没有被及时回收,有可能会导致 OOM 。
有两个办法可以解决这个问题:
① 将 MyHandler 改为静态类,这样它将不再持有外部类的引用。可以将 HandlerActivity 作为弱引用放到 MyHandler 中使用,页面退出的时候可以被及时回收。
② 页面退出的时候,在 onDestroy 中,调用 Handler 的 removeMessages 方法,将所有的消息 remove 掉,这样也能消除持有链。
个人还是觉得选择②去解决内存泄漏问题比较简单点。
为什么说 Android 是基于消息驱动的
当我们打开Apk时,系统会为Apk创建一个线程,这个就是主线程,而主线程的入口就在ActivityThread.java 中的 main() 方法。
// ActivityThread.java
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
AndroidOs.install();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
//【1】准备主线程 Looper
Looper.prepareMainLooper();
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
//【2】获取 Handler
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//【3】开启消息循环
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
这根本就是启动 Handler 最基本的方法,来看看 getHandler() 方法:
// ActivityThread.java
final H mH = new H();
final Handler getHandler() {
return mH;
}
简单看下 H 的部分代码:
// ActivityThread.java
class H extends Handler {
public static final int BIND_APPLICATION = 110;
@UnsupportedAppUsage
public static final int EXIT_APPLICATION = 111;
@UnsupportedAppUsage
public static final int RECEIVER = 113;
@UnsupportedAppUsage
public static final int CREATE_SERVICE = 114;
@UnsupportedAppUsage
public static final int SERVICE_ARGS = 115;
@UnsupportedAppUsage
public static final int STOP_SERVICE = 116;
// 省略部分代码...
public static final int EXECUTE_TRANSACTION = 159;
public static final int RELAUNCH_ACTIVITY = 160;
// 省略部分代码...
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case BIND_APPLICATION:
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
break;
case EXIT_APPLICATION:
if (mInitialApplication != null) {
mInitialApplication.onTerminate();
}
Looper.myLooper().quit();
break;
case CREATE_SERVICE:
handleCreateService((CreateServiceData)msg.obj);
break;
// 省略部分代码...
// 处理 Activity 生命周期相关的事件
// 如 onCreate/onResume/onPause等
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
// 省略部分代码...
break;
case RELAUNCH_ACTIVITY:
handleRelaunchActivityLocally((IBinder) msg.obj);
break;
case PURGE_RESOURCES:
schedulePurgeIdler();
break;
}
// 省略部分代码...
}
}
可以看到,几乎从 App 启动,到 Activity 相关的生命周期,Services 相关的生命周期,到 App 退出等等,都是通过 Handler 来驱动的。
注:App在启动的时候,系统就已经创建好了主线程的Looper。
Loop.looper() 死循环为什么不会阻塞主线程
App 的主线程中通过 Loop.looper() 开启了消息“死循环”,那为什么 App 依然可以正常运转,没有阻塞到主线程呢?主线程无时无刻都在处理消息,因为手机是有屏幕刷新率的,会不断的发消息通知刷新,光这一点就说明主线程不会被阻塞。