Android 多线程之 Handler 源码分析
文章目录
- Android 多线程之 Handler 源码分析
前言
这主要是一个讲解 Android 中多线程的系列,全部文章如下:
- Android 多线程之 Handler 基本使用
- Android 多线程之 Handler 源码分析
- Android 多线程之 HandlerThread源码分析
- Android 多线程之 AsyncTask使用源码分析
- Android 多线程之 IntentService使用源码分析
我们在上篇文章Android 多线程之 Handler 基本使用中讲解了 Handler 的使用以及解决了在使用 Handler 中内存泄漏的问题。
对于我们程序员来说,面对一个知识点,我们不仅要知其然,还要知其所以然,所以本篇文章主要讲下 Handler 的源码。
分析源码之前先来看下 Handler 的运行机制
一、Handler 的运行机制
实际上 Android 中的消息机制主要指的就是 Handler 的运行机制。
Handler 的运行需要以下对象的支持:
- Message
- MessageQueue
- Loop
- Handler
先来大概了解下:
1.1 Message
来看下 Android Api 中关于 Message 的定义:

可以看到,官方给出的定义是:
定义和包含了一些能够发送给
Handler的描述和任意数据的对象,这个对象包含两个额外的int类型的字段和一些额外的对象数据以便能够适应多种场景
虽然Message的构造方法是公共的,但是最好是通过Message.obtain()方法或者Handler.obtainMessage()方法来获取Message对象
笼统的来说,Message 其实就是 Android 中在不同线程中通信的载体,Message 对象中存储了我们不同线程间通信的数据。
1.2 MessageQueue
看下官方的介绍:

保存了一个由
Looper管理的消息列表,消息并不是直接被添加到MessageQueue中的,而是通过Handler和Looper的协作。
你可以通过Looper的myQueue()方法获得MessageQueue对象。
可以看到,MessageQueue 保存了线程间通信消息列表,他由 Handler 和 Looper 共同管理。本身是一种数据结构,存储了线程间通讯的信息。
1.3 Looper
官方的介绍:

Looper这个类通常为线程(Thread)用于运行一个消息循环,线程默认是没有和Looper相关的消息循环,通过执行Looper.prepare()去运行消息循环,然后执行Looper.loop()方法去处理Message,直到当前的消息循环停止。
从官网的介绍我们可以看出来,Looper 通常是和线程相关的,用于在线程上开启一个消息循环(MessageQueue),然后不断的处理消息循环中的消息,一直到消息循环终止。
其实 Looper 主要有的两大功能:
- 消息获取 :通过循环
MessageQueue,不断的取出里面的 Message,如果MessageQueue中 没有Message,则阻塞。 - 消息分发:将取出的
Message分发给Handler处理
1.4 Handler
依旧来看下官方描述:

