Handler是Android的消息机制,想必都熟悉和使用过。可能很多初学安卓的同学对报错:Only the original thread that created a view hierarchy can touch its views这个异常并不陌生,英文意思是只有创建视图层次结构的原始线程才能接触到它的视图,就是只能在主线程中更新UI,在子线程中更新UI就会报这个异常,有兴趣可以了解一下view的显示在屏幕过程中的原理,其中ViewRootImpl类就是负责View 的测量,布局,绘制操作,该类的requestLayout()方法请求view布局的时候就调用这个checkThread()方法判断当前线程是否是主线程。
问题:怎么在子线程中更新UI呢?
想必这个问题,做安卓开发的都会解决,可能都会异口同声的说使用Handler或者调用runOnUIThread方法可以切换到主线程中进行更新ui,是的,如果知道怎么使用后能知道原理,那后面使用的过程中也会避免一些问题,比如使用Handler不注意引起内存泄漏问题,了解原理能更巧妙的运用。其实Activity的runOnUIThread方法最终也是成员Handler对象切换线程的。
一、消息机制简介
整个消息机制主要是由Handler、MessageQueue、Looper这三个类实现的,其中Message就是消息的包装类。Handler负责消息的发送处理,MessageQueue就是存储消息的队列,负责管理消息、堵塞挂起和换新线程,Looper负责轮询获取MessageQueue的消息进行分发,一个线程只能有一个Looper,线程中的成员ThreadLocal存储这个Looper,而一个线程可以有多个Handler。
以下是使用Handler的例子,下面例子的消息都是运行在一个子线程中,别错误当成主线程更新ui的例子,初学者注意了哈:
如果在一个Activity使用Handler,最好定义一个静态类继承Handler,这样创建这个静态Handler对象后就不会持有Activity的引用,因为我是用Kotlin写的demo,下图Kotlin的内部类默认就是静态的。
线程中的Looper与Handler关联。如下图:
发送一个消息,消息就会分发到Handler的handleMessage方法中。
二、消息机制源码分析
1、Looper类源码分析
调用prepare方法,主要是创建Looper对象,并保存在线程私有的ThreadLocal本地存储区域,就是sThreadLocal成员变量中,线程之间无法访问彼此本地存储区域。
然后调用loop()方法进入循环模式,循环直到消息为空时退出循环,读取MessageQueue的下一条Message,把Message分发给相应的target。当next()取出下一条消息时,队列中已经没有消息时,会产生阻塞。等待MessageQueue中加入消息,并重新唤醒线程。
public static void loop() {
final Looper me = myLooper(); //获取TLS存储的Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //获取Looper对象中的消息队列
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) { //进入loop的主循环方法
Message msg = queue.next(); //可能会阻塞,因为next()方法可能会无限循环
if (msg == null) { //消息为空,则退出循环
return;
}
Printer logging = me.mLogging; //默认为null,可通过setMessageLogging()方法来指定输出,用于debug功能
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg); //获取msg的目标Handler,然后用于分发Message
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
}
msg.recycleUnchecked();
}
}
2、MessageQueue源码分析
获取消息的next()方法:Looper.loop()方法中循环调用MessageQueue队列的next()方法获取消息,nativePollOnce方法是堵塞操作,该方法第二参数nextPollTimeoutMillis代表下一个消息来到前,还需要等待的时长,当nextPollTimeoutMillis为-1时,当前队列没有消息,会堵塞当前线程进入休眠状态。
Message next() {
final long ptr = mPtr;
if (ptr == 0) { //当消息循环已经退出,则直接返回
return null;
}
int pendingIdleHandlerCount = -1; // 循环迭代的首次为-1
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞操作,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//当消息Handler为空时,查询MessageQueue中的下一条异步消息msg,为空则退出循环。
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//当异步消息触发时间大于当前时间,则设置下一次轮询的超时时长
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;
//设置消息的使用状态,即flags |= FLAG_IN_USE
msg.markInUse();
return msg; //成功地获取MessageQueue中的下一条即将要执行的消息
}
} else {
//没有消息
nextPollTimeoutMillis = -1;
}
//消息正在退出,返回null
if (mQuitting) {
dispose();
return null;
}
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//如果队列没有消息,pendingIdleHandlerCount等于-1或者0,所有就执行下面语句
if (pendingIdleHandlerCount <= 0) {
// 没有消息就赋值mBlocked状态为true,并且此时nextPollTimeoutMillis为-1,
//调用continue后循环一次调用nativePollOnce方法堵塞当前线程,后面如果有消息入列就会调用nativeWake方法唤醒线程
mBlocked = true;
continue;
}
...............................
}
}
进入队列的enqueueMessage方法:
MessageQueue是按照Message触发时间的先后顺序排列的,队头的消息是将要最早触发的消息。当有消息需要加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序。
boolean enqueueMessage(Message msg, long when) {
// 每一个Message必须有一个target
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) { //正在退出时,回收msg,加入到消息池
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的, 则进入该该分支
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非
//消息队头存在barrier,并且同时Message是队列中最早的异步消息。
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;
//前一个节点指向要插入的节点,就完成的一个节点的插入操作
prev.next = msg;
}
//队列如果为空,线程就属于等待状态,而且needWake值为true
//队列如果不为空,队列会一直循环获取消息并处理或者堵塞等待nextPollTimeoutMillis时长唤醒操作。
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
3、Handler的源码分析
Handler是否负责把自身引用赋值给Message中的target,并且向队列中加入该Message消息,uptimeMillis是延时多长时间处理该消息
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//给Message的target赋值自身引用,后面消息分发时调用自身dispatchMessage方法分发消息
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//调用队列的入列方法
return queue.enqueueMessage(msg, uptimeMillis);
}
//在loop()方法中,获取到下一条消息后,执行msg.target.dispatchMessage(msg),来分发消息到目标Handler对象。
分发消息流程:
当Message的msg.callback不为空时,则回调方法msg.callback.run();
当Handler的mCallback不为空时,则回调方法mCallback.handleMessage(msg);
最后调用Handler自身的回调方法handleMessage(),该方法默认为空,Handler子类通过覆写该方法来完成具体的逻辑。
消息分发的优先级:
Message的回调方法:message.callback.run(),优先级最高;
Handler中Callback的回调方法:Handler.mCallback.handleMessage(msg),优先级仅次于1;
Handler的默认方法:Handler.handleMessage(msg),优先级最低。
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//回调到实现Handler该方法中消费消息
handleMessage(msg);
}
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//其中mQueue是消息队列,从Looper中获取的
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//调用enqueueMessage方法
return enqueueMessage(queue, msg, uptimeMillis);
}
Handler的消息机制图解: