Android异步消息机制Handler详解,源码剖析(API 23)

来源:转载

声明:Android不同API版本中同一个类的实现方法可能会有不同,本文是基于最新的API 23的源码进行讲解的。

Android的UI是线程不安全的,也就是说在子线程中操作主线程(UI线程)程序就会崩溃。想在子线程中更新UI就需要用到异步操作机制。

Android的异步操作主要有两种,AsyncTask和Handler。AsyncTask的详细讲解和源码分析在我的另一篇博客中进行了详细的讲解,本篇就来讲解一下Handler的用法与源码分析。

基本用法

在Activity中有一个Button和一个EditText,当点击Button之后,每1秒钟更新一下这个EditText中的文字。

关键代码

按钮的Click方法
<span style="font-size:18px;"> handlerBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) {Timer timer = new Timer();timer.scheduleAtFixedRate(new MyTask(), 1, 1000); }});</span>


<span style="font-size:18px;"> private class MyTask extends TimerTask {@Overridepublic void run() { Message message = new Message(); message.what = 1; mHandler.sendMessage(message);} }</span>


Handler处理方法
<span style="font-size:18px;"> private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) { super.handleMessage(msg); EditText editText = (EditText)findViewById(R.id.editText_handler); editText.setText("Handler Test " + i); i++;} };</span>


效果



源码分析

Handler机制流程图



1. Message

Message 消息,理解为线程间交流的信息,处理数据后台线程需要更新 UI ,则发送Message 内含一些数据给 UI 线程。

2. Handler

Handler处理者,是 Message 的主要处理者,负责 Message 的发送,Message 内容的执行处理。后台线程就是通过传进来的 Handler对象引用来 sendMessage(Message)。

而使用 Handler,需要 implement 该类的 handleMessage(Message)方法,它是处理这些

Message 的操作内容,例如 Update UI 。通常需要子类化 Handler 来实现 handleMessage方法。

3. Message Queue

Message Queue 消息队列,用来存放通过 Handler 发布的消息,按照先进先出执行。每个 message queue 都会有一个对应的 Handler。Handler 会向 messagequeue 通过两种方法发送消息:sendMessage 或 post。这两种消息都会插在 message queue 队尾并

按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过 sendMessage发送的是一个 message 对象,会被 Handler 的 handleMessage()函数处理;而通过 post 方法发送的是一个 runnable 对象,则会自己执行。

4. Looper

Looper 是每条线程里的 Message Queue 的管家。Android 没有 Global 的MessageQueue,而 Android 会自动替主线程(UI 线程)建立 Message Queue,但在子线程里并没有建立 Message Queue。 所以调用 Looper.getMainLooper()得到的主线程的 Looper 不为 NULL,但调用 Looper.myLooper()得到当前线程的 Looper 就有可能为 NULL。

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()); }}<strong>mLooper = Looper.myLooper();</strong>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.myLooper()这个方法之后对mLooper进行了判断,如果为空就报错"Can't create handler inside thread that has not called Looper.prepare()",接下来就看看这个方法里到底有什么。

/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static @Nullable Looper myLooper() {return sThreadLocal.get(); }


原来就是返回一个当前线程的Looper啊,没有的话就返回null。这也就解释了为什么在子线程中必须先调用Looper.prepare()了。在主线程main()方法中已经调用了Looper.prepare(),所以在主线程中可以直接new一个Handler而不用事先调用Looper.prepare()。
说了那么多的Looper.prepare(),下面来看一下到底什么是Looper.prepare()。
/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ 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)); }
先判断一下sThreadLocal中有没有Looper,没有就创建一个。同时也可以发现一个线程中只能有一个Looper。

Message

我们使用handler的时候需要先sendMessage()再handleMessage(),现在就来分析一下这个Message到底是怎么发送出去又是怎么接到的。

<span style="font-size:18px;"> public final boolean sendMessage(Message msg) {return sendMessageDelayed(msg, 0); }</span>


<span style="font-size:18px;"> public final boolean sendMessageDelayed(Message msg, long delayMillis) {if (delayMillis < 0) { delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }</span>


<span style="font-size:18px;">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); }</span>

可以看到跳来跳去最后跳到了sendMessageAtTime(),在这个方法中对MessageQueue进行了判断并在最后调用了enqueueMessage()。

<span style="font-size:18px;">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.nextprev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) {nativeWake(mPtr); }}return true; }</span>


用了一个mMessages对象表示当前待处理的消息。消息入队就把所有的消息按照时间的顺序排列,根据时间的顺序调用msg.next来指定它的下一个消息。如果你用sendMessageAtFrontOfQueue()发送消息,系统也会调用enqueueMessage(),只不过时间为0,并且把mMessages赋值为新入队的这条消息,然后将这条消息的next指定为刚才的mMessages,完成了添加消息到队列头部的操作。

消息出队
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 Printer logging = me.mLogging; if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); 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();} }
这里有一个死循环,queue.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 iterationint 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;}// If first time idle, then get the number of idlers to run.// Idle handles only run if the queue is empty or if the first message// in the queue (possibly a barrier) is due to be handled in the future.if (pendingIdleHandlerCount < 0&& (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size();}if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue;}if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];}mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) {final IdleHandler idler = mPendingIdleHandlers[i];mPendingIdleHandlers[i] = null; // release the reference to the handlerboolean keep = false;try { keep = idler.queueIdle();} catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t);}if (!keep) { synchronized (this) {mIdleHandlers.remove(idler); }} } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0;} }


如果当前MessageQueue中存在mMessages(即待处理消息),就将这个消息出队,每当有一个message出队就调用msg.target.dispatchMessage(msg) (msg.target是一个Handler)然后让下一条消息成为mMessages,否则就进入一个阻塞状态,一直等到有新的消息入队。

Handler.dispatchMessage()
public void dispatchMessage(Message msg) {if (msg.callback != null) { handleCallback(msg);} else { if (mCallback != null) {if (mCallback.handleMessage(msg)) { return;} } handleMessage(msg);} }
如果mCallback不为空,则调用mCallback的handleMessage()方法,否则直接调用Handler的handleMessage()方法,并将消息对象作为参数传递过去。

到此为止整个Handler的消息传递过程就已经全部讲完了,相信大家一定对Handler有了更深入的理解。

一个标准的Handler

class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }


分享给朋友:
您可能感兴趣的文章:
随机阅读: