深入解析Android中Handler消息机制

青春须早为,岂能长少年。这篇文章主要讲述深入解析Android中Handler消息机制相关的知识,希望能为你提供帮助。
android提供了Handler 和 Looper 来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。Handler消息机制可以说是Android系统中最重要部分之一, 所以, 本篇博客我们就来深入解析Android中Handler消息机制。
Handler的简单使用 为什么系统不允许子线程更新UI
因为的UI控件不是线程安全的。
如果在多线程中并发访问可能会导致UI控件处于不可预期的状态, 那为什么不对UI控件的访问加上上锁机制呢? 因为有这么两个缺点:
1.上锁会让UI控件变得复杂和低效
2.上锁后会阻塞某些进程的执行
对于手机系统来说, 这两个缺点是不可接受的, 所以最简单高效的方法就是 —— 采用单线程模型来处理UI操作。
对开发者而言也不是很麻烦, 只是通过Handler切换一下访问的线程的就好。
Handler的简单使用
既然子线程不能更改界面, 那么我们现在就借助Handler让我们更改一下界面:
主要步骤是这样子的:
1.new出来一个Handler对象, 复写handleMessage方法
2.在需要执行更新UI的地方 sendEmptyMessage 或者 sendMessage
3.在handleMessage里面的switch里面case不同的常量执行相关操作