一个
Handler允许你发送和处理在线程MessageQueue里面的Message或者Runnable对象。每个Handler实例都与单个线程和该线程的消息队列相关联。当你创建一个新的Handler对象时,它将和创建它的线程和该线程的消息队列相绑定,从此刻开始,它将发送消息和Runnable对象到和它绑定的MessageQueue,并在它们从MessageQueue中出来时处理它们。
官方已经说得很清楚了,Handler 对象可以发送和处理 MessageQueue 中的消息对象或者 Runnable 对象。并且一个 Handler 创建的时候,是要和当前所处的线程以及该线程中的 MessageQueue 相绑定的。也就是说一个 Handler 对象只能绑定一个线程。
1.5 Message、MessageQueue、Looper、Handler 的对象关系
MessageQueue中存放的是Message,一个MessageQueue中有可以多个Message,或者没有Message(此时阻塞)- 一个线程中有一个
Looper,并且只有一个MessageQueue,Looper去循环MessageQueue,有消息就发给Handler处理。 - 一个
Handler只能绑定一个Looper,但是一个Looper可以和多个Handler相关联
好了,Handler 源码分析里面的几个关键对象已经大体上说了一遍,下面就一起来看看源码,看看 Handler 是怎么工作的吧。
二、Handler 源码分析
2.1 在子线程中创建 Handler
如果我们需要在子线程中创建 Handler ,需要如下代码:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler threadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Message message = Message.obtain();
message.what = 1;
threadHandler.sendMessage(message);
Looper.loop();
}
}).start();
主要分为三步
- 创建 Looper: Looper.prepare();
- 创建 Handler:Handler threadHandler = new Handler()
- Handler 发送消息
- 开启 Looper 循环
创建 Looper
我们先从Looper.prepare()这里开始,prepare() 方法的源码如下:
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));
}
prepare 方法的参数很明显,是否允许退出。
prepare 方法首先通过 sThreadLocal.get() 获取 Looper,如果当前线程中的 Looper 不为空,那么抛出异常,提示“每个线程只能有一个 Looper”
那么 sThreadLocal 是什么呢?我们看下定义:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
sThreadLocal 实际上就是 一个指定类型的 ThreadLocal 对象,那么 ThreadLocal 的作用是什么呢?
实际上 ThreadLocal 可以在不同的线程之中互不干扰地存储并提供数据,通过 ThreadLocal 可以轻松的存储、获取每个线程的 Looper,这样就保证了 一个线程只能有一个 Looper 对象。
这里不做过多的讲解,想了解更多的话,请去这里
继续看关键代码:
sThreadLocal.set(new Looper(quitAllowed));
把通过 new Looper(quitAllowed) 的方式得到的 Looper 对象,放入到 ThreadLocal 的实例 sThreadLocal.set 中,继续看下 Looper 的构造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在构造方法里面新建了一个 MessageQueue 对象,然后把给当前的线程赋值给 mThread。最终返回一个 Looper 对象。
再来看下sThreadLocal.set(new Looper(quitAllowed));方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
通过getMap() 方法得到当前线程的 ThreadLocalMap 对象 map,顾名思义, ThreadLocalMap 存储是一个关于 ThreadLocal 的 Map,里面的
- key 是 ThreadLocal 对象,
- value 是 Object 对象。
接着判断 map 对象是否为空:
- 如果不为空,那么把传进来的 Looper 对象作为 value,把当前的 ThreadLocal 作为 key,存储到当前线程的 ThreadLocalMap 对象中,
- 如果为空,则新建一个 ThreadLocalMap 对象,把传进来的 Looper 对象作为 value,把当前的 ThreadLocal 作为 key,存储到当前线程的 ThreadLocalMap 对象中。
到此我们知道,我们新建了 Looper 对象后,是把当前线程的 ThreadLocal 和 Looper 对象建立映射关系,存储到 ThreadLocalMap 中的。
接下来看下第二步:创建 Handler 对象
创建 Handler 对象
分析代码:
Handler threadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
首先看到,我们 new 出了一个 Handler 对象。来看下 Handler 的构造方法:
public Handler() {
this(null, false);
}
很简单,无参构造方法调用有参构造方法,接下来看看有参构造方法:
public Handler(Callback callback, boolean async) {
// 检查当前的类是否是匿名内部类、成员内部类或者是本地类,如果是的话,就打印 log 可能引起内存泄漏,鼠标悬浮的时候也会提示。
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());
}
}
// 创建 Looper 对象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 获取 MessageQueue
mQueue = mLooper.mQueue;
// 参数赋值
mCallback = callback;
// 参数赋值
mAsynchronous = async;
}
首先要检查当前的类名,如果是匿名内部类、成员内部类或者是本地类,则给出可能有内存泄漏的提示
我们先看下这行代码:
mLooper = Looper.myLooper();
这行代码的作用主要是获取当前线程的 mLooper 对象。
我们去Looper.myMooper() 里面看下:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
sThreadLocal 是不是很熟悉,前面在创建 Looper 的时候,已经把 Looper 和 ThreadLocal 作为映射关联起来,存储到 sThreadLocal 中了。
在这里调用了 sThreadLocal.get 的 get() 方法返回了创建 Looper 时存入 sThreadLocal 中的 Looper 对象。
再来具体看下 sThreadLocal.get():
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
可以看到首先通过 Thread.currentThread() 拿到了当前的线程对象 t,然后根据 getMap(t) 拿到 ThreadLocalMap 对象,
getMap(t) 源码:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
就是返回当前线程的 t.threadLocals;
然后我们再通过 map 的 getEntry 方法拿到 ThreadLocalMap 的 Entry对象。那么这里的 ThreadLocalMap.Entry 里面存储的是什么呢?
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
对应我们这里就是:
- key 存储的是当前线程的 ThreadLocal 对象
- value 存储的 Object 是就是我们的 Looper 对象。
最后来说下 sThreadLocal.get() 的工作:
- 当在 ThreadLocalMap 不为空的时候,就从中取出对应当前 ThreadLocal 的 Looper 对象
- 当在 ThreadLocalMap 为空的时候,执行 setInitialValue() 方法去创建一个新的 ThreadLocalMap 赋值给 threadLocals,并且返回给 Looper 对象。
继续往下看:
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
从这行代码可以看出,我们在 new 出一个 Handler 对象的时候,必须先有 Looper 对象,否则就会抛出异常。所以这就是在子线程中使用 Handler 的时候,必须先执行prepare、创建Handler、loop的原因了。
下面的就比较简单了:
// 把创建 Looper 时创建的 MessageQueue 赋值给 mQueue
mQueue = mLooper.mQueue;
// 把传入的callback 赋值给 mCallback
mCallback = callback;
// 把传入的async 赋值给 mAsynchronous
mAsynchronous = async;
Handler 发送消息
Message message = Message.obtain();
message.what = 1;
threadHandler.sendMessage(message);
首先我们创建了一个 Message 对象,然后通过 Handler 的 sendMessage 方法把 Message 发送出去。
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
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);
}
我们看到 sendMessage 方法最终调用了 Handler 的 enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 方法。
在 Handler 的 enqueueMessage 方法中:
通过 msg.target = this; 把 Message 的 target 属性设置为当前的 Handler,这样就把该消息和要处理该消息的 Handler 做了绑定。
然后调用 MessageQueue 的 enqueueMessage(msg, uptimeMillis) 方法将消息添加到 MessageQueue 队列中。
至此,我们只是把消息加入到了队列中,但是还不能通过 Handler 的 handleMessage(Message msg) 方法处理,因为我们只是把消息加入到了 MessageQueue 中,MessageQueue 只是一个队列,要用 Looper 不断的去从 MessageQueue 中取消息给 Handler 才能处理消息。
接下来就看最后一步,开启 Looper 循环。
开启 Looper 循环
对应代码:
Looper.loop();
我们看下源码:
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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
首先通过 myLooper() 拿到 Looper 对象 me,然后再根据 me 拿到 MessageQueue 对象 queue。
然后我们看到一个无限循环
for(;;){
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
}
这个无限循环用于不断的从 MessageQueue 中取消息,如果 MessageQueue 中没有消息,那么就阻塞。
再来看下关键代码:
msg.target.dispatchMessage(msg);
msg 是从 MessageQueue 里面取出的消息。那我 msg.target 是什么呢?在第三步,我们已经在Handler 的 enqueueMessage 方法中将 Handler 对象赋值给 Message 的 target 属性了。所以这里的 msg.target 指代的就是 Handler 对象。
public final class Message implements Parcelable {
、、、//省略其他代码
Handler target;
、、、//省略其他代码
}
然后调用了调用了 msg.target(也就是 Handler 对象) 的 dispatchMessage() 方法。
来看下源码:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
一眼就看到了最后的 handleMessage(msg); 方法,是不是有点似曾相识呢?
我们再来看下我们最开始的在子线程中使用 Handler 的代码:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler threadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Message message = Message.obtain();
message.what = 1;
threadHandler.sendMessage(message);
Looper.loop();
}
}).start();
看到我们重写了 Handler 的 handleMessage 方法,也就是上面我们分析的 dispatchMessage 中调用的 handleMessage 方法,这样我们就把所有的流程关联了起来。
到此,相信已经知道为什么我们通过 Handler 发送消息,然后再通过 Handler 处理了。
2.2 在主线程中使用 Handler
我们在主线程使用 Handler 的时候,并没有执行 Looper.prepare()、Looper.loop() 方法,比如下面:
/**
* 主线程中的Handler
*/
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
这是为什么呢?
前面我们说过,Android 是单线程模型,其线程为 ActivityThread,主程序的入口是 main(); main() 方法源码如下:
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"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看到,虽然我们没有主动的执行 prepare、loop方法,但是在 ActivityThread 的 main() 方法里面执行了:
Looper.prepareMainLooper();
Looper.loop();
main() 方法里面还有个特殊的 Handler:sMainThreadHandler
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
继续看下 thread.getHandler():
final H mH = new H();
final Handler getHandler() {
return mH;
}
返回的是一个 H,这个 H 是 ActivityThread 的内部类,并且继承于 Handler :

