系列文章:
前言
Android和Windows一样都采用了消息机制,从开发角度说,Handler
是Android消息机制的上层接口,开发过程中和Handler
交互即可。Android规范限制我们不能在子线程中更新UI,只能在主线程中更新UI,Handler可以轻松的将一个任务切换到Handler所在的线程中去执行。所以,开发过程中Handler通常被用来更新UI,利用Handler将更新UI的操作切换到主线程中执行。
ViewRootImpl
对我们的UI操作进行了验证,具体是在checkThread()
方法中完成的,如下:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
ViewRootImpl
是所有View
的根,Window
和View
通过ViewRootImpl
建立联系,checkThread()
方法校验当前线程是否是ViewRootImpl
被创建时所在的线程。具体请参考Android中子线程真的不能更新UI吗?
说明:本文中的源码都是基于Android-25版本。
Handler
Android的消息机制主要是指Handler的运行机制,Handler的运行需要MessageQueue
和Looper
支撑,MessageQueue是消息队列,内部存储了一组消息,而Looper则是消息循环,Looper会无限循环查看是否有新消息,如果有的话处理消息,没有的话就一直等待。
Looper中有一个特殊概念ThreadLocal
,ThreadLocal
并不是线程,作用是在每个线程中存储数据,可以让不同线程保存在同一个ThreadLocal
中的对象数据互不干扰。ThreadLocal
的工作原理请参考:Android的消息机制之ThreadLocal的工作原理
工作过程
Handler在创建过程时会采用当前线程的Looper来构造消息循环系统,那么Handler内部是如何获取到Looper呢?通过ThreadLocal
可以很轻松的获取每个线程的Looper,但是线程默认是没有Looper的,要使用Handler就必须为线程创建Looper。而主线程(UI)线程即ActivityThread
被创建时就初始化了Looper,所以我们在主线程中可以默认使用Looper。
Handler的默认构造方法最终会通过以下构造方法实现:
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 that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从中可以看到,如果当前线程没有Looper,则会抛出异常,而mLooper
是由Looper.myLooper()
方法返回的,代码如下:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
Looper.myLooper()
就是返回了保存在ThreadLocal
中的Looper对象。
Handler创建完毕后,Handler和Looper以及MessageQueue便协同工作,构成一个消息系统。通过Handler的post
系列方法将一个Runnable
投递到Looper,或者send
系列方法发送一个消息到Looper中,通过调用MessageQueue的enqueueMessage()
方法将消息放入消息队列中,此时Looper发现有新消息到来,处理此消息。最终消息中的Runnable
或者Handler的handleMessage()
方法就会被调用,这样就又将任务切换到创建Handler的线程中去了。过程如图所示:
工作原理
Handler的工作主要包括发送和接收消息。
发送消息
上面提到,发送消息是通过一系列post或者send方法实现,post系列的方法最终都是通过send系列方法实现的。Handler中有关发送消息的send
系列方法源码如下:
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
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 sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
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);
}
public final boolean sendMessageAtFrontOfQueue(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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到除了sendMessageAtFrontOfQueue()
方法,其余最终都是调用sendMessageAtTime()
方法,在sendMessageAtTime()
方法中调用enqueueMessage()
方法,再调用MessageQueue
的enqueueMessage()
方法,发送消息就是向消息队列中插入一条消息,MessageQueue的next()
方法将这条消息返回给Looper。
处理消息
Looper接收到消息后处理,并最终交由Handler的dispatchMessage()
处理,源码如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
首先检查Message
的callback
,不为null则调用handleCallback()
,源码如下:
private static void handleCallback(Message message) {
message.callback.run();
}
Message
的callback
是一个Runnable
对象,就是Handler中post方法的Runnable
参数。post方法源码如下:
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
post
方法利用sendMessageDelayed()
方法发送消息,其中又调用了getPostMessage()
方法,其源码如下:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
该方法传入一个Runnable
参数,得到一个Message
对象,将Runnable
参数赋值给Message对象的callback
字段。
然后检查mCallback
,不为null则调用mCallback
的handleMessage()
,Callback
是一个接口,定义如下:
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
使用Callback
创建Handler不需要派生一个Handler子类,通常开发过程中都是派生Handler子类并重写其handleMessage()
方法来处理具体消息,Callback
提供了另一种使用Handler方法。
最后不符合以上两点时,就调用Handler的handleMessage()
来处理消息。
Looper
Looper在Android消息机制中扮演消息循环的角色,它会不断从MessageQueue中查看是否有新消息,如果有新消息就立即处理,否则一直阻塞在那里。
创建Looper
首先看一下构造方法,在构造方法中它会创建一个MessageQueue消息队列,然后将当前线程的对象保存起来。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Handler的工作需要Looper,没有Looper的线程就会报错,那么如何为线程创建Looper呢?通过Looper.prepare()
即可为当前线程创建一个Looper,同时会将Looper保存在ThreadLocal
中,Handler的构造函数便是从ThreadLocal
中获取Looper
对象,从代码如下:
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));
}
接着通过Looper.loop()
来开启消息循环。为一个子线程创建Looper和Handler的代码如下所示:
new Thread("Thread#2"){
@Override
public void run(){
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
};
}.start();
Looper除了prepare()
方法外,还提供了prepareMainLooper()
方法,这个方法主要是给主线程即ActivityThread
创建Looper使用的,本质也是通过prepare()
方法来实现的,源码如下:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
在程序启动的时候,系统已经帮我们自动调用了Looper.prepare()
方法。ActivityThread
中的main()
方法调用了Looper.prepareMainLooper()
方法,而这个方法又会再去调用Looper.prepare()
方法。因此我们应用程序的主线程中会始终存在一个Looper对象,从而不需要再手动去调用Looper.prepare()
方法了。
由于主线程Looper比较特殊,所以Looper提供了一个getMainLooper()
方法,通过它可以在任何地方获取主线程的Looper。
Looper退出
Looper也提供了退出方法,quit()
和quitSafely()
,区别在于quit()
是直接退出Looper,而quitSafely()
只是设定一个退出标记,然后把消息队列中的已有消息全部处理完毕后再退出。quit()
方法最终调用的是MessageQueue
中的removeAllMessagesLocked()
方法,quitSafely()
最终调用的是MessageQueue
中的removeAllFutureMessagesLocked()
方法,源码如下:
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
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;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
Looper退出后,通过Handler发送的消息会失败,Handler的send方法会返回false,在子线程中如果为其手动创建了Looper,消息处理完毕后应该调用quit()
方法来终止消息循环,否则这个子线程会一直处于等待状态,退出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 traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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();
}
}
loop()
方法是一个死循环,唯一跳出循环的方法是MessageQueue
的next()
方法返回了null。当Looper的退出方法被调用时,通知消息队列消息退出,当消息队列被标记为退出状态时,它的next()
方法就会返回null。也就是说Looper必须退出,否则loop()
方法会无限循环下去。
loop()
方法会调用MessageQueue
中的next()
方法来获取新消息,而next()
是一个阻塞操作,没有新消息时会一直阻塞在那里,同理导致loop()
阻塞,如果MessageQueue
的next()
方法返回了新消息,Looper便会处理这条消息,通过以下语句执行:
msg.target.dispatchMessage(msg);
这里的msg.target
是发送这条消息的Handler对象,这样通过Handler的dispatchMessage()
方法来处理消息,dispatchMessage()
是在创建Handler时所使用的Looper中执行的,这样就将代码逻辑切换到指定的线程中了。
Message & MessageQueue
Message
在线程之间传递消息,Message
有what
字段,是消息类型字段,arg1
和arg2
携带一些整型数据,obj
字段携带一个object对象。
MessageQueue
是消息队列,存放所有通过Handler发送的消息。消息一直存放在消息队列中,等待被处理。每个线程只会有一个MessageQueue
对象。
MessageQueue
主要包含两个操作,插入和读取,读取伴随着删除操作。euqueueMessage()
的作用是往消息队列中插入一条消息,next()
的作用是从消息队列中读取一条消息并移除。尽管MessageQueue
叫做消息队列,但是其内部是通过单链表的数据结构来维护消息列表。euqueueMessage()
的源码如下:
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) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} 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.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
从源码中可以分析出,当前消息队列的头结点为空或待插入的消息需要被立即执行时,就让当前消息成为消息队列的新的头结点,并且如果消息队列处于阻塞状态,则将消息队列唤醒;否则则按消息等待被执行的时间顺序,将待插入消息插入消息队列中,最后如果需要唤醒消息队列,则通过native
方法nativeWake()
来唤醒消息队列。
下面再看看next()
方法的源码:
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
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) {
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;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
...
}
...
}
}
可以发现next()
方法是一个无限循环方法,如果消息队列中没有消息,其会一直阻塞在这里,有新消息到来时,next()
方法会返回这条消息并将其从单链表中移除。
Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
问题描述
Android程序的入口点可以认为是ActivityThread
类的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();
...
}
可以看到Looper开启了消息循环,loop()
方法是一个死循环,但是并没有看见有相关代码为这个死循环准备了一个新线程去运转,但是主线程却并不会因为Looper.loop()
中的这个死循环卡死,这是为什么呢?
原因
从上述代码我们可以发现,首先调用prepareMainLooper()
方法为主线程创建一个消息队列;其次,生成一个ActivityThread
对象,在其初始化代码中会创建一个H(Handler)
对象,即ActivityThread.H
,它内部定义了一组消息类型,主要包含了四大组件的启动和停止过程,如下所示:
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int PAUSE_ACTIVITY_FINISHING= 102;
public static final int STOP_ACTIVITY_SHOW = 103;
public static final int STOP_ACTIVITY_HIDE = 104;
public static final int SHOW_WINDOW = 105;
public static final int HIDE_WINDOW = 106;
public static final int RESUME_ACTIVITY = 107;
public static final int SEND_RESULT = 108;
public static final int DESTROY_ACTIVITY = 109;
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
public static final int NEW_INTENT = 112;
public static final int RECEIVER = 113;
public static final int CREATE_SERVICE = 114;
public static final int SERVICE_ARGS = 115;
public static final int STOP_SERVICE = 116;
...
}
thread.attach(false)
生成了一个AppplicationThread(Binder)
对象,ActivityThread
通过ApplicationThread
和AMS
进行进程间通信,AMS
以进程间通信方式完成ActivityThread
的请求后会回调ApplicationThread
中的Binder
方法,Binder
负责接远程ActivityManagerService(AMS)
的IPC调用,用于接收系统服务AMS发来的消息,收到消息后,通过Handler将消息发送到消息队列,UI主线程会异步的从消息队列中取出消息并执行操作;最后,UI主线程调用Looper.loop()
进入消息循环。
Android的Handler消息机制涉及到Linux的pipe/epoll
机制,MessageQueue没有消息时,阻塞在那里,主线程会释放CPU进入休眠状态,通过Linux系统的epoll
机制中的epoll_wait
函数进行等待,当有新消息来临时,往pipe(管道)
写入端写入消息来唤醒主线程,其实就是一个生产消费模型。
(还有一个疑问,那就是怎么响应点击事件呢?或者说对通常的GUI模型,如windows都是怎么实现的呢?生产消费模型?)
以上部分摘自柯元旦<Android内核剖析>
Handler的其它用法
除了通过编写子线程并结合Handler发送消息改变UI外,Handler还有一些其他用法。
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;
}
发现View的post()
方法就是调用了Handler中的post()
方法,前文已经说过Handler的post()
方法了,不再多解释。
Activity中的runOnUiThread()方法
代码如下所示:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
先判断当前线程是否是UI线程,如果不是则调用Handler的post()
方法,否则就直接调用Runnable
对象的run()
方法。
参考信息
- 任玉刚.《Android开发艺术探索》
- Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
网友评论