public class MainActivity extends ActionBarActivity {private TextView mTextView; private Handler mHandler; private static final int UI_UPDATE1 = 0; private static final int UI_UPDATE2 = 1; @ Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mHandler = new Handler() {@ Override public void handleMessage(Message msg) { switch (msg.what) { case UI_UPDATE1: mTextView.setText(" 通过Handler方法1修改UI" ); break; case UI_UPDATE2: mTextView.setText(" 通过Handler方法2修改UI" ); break; } }}; mTextView = (TextView) findViewById(R.id.textview); mTextView.setOnClickListener(new OnClickListener() {@ Override public void onClick(View v) { Log.d(" Test" , " 点击文字" ); updateUi(); } }); }protected void updateUi() { new Thread(new Runnable() {@ Override public void run() { // 方式一和方式二可以达到相同的效果,就是更改界面 //方式一 //mHandler.sendEmptyMessage(UI_UPDATE1); //方式二 Message msg = Message.obtain(); msg.what = UI_UPDATE2; mHandler.sendMessage(msg); } }).start(); } }

< RelativeLayout xmlns:android= " http://schemas.android.com/apk/res/android" xmlns:tools= " http://schemas.android.com/tools" android:layout_width= " match_parent" android:layout_height= " match_parent" tools:context= " com.example.handlerdemo3.MainActivity" > < TextView android:id= " @ + id/textview" android:layout_width= " wrap_content" android:layout_height= " wrap_content" android:text= " @ string/hello_world" android:layout_centerInParent= " true" android:clickable= " true" /> < /RelativeLayout>

消息机制的分析理解 安卓的异步消息处理机制就是handler机制。
主线程, ActivityThread被创建的时候就会创建Looper
Looper被创建的时候创建MessageQueue。
也就是说主线程会直接或间接创建出来Looper和MessageQueue。
Handler的工作机制简单来说是这样的
1.Handler发送消息仅仅是调用MessageQueue的enqueueMessage向插入一条信息到MessageQueue
2.Looper不断轮询调用MeaasgaQueue的next方法
3.如果发现message就调用handler的dispatchMessage, dispatchMessage被成功调用, 接着调用handlerMessage()
深入解析Android中Handler消息机制

文章图片

Handler消息机制的源码分析 ThreadLocal工作原理
首先, ThreadLocal 不是用来解决共享对象的多线程访问问题的, 一般情况下, 通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象, 其他线程是不需要访问的, 也访问不到的。各个线程中访问的是不同的对象。
另外, 说ThreadLocal使得各线程能够保持各自独立的一个对象, 并不是通过ThreadLocal.set()来实现的, 而是通过每个线程中的new 对象 的操作来创建的对象, 每个线程创建一个, 不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中, 每个线程都有这样一个map, 执行ThreadLocal.get()时, 各线程从自己的map中取出放进去的对象, 因此取出来的是各自自己线程中的对象, ThreadLocal实例是作为map的key来使用的。
如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象, 那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身, 还是有并发访问问题。
我们看一下ThreadLocal的set方法
public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values = = null) { values = initializeValues(currentThread); } values.put(this, value); }

查看values方法里面做了什么
Values values(Thread current) { return current.localValues; }

通过上面代码我们可以知道, 当values为空的时候, 才调用initializeValues方法进行初始化, 查看inheritValues相关逻辑:
private void inheritValues(Values fromParent) { // Transfer values from parent to child thread. Object[] table = this.table; for (int i = table.length - 2; i > = 0; i -= 2) { Object k = table[i]; if (k = = null || k = = TOMBSTONE) { // Skip this entry. continue; }// The table can only contain null, tombstones and references. Reference< InheritableThreadLocal< ?> > reference = (Reference< InheritableThreadLocal< ?> > ) k; // Raw type enables us to pass in an Object below. InheritableThreadLocal key = reference.get(); if (key != null) { // Replace value with filtered value. // We should just let exceptions bubble out and tank // the thread creation table[i + 1] = key.childValue(fromParent.table[i + 1]); } else { // The key was reclaimed. table[i] = TOMBSTONE; table[i + 1] = null; fromParent.table[i] = TOMBSTONE; fromParent.table[i + 1] = null; tombstones+ + ; fromParent.tombstones+ + ; size--; fromParent.size--; } } }

其实就是各种赋值table数组, 进行初始化
最后才是调用values.put(this, value)把ThreadLocal和value一起保存, 我们可以看一下values.put(this, value)方法
void put(ThreadLocal< ?> key, Object value) { cleanUp(); // Keep track of first tombstone. That' s where we want to go back // and add an entry if necessary. int firstTombstone = -1; for (int index = key.hash & mask; ; index = next(index)) { Object k = table[index]; if (k = = key.reference) { // Replace existing entry. table[index + 1] = value; return; }if (k = = null) { if (firstTombstone = = -1) { // Fill in null slot. table[index] = key.reference; table[index + 1] = value; size+ + ; return; }// Go back and replace first tombstone. table[firstTombstone] = key.reference; table[firstTombstone + 1] = value; tombstones--; size+ + ; return; }// Remember first tombstone. if (firstTombstone = = -1 & & k = = TOMBSTONE) { firstTombstone = index; } } }

可以看出, Threadlocal的值在table数组的存储位置总是reference的下一个位置.
接下来, 查看Threadlocal的get方法
深入解析Android中Handler消息机制

文章图片

Get方法的逻辑是: 通过values方法取出当前线程的localValues对象, 如果为null, 就返回初始值。如果localValues不为null, 取出其table数组, 如果reference等于table数组index角标的值, 就在table[index + 1]取出其Threadlocal值。
MessageQueue工作原理
MessageQueue中文翻译就是消息队列, 它内部存储了一组信息, 存放的是Message, 以队列的形式对外提供了插入和删除的工作( 虽然名字叫做队列, 但是其内部的 存储结构是单链表)
主要 插入 和 读取 两个操作, 这两个操作对应着两个方法:
插入( 入队) enqueueMessage(Message msg, long when) 读取( 出队) next()

查看enqueueMessage的源码:
boolean enqueueMessage(Message msg, long when) { if (msg.isInUse()) { throw new AndroidRuntimeException(msg + " This message is already in use." ); } if (msg.target = = null) { throw new AndroidRuntimeException(" Message must have a target." ); }boolean needWake; synchronized (this) { if (mQuiting) { RuntimeException e = new RuntimeException( msg.target + " sending message to a Handler on a dead thread" ); Log.w(" MessageQueue" , e.getMessage(), e); return false; }msg.when = when; Message p = mMessages; 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; } } if (needWake) { nativeWake(mPtr); } return true;

从enqueueMessage的实现来看, 主要操作就是单链表的插入操作, 接下来查看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; }// 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; } }

从上面代码可以得出以下结论:
1.next方法是一个无限循环的方法, 如果消息队列中没有消息, 那么next方法会一直阻塞
2.当有新消息到来, next方法会返回这条消息, 并将其从单链表中移除。
Looper的工作原理
Looper是一个轮询器, 它的作用不断轮询MessageQueue, 当如果有新的消息就交给Handler处理, 如果轮询不到新的消息, 那就自身就处于阻塞状态。
查看Looper类的源码, 可以发现的他的构造方法里面创建了一个MessageQueue, 然后将当前线程的对象保存起来
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }

在Looper的构造方法中初始化了一个消息队列MessageQueue和一个线程Thread。从这可看出: 一个Looper对应着一个消息队列以及当前线程。
当收到消息Message后系统会将其存入消息队列中等候处理。至于Looper, 它在Android的消息机制中担负着消息轮询的职责, 它会不间断地查看MessageQueue中是否有新的未处理的消息; 若有则立刻处理, 若无则进入阻塞。
相信大家一定有遇到过, 在子线程中创建Handler会报如下错误
深入解析Android中Handler消息机制

文章图片

解决办法就是new Handler的时候加上Looper.prepare();
而Looper.prepare()的内部实现逻辑就是创建一个Looper
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)); }

线程默认是没有Looper的, 但是为什么在主线程没有创建的Looper就可以使用Handler? 主线程是特别的。主线程, 也就是ActivityThread, 当主线程被创建的时候, 会调用Looper内的prepareMainLooper方法, 创建Looper, 该方法是专门给主线程创建Looper用的。也正因为这点, 所以我们在主线程创建了Handler就直接能用了。
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还提供了一个getMainLooper方法, 使得可以在任何地方获取主线程的Looper。
public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } }

接下来, 我们看一下Looper的退出
Looper提供了两个方法进行退出操作, 分别是quit和quitSafely,他们调用的是MessageQueue的quit方法
public void quit() { mQueue.quit(false); }

public void quitSafely() { mQueue.quit(true); }

MessageQueue的quit方法:
void quit(boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException(" Main thread not allowed to quit." ); }synchronized (this) { if (mQuitting) { return; } mQuitting = true; if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); }// We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); } }