H 这个类定义了很多常量,标示一些消息类型,比如上图的第一个
LAUNCH_ACTIVITY :启动 Activity 等。主要包含了四大组件的启动和停止等过程。
AnctivityThread 通过 ApplicationThread 和 AMS(ActivityManagerService) 进行进程间通信,AMS 以进程间通信的方式完成 ActivityThread 的请求后回调 ApplicationThread 中的 Binder 方法,然后 ApplicationThread 会向 H 发送消息,H 收到消息后会将 ApplicationThread 中的逻辑切换到 ActivityThread 中去执行,也就是切换到主线程中去执行。
既然知道 Handler 的原理,那么我们就对上一节的六个实例做一个源码分析吧。
三、上篇文章实例源码分析
3.1 使用 Handler 的 post() 方法更新 UI
3.1.1 使用:
//使用post方法直接更新ui线程
new Thread(new Runnable() {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
mShow.setText("使用post方法直接更新ui线程");
}
});
}
}).start();
3.1.2 分析
我们看下 mHandler.post() 方法:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
首先通过 getPostMessage®:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
根据传入的 Runnable 创建一个 Message 对象。
然后调用 sendMessageDelayed(getPostMessage®, 0) 方法,sendMessageDelayed 方法我们在上面见过,最终会调用 Handler 的 enqueueMessage(queue, msg, uptimeMillis); 方法。把消息放入 MessageQueue 中。由 Looper 去不断取消息给 Handler 处理。
3.2 使用 Handler 的 sendMessage() 方法更新 UI
3.2.1 实例
new Thread(new Runnable() {
@Override
public void run() {
Message message = mHandler.obtainMessage(7, "子线程中发布消息,更新主线程");
mHandler.sendMessage(message);
}
}).start();
3.2.2 分析
关键代码在
mHandler.sendMessage(message);
看下 sendMessage方法:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
我们看到,也是调用了 sendMessageDelayed(msg, 0) 方法,那么下面就和使用 Handler.post() 方法一样了,不在分析
3.3 使用 runOnUiThread() 方法更新 UI
3.3.1 实例
new Thread(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
mShow.setText("使用runOnUiThread更新ui线程");
}
});
}
}).start();
3.3.2 分析
我们看下 runOnUiThread 的源码:
@Override
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
先判断是当前的线程是不是 ui 线程,如果不是 UI 线程,就执行 Activity 中的 Handler 对象 mHandler.post() 方法,在主线程中执行;如果是在主线程,则直接在主线程中执行 Runnable 中的 run() 方法。
3.4 使用 View 的 post() 方法更新 UI
3.4.1 实例
new Thread(new Runnable() {
@Override
public void run() {
mButton5.post(new Runnable() {
@Override
public void run() {
mShow.setText("通过View的post方法更新ui");
}
});
}
3.4.2 分析
看下 View.post方法:
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
AttachInfo 的官方说明是:当 View 附加到父视图时向 View 提供的一组信息。
如果 attachInfo 不为空,执行 attachInfo 中的 mHandler post 方法,和 3.1 类似
如果 attachInfo 为空,执行 getRunQueue() 中的 mHandler post 方法,那 getRunQueue() 又是啥呢?
private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}
返回的是 HandlerActionQueue 对象,那么 HandlerActionQueue是啥?
/**
* Class used to enqueue pending work from Views when no Handler is attached.
*
* @hide Exposed for test framework only.
*/
public class HandlerActionQueue {}
注释中描述的很清楚,这个类用于当 View 没有附加到父 View 的时候,把待执行的工作放进队列。然后再 View 附加到父 View 之后再去执行之前添加的任务,有兴趣的可以具体了解下工作流程
相关文章地址:地址
3.5 子线程中创建 Handler(handler1)发送消息,在子线程中的Handler(handler1) 中处理,然后发送给主线程(mHandler) 去更新 UI
这个就不在分析,在上面的第二部分已经说得很清楚了。
3.6 在子线程中更新 UI
3.6.1 实例
public class HandlerTestActivity extends BaseActivity {
TextView mShow;
Button mButton4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentLayout(R.layout.a_activity_handler_test);
mShow = findViewById(R.id.show);
mButton4 = findViewById(R.id.button4);
mButton4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
beforeOnResumeClick();
}
});
// 写在OnResume之前执行点击事件的话,可以在子线程更新UI线程
mButton4.performClick();
}
/**
* 测试在onResume之前调用Thread更新ui
*/
private void beforeOnResumeClick() {
new Thread() {
@Override
public void run() {
super.run();
mShow.setText("测试在onResume之前调用Thread更新ui");
}
}.start();
}
}
3.6.2 分析
可以看到,我们如果想在非 UI 线程更新 UI,就必须在 onResume 之前完成 View 的绑定,更新 UI 的操作,这是为什么呢?
我们先从在子线程中更新 UI 的异常说起:

