Android消息机制Handler源码分析
系列文章:
- Android消息机制Handler源码分析
- Android Handler消息机制中的ThreadLocal源码分析
前言
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都是怎么实现的呢?生产消费模型?)
以上部分摘自柯元旦
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()里的死循环卡死?
更多相关文章
- Android(安卓)AsyncTask
- Android进程与线程基本知识
- Android有两种方法检测USB设备插入
- Android消息提示框Toast
- android的测试工具CTS
- android的测试工具CTS
- Android(安卓)studio :Please configure Android(安卓)SDK
- Android(安卓)WebView系列(二)Android和JS互调,BridgeWebView的使用
- Android进程与线程基本知识