如果调用Looper.quit方法, 最终会调用removeAllMessagesLocked方法, 该方法逻辑: 直接遍历所有的消息, 并将消息强制回收
private void removeAllMessagesLocked() { Message p = mMessages; while (p != null) { Message n = p.next; p.recycleUnchecked(); p = n; } mMessages = null; }

如果调用Looper.quitSafely方法, 最终会调removeAllFutureMessagesLocked方法, 该方法逻辑:
void removeCallbacksAndMessages(Handler h, Object object) { if (h = = null) { return; }synchronized (this) { Message p = mMessages; // Remove all messages at front. while (p != null & & p.target = = h & & (object = = null || p.obj = = object)) { Message n = p.next; mMessages = n; p.recycleUnchecked(); p = n; }// Remove all messages after front. while (p != null) { Message n = p.next; if (n != null) { if (n.target = = h & & (object = = null || n.obj = = object)) { Message nn = n.next; n.recycleUnchecked(); p.next = nn; continue; } } p = n; } } }

【深入解析Android中Handler消息机制】一个无限for循环, 只有n.when > now和n = = null才会跳出循环, 说明是等消息队列中已有的消息处理完毕后, 才会跳出, 然后执行回收。
Looper这个类里面最重要的方法就是loop()开启消息循环这个方法了, 看一下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.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(); } }

通过代码我们知道: looper方法是一个死循环, 唯一跳出的循环的方式是MessageQueue的next方法返回null, 但这几乎不可能, 因为在MessageQueue的next方法中, 假如没有消息加入队列, next方法会一直阻塞, 不会返回null。如果我们不手动调用quit或者quitSafely方法的话, MessageQueue的next方法是不可能返回null的。
当MessageQueue没有消息时, next方法会一直阻塞在那里, 因为MessageQueue的next方法阻塞了, 就导致Looper的loop方法也一直在阻塞了。
这里我们那一分为二的谈,
loop轮询不到消息: 那么处于阻塞状态, 然后就没有然后了, 除了又轮询到了新的消息
loop轮到了新的消息: Looper就会处理消息
1、msg.target.dispatchMessage(msg), 这里的 msg.target就是指Handler对象
2、到了最后, Handler发送的消息又交给了自己的dispatchMessage方法来处理了。( 这个dispatchMessage方法不是Handler自己调用时, 是与Handler相相关的Looper间接调用的) , 这样下来, 就成功地将逻辑切换到指定的线程当中去了
Handler的工作原理
Handler的主要工作: 消息的 发送 和 接收 。Handler消息发送的形式有post和send两种。
查看Handler中sendMessage代码
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }

sendMessage调用sendMessageDelayed
public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }

sendMessageDelayed调用sendMessageAtTime
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); }

sendMessageAtTime调用enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }

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; }

得出结论: Handler的发送消息仅仅是调用MessageQueue的enqueueMessage向插入一条信息到MessageQueue, MessageQueue就会返回这条消息给Looper, Looper会交给msg.target.dispatchMessage(msg)方法处理, 就进入消息处理阶段。
查看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); } }

首先, 判断 Message. Callback是否为null,不为null, 就调用handleCallback方法
private static void handleCallback(Message message) { message.callback.run(); }

Callback是一个Runnable对象, 实际上就是post方法传递的Runnable参数。
其次, 检查mCallback是否为null, 如果不为null就调用mCallback.handleMessage方法, 查看mCallback.handleMessage方法
public interface Callback { public boolean handleMessage(Message msg); }

源码中注释已经对Callback进行了解释:
可以用来创建一个Handler的实例但不需要派生Handler的子类
在日常开发中, 创建Handler最常见的方式就是派生一个Handler的子类并重写handleMessage方法来处理具体的消息, 而Callback给我们提供了另外一种方式, 不需要派生Handler的子类。
最后, 调用Handler的handleMessage方法, 这就是我们平时写Handler要实现的方法
深入解析Android中Handler消息机制

文章图片

主线程的消息循环
Android的主线程就是ActivityThread, 主线程的路口方法是main方法, 在 main中系统会通过Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue, 并通过Looper.loop()开启主线程消息循环
public static void main(String[] args) { SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy.We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); Security.addProvider(new AndroidKeyStoreProvider()); Process.setArgV0(" < pre-initialized> " ); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler = = null) { sMainThreadHandler = thread.getHandler(); }AsyncTask.init(); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, " ActivityThread" )); }Looper.loop(); throw new RuntimeException(" Main thread loop unexpectedly exited" ); }

主线程开始循环后, ActivityThread还需要一个Handler来和消息队列进行交互, 这个Handler就是ActivityThread.H
深入解析Android中Handler消息机制

文章图片

ApplicationThread通过binder与Ams通信,并将Ams的调用,通过H类(也就是Hnalder)将消息发送到消息队列,然后进行相应的操作, H收到消息后, 就会将ApplicationThread中逻辑切换到ActivityThread中执行, 也就是主线程中执行, 这个过程就是主线程的消息循环。
至此, Handler消息机制就分析完毕, 如有错漏, 欢迎留言指证。

    推荐阅读