我们看下异常的堆栈信息,可以知道异常产生的路径:
- View 的 setText
- View 的 checkForRelayout
- View 的 invalidate
- View 的 invalidate
- ViewGroup 的 InvalidateChild
- ViewRootImpl 的 invalidateChildInParent
- ViewRootImpl 的 CheckThread 方法。
我们看下异常堆栈里面的流程:
首先,我们是在 mShow.setText("测试在onResume之前调用Thread更新ui"); 这里崩溃的,来看下 setText(CharSequence text) 的源码:
public final void setText(CharSequence text) {
setText(text, mBufferType);
}
public void setText(CharSequence text, BufferType type) {
setText(text, type, true, 0);
if (mCharWrapper != null) {
mCharWrapper.mChars = null;
}
}
private void setText(CharSequence text, BufferType type,
boolean notifyBefore, int oldlen) {
// 省略前面代码
if (mLayout != null) {
checkForRelayout();
}
// 省略后面代码
}
看到这里有个 checkForRelayout() 方法:
private void checkForRelayout() {
// If we have a fixed width, we can just swap in a new text layout
// if the text height stays the same or if the view height is fixed.
if ((mLayoutParams.width != LayoutParams.WRAP_CONTENT
|| (mMaxWidthMode == mMinWidthMode && mMaxWidth == mMinWidth))
&& (mHint == null || mHintLayout != null)
&& (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) {
// Static width, so try making a new text layout.
int oldht = mLayout.getHeight();
int want = mLayout.getWidth();
int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();
/*
* No need to bring the text into view, since the size is not
* changing (unless we do the requestLayout(), in which case it
* will happen at measure).
*/
makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(),
false);
if (mEllipsize != TextUtils.TruncateAt.MARQUEE) {
// In a fixed-height view, so use our new text layout.
if (mLayoutParams.height != LayoutParams.WRAP_CONTENT
&& mLayoutParams.height != LayoutParams.MATCH_PARENT) {
autoSizeText();
invalidate();
return;
}
// Dynamic height, but height has stayed the same,
// so use our new text layout.
if (mLayout.getHeight() == oldht
&& (mHintLayout == null || mHintLayout.getHeight() == oldht)) {
autoSizeText();
invalidate();
return;
}
}
// We lose: the height has changed and we have a dynamic height.
// Request a new view layout using our new text layout.
requestLayout();
invalidate();
} else {
// Dynamic width, so we have no choice but to request a new
// view layout with a new text layout.
nullLayouts();
requestLayout();
invalidate();
}
}
可以看到,checkForRelayout 这个方法不管怎样都会调用invalidate();方法。
看下 View 的 invalidate() 方法:
public void invalidate() {
invalidate(true);
}
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
// 省略部分代码
// Propagate the damage rectangle to the parent view.
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
p.invalidateChild(this, damage);
}
// 省略部分代码
}
}
我们看到了 p.invalidateChild(this, damage); 这行代码,是使用了 ViewParent 对象的 invalidateChild 方法刷新布局,由于这里的 p 是 ViewParent 对象,然而 ViewParent 是个接口,而 ViewGroup 又实现了 ViewParent 接口(并且异常堆栈里面已经明确显示了是使用 ViewGroup 里面的 invalidateChild 方法),现在我们在 ViewGroup 中找下 invalidateChild() 方法:
@Deprecated
@Override
public final void invalidateChild(View child, final Rect dirty) {
// 省略部分代码
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
}
if (drawAnimation) {
if (view != null) {
view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
} else if (parent instanceof ViewRootImpl) {
((ViewRootImpl) parent).mIsAnimating = true;
}
}
// If the parent is dirty opaque or not dirty, mark it dirty with the opaque
// flag coming from the child that initiated the invalidate
if (view != null) {
if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
view.getSolidColor() == 0) {
opaqueFlag = PFLAG_DIRTY;
}
if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;
}
}
parent = parent.invalidateChildInParent(location, dirty);
if (view != null) {
// Account for transform on current parent
Matrix m = view.getMatrix();
if (!m.isIdentity()) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
m.mapRect(boundingRect);
dirty.set((int) Math.floor(boundingRect.left),
(int) Math.floor(boundingRect.top),
(int) Math.ceil(boundingRect.right),
(int) Math.ceil(boundingRect.bottom));
}
}
} while (parent != null);
}
}
我们由找到了这两句关键代码:
((ViewRootImpl) parent).mIsAnimating = true;
parent = parent.invalidateChildInParent(location, dirty);
可以知道,parent.invalidateChildInParent(location, dirty) 这行代码调用的是 ViewRootImpl 的方法,
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
// 省略大量代码
}
而 checkThread() 代码又是如下:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
可以看到,正是这里抛出的异常。从上面的分析可知,是我们在子线程中 setText 的时候,抛出的这个异常。
看下 mThread 是什么:
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
final Thread mThread;
public ViewRootImpl(Context context, Display disp
mThread = Thread.currentThread();
}
}
可以看到 mThread 是在生成 ViewRootImpl 对象的时候赋值的。
上篇文章我们讲了在子线程中更新 UI 必须在 onResume 之前,就是因为在 onResume 之前 ViewRootImpl 还没生成,所以没法检查更新 UI 操作是不是在主线程,所以不会报错。
鉴于篇幅原因,这里不再过多介绍 ViewRootImpl,建议大家看下这篇文章,里面讲到了 ViewRootImpl 相关的内容,后面我也会写下我对 ViewRootImpl 的认识。
四、总结
到此,希望你能对 Android 中的消息机制有了进一步的认识。另外,看源码的时候一定不能着急,并且不要每个地方都看明白,找到重点看就行了,不然你会哭的。。。
其实关于 Handler 的文章很早就想写来总结下,但是一直没有写,一忙起来就懒得写东西了,以后还得坚持记录。

本文深入剖析了Android中Handler的工作原理及源码实现,包括Handler、Looper、MessageQueue等核心组件的作用机制,以及如何在子线程与主线程间进行消息传递。
889

被折叠的 条评论
为什么被折叠?



