Android-消息机制(一):过程及源码分析

xiaoxiao2021-02-28  130

概述

Android消息机制,经常被称为Handler机制或异步消息机制,顾名思义,就是利用Handler进行线程间交互的消息机制。从开发的角度将,使用场景主要是将一个任务切换到Handler所在的线程中执行,或者将一个消息传达到Handler所在的线程,如主线程UI更新、网络数据获取等就是任务的线程切换,又如Activity生命周期切换、Service创建和绑定等就是线程的信息交互。

上层接口使用

基本用法

Handler的用法大家一定都比较熟悉,下面给出Handler的基本用法的UML顺序图和示例代码。 public class HandlerTestActivity extends AppCompatActivity { static final int SOMETHING_HAPPEND = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler_test); // TODO 1.假设这是需要处理消息的线程Thread1 Thread thread1 = new Thread(new Runnable() { @Override public void run() { //TODO 2.在Thread1中准备一个Looper对象 Looper.prepare(); //TODO 3.在Thread1中创建Handler对象,重写handleMessage方法 final Handler thread1Handler = new Handler() { @Override public void handleMessage(Message msg) { //TODO 4.假设该方法能正确处理来自Thread2的消息 if (msg.what == SOMETHING_HAPPEND) { Log.e("Msg:", "Something happend in thread2,and be sent to thread1!"); } //TODO 8.消息处理完成,使Looper对象跳出循环 getLooper().quit(); Log.e("Msg:", "Something handled success! looper quit!"); } }; //TODO 5.在Thread1中开启新线程Thread2 Thread thread2 = new Thread(new Runnable() { @Override public void run() { //TODO 6.在Thread2中调用Thread1中的Handler对象的发送消息方法,并相信这个消息一定会到达 Message msg = new Message(); msg.what = HandlerTestActivity.SOMETHING_HAPPEND; thread1Handler.sendMessage(msg); } }); thread2.start(); //TODO 7.在Thread1中开启Looper的循环处理,等待消息处理完成 Looper.loop(); } }); thread1.start(); } }

运行成功: - - 第三条日志上,根据stackoverflow和Google官方帖子的说法,是Android6.0的一个Bug,在6.0.1版本已修复,而我确实是用的Android6.0 API23的虚拟机。

过程总结

从上面的示例代码和UML顺序图可以总结Handler消息机制的基本过程。 Thread1中 1.Looper.prepare(),调用 Looper的静态方法,为当前线程初始化一个Looper对象,否则调用Handler构造方法会抛出异常。2.Handler Handler=new Handler() ,调用Handler的构造方法,可想而知,它会寻找本线程的Looper对象,Looper对象为null则抛出异常。3.@Override handleMessage(),重写handleMessage方法,种下一颗种子,静候它被调用。4.调用Looper对象的quit方法(),在消息处理完成后,关闭Looper消息循环。Thread2中 5.构造Message对象或者Runnable对象,填充内容。6.调用Handler对象sendMessage()或者post()方法,传递Message对象或者Runnable对象。Thread1中 7.Looper.loop(),开启Looper对象的消息循环。静候消息处理 消息被投递到Handler对象所在的线程Thread1的MessageQueue中去。MessageQueue将该消息压入单链表中,并逐一处理。MessageQueue将该消息取出,发现消息的target是Handler对象。调用该Handler对象的handleMessage方法。该方法内部表示处理完成该信息后,立即停止Looper对象的消息循环。以上就是Handler消息机制的基本使用和流程,下面我们由浅入深,进一步分析。

消息机制构建过程详解

下面我们以问题为向导的方式,思考整个过程的构建思想。

Looper.prepare()

从上面的介绍我们知道,构造Handler对象之前,Looper对象必须已经存在,否则将会报错;每个线程只有一个Looper对象;MessageQueue没有被显式调用,但是是确实存在的。

1.为什么需要先准备Looper对象?不可以Handler对象构造完成后再构造Looper对象吗?

首先,按照我们生活的经历,正常人去做这种具备流程的事情时,都会有两种比较极端的做法:一种是在完成某件事情之前,把所有需要的材料先准备好,磨刀不误砍柴工嘛,再者有一种万事俱备只欠东风的淡定感;第二种就是,事情一件一件来,需要这个时做这个,需要那个时做那个,一切都是如此地合理,资源也不会浪费,免得做了无用功。这两种做法映射到计算机世界里,其实也就是著名的“懒人”(懒加载)和“主动”(主动加载)的区别。很显然,没有谁对谁错的说法,在不同的适用场景下,就会有不一样的效果。借助《人月神话》的原句就是“没有银弹”。所以我猜Android消息机制设计者可能就是生活中经常做第一种选择的人,或者说有他的考虑。那么我接下来就来马后炮一下,先贴上源码,看看他到底是怎么做的。 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)); } 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和prepareMainLooper都是调用私有的prepare方法,看到这个参数名称就可以理解了,quitAllowed,其实就是这允许自己走后门,不允许开发者显式调用带参数的prepare方法,防止开发者恶意将自己的Looper消息循环设置成不可关闭。但是为什么prepareMainLooper就可以走后门呢?这个我们会在主线程的消息循环机制中讲到,其实恰恰相反就是要保证主线程的Looper消息循环永远不可关闭,从而保证系统主线程能不受恶意干扰地完成功能。接下来我们只需要看一下私有的prepare方法是如何构造一个Looper对象,且保证每个线程只有一个Looper对象的。显然,私有的prepare一开始就检查sThreadLocal.get方法是否不为null,否则就去sThreadLocal.set方法,又根据其异常信息和Looper的构造方法,我们可以知道,prepare方法的目的是检查当前线程是否已经有Looper和Looper对象的构造。这里就引入了一个问题。

2.如何保证每个线程只有一个Looper?为什么在不同的线程调用prepare方法就可以为每个线程准备一个Looper对象,且互不干扰?

这里我们就需要介绍一个重要的角色——ThreadLocal。

ThreadLocal

ThreadLocal在Android开发甚至在Android系统组件中的使用都是非常广泛的,主要的使用场景是某些以线程为作用域且在不同线程具有不同的数据副本。很显然,Android消息机制中的Looper就适用于此类使用场景。且prepare方法中的参数sThreadLocal也确实是ThreadLocal对象ThreadLocal主要的功能就是保存线程内的数据,保证不同线程的数据相互区分,而不会相互影响。Why ThreadLocal? 这里引用任玉刚大神的《Android开发艺术探索》,“通过ThreadLocal就可以轻松实现Looper在线程中的存取。如果不采用ThreadLocal,那么系统就必须提供一个全局的哈希表供Handler查找指定线程的Looper,这样一来就必须提供一个类似于LooperManager的类了,但是Android系统并没有这么做,而是选择了ThreadLocal。” // sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); ThreadLocal源代码主要的部分是set方法、get方法和内部类Values。 static class Values { ... /** * Map entries. Contains alternating keys (ThreadLocal) and values. * The length is always a power of 2. */ private Object[] table;//用于存储数据的数据结构 /** Used to turn hashes into indices. */ private int mask; /** Number of live entries. */ private int size; ... }` - ` public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread);//寻找当前线程的Values内部类 if (values == null) { values = initializeValues(currentThread); } values.put(this, value); } public T get() { // Optimized for the fast path. Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; } } else { values = initializeValues(currentThread); } return (T) values.getAfterMiss(this); } 从源码可以看到ThreadLocal使用泛型,且其内部类ThreadLocal.Values内部主要存储数据数据结构是Object[]数组。ThreadLocal的get和set方法及Values对存储的对象作了混淆,不过这不是我们关注的重点。重点是理解其工作原理。

下面我们继续看一下new Looper()构造方法里发生了什么,上源码。

private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }

然后是MessageQueue的构造方法源码。

private native static long nativeInit(); ... MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit(); } ... 从上面我们可以清楚地看到,消息机制的构建遵循的是“工欲善其事必先利其器”的原则,其实在Looper.prepare方法中,就已经完成了所有的对象构造工作。这一点已经非常地友好了(对比起要一个个构造对象来说,相当于打包了一个全家桶)。另外一点值得主要的是,MessageQueue作为重要的数据结构载体,其初始化方法中调用nativeInit本地方法,最大的可能就是出于对效率的考虑和对阻塞的需求(后面会讲到MessageQueue.next()必须是阻塞式的方法,才能保证Looper对象的消息循环的性能),这一点是比较深奥的,作为保留的问题,将在后面进行探索。

Handler对象构造

在Looper.prepare()方法之后,消息机制中的当前线程Looper对象和MessageQueue对象就已经构造完成了,下面就差“搬运工”——Handler。我们直接贴出Handler的一系列重载的构造方法。

public Handler() { this(null, false); } public Handler(Callback callback) { this(callback, false); } public Handler(Looper looper) { this(looper, null, false); } public Handler(Looper looper, Callback callback) { this(looper, callback, false); } /** * Use the {@link Looper} for the current thread * and set whether the handler should be asynchronous. * * Handlers are synchronous by default unless this constructor is used to make * one that is strictly asynchronous. *这里有说明mAsynchronous的作用 * Asynchronous messages represent interrupts or events that do not require global ordering * with respect to synchronous messages. Asynchronous messages are not subject to * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}. * * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. * * @hide */ public Handler(boolean async) { this(null, async); } 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) {//检查looper对象的初始化情况 throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } ... /** * 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); } ... final MessageQueue mQueue; final Looper mLooper; final Callback mCallback; final boolean mAsynchronous; ... 可以看到大部分构造方法又是调用参数为Callback和boolean的方法,这个方法是存在内存泄露可能性检查的,但其实只是给出警告日志。这些构造方法主要是对mLooper、mQueue、mCallback、mAsynchronous四个成员变量的初始化,前面两个就是消息机制大厦的另外两条大腿。mCallback是内部类Handler.Callback,源码的解释做了说明:可以使用Callback来创建一个Handler实例而避免派生出Handler的子类并重写handleMessage方法。这里需要注意,这里的Callback的handleMessage方法返回值是布尔值,并不是Void。从其他构造方法可以看到,开发者其实是可以既派生出Handler子类重写Handler方法,并传入Callback的。这里消息处理调用顺序会在后续MessageQueue的消息处理中详细讲。mAsynchronous,可以看出大部分构造函数都是将Handler对象的mAsynchronous默认设置成false,表示Handler默认是同步的,且源码解释中说明,该属性主要是影响MessageQueue对Messages消息的处理方式,所以也作为保留问题,在MessageQueue的解析中详细描述。

Message对象的构造就没什么好讲的了,下面进入Handler对象的工作——如何将Message“搬运”到MessageQueue中。

Handler-sendMessage()/post()

Handler对象构造完成后,只需要“种下果实”就可以静候丰收了,即重写handleMessage方法即可。下面我们来看一下利用Handler对象调用sendMessage或post方法时,其内部是如何运作的,贴源码。 - send系列方法。

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); } //最终调用了Handler的enqueueMessage方法 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this;//把Message对象的目标设置成Handler对象本身 if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } public final boolean postAtTime(Runnable r, long uptimeMillis) { return sendMessageAtTime(getPostMessage(r), uptimeMillis); } public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) { return sendMessageAtTime(getPostMessage(r, token), uptimeMillis); } public final boolean postDelayed(Runnable r, long delayMillis) { return sendMessageDelayed(getPostMessage(r), delayMillis); } public final boolean postAtFrontOfQueue(Runnable r) { return sendMessageAtFrontOfQueue(getPostMessage(r)); } //将Runnable对象封装在Message的Callback里 private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; } private static Message getPostMessage(Runnable r, Object token) { Message m = Message.obtain(); m.obj = token; m.callback = r; return m; } 所以post方法最终也是把Runnable对象封装成Message,然后调用send系列方法。sendMessage系列方法最终是调用Handler的enqueueMessage方法,该方法内部最关键的一句代码就是把Message对象的目标设置成Handler对象本身,最后调用MessageQueue的enqueueMessage方法。

接下来我们来看看,消息队列的压入操作。

MessageQueue-enqueueMessage()

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

从enqueueMessage的实现来看,它的主要操作时单链表的插入操作,最终还会调用到native方法,深入的话,有时间再看吧,这里不做重点。

接下来重头戏来了,Handler对象构造完成表示消息系统(组件——Looper、Handler、MessageQueue)已经搭建完成;Handler对象sendMessage方法被调用,表示消息已经被投递到MessageQueue中了。这就好比工厂的流水线,设备搭建完毕,东西已经方法流水线上了,就差启动传送带了。这个核心工作就属于Looper负责。

下面我们看看Looper是如何启动消息循环,并且配合MessageQueue完成消息的处理和分发的。

Looper.loop()

前面讲到基于ThreadLocal的思想,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 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(); } } loop方法看起来没有想象中那么复杂,最关键的一点就是loop方法中包含了一个死循环,而唯一的跳出方法是线程的MessageQueue.next方法返回null,可见,next方法是不会轻易返回null的,而且,源码注释里说明了next方法可能会阻塞。接着分析,如果next方法取出的Message不为空,则调用Message.target的dispatchMessage方法,Message.target其实就是之前已经绑定过Handler对象,dispatchMessage方法听名字就知道,这时候已经把Message的生死大权交给Handler,Handler如何“处置”Message就在这个方法里面。

所以我们接着分析MessageQueue.next方法和Handler.dispatchMessage方法

MessageQueue-next()

Message next() { 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;//唯一的返回msg } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null;//唯一的返回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 handler boolean 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; } } 这里的代码比较深奥,任玉刚大神的书里也只是一笔带过,大概看了一下老罗的源码分析,其中较大篇幅是讲这一段内容的,总结来说next()方法是一个无限循环的方法,没有消息时就阻塞在这里,但不是做忙等待,而是空闲等待,消息队列为空时,调用IdleHandler 进行其他任务。这里也留作扩展,在之后做详细分析,毕竟不是主要的大问题,我们先熟悉Java层的代码,c++和Linux相关的日后相见。

Handler-dispatchMessage()

public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg);//写成this.handleMessage(msg);可能语义会更明显吧 } } private static void handleCallback(Message message) { message.callback.run(); } 这段代码真是言简意赅,中间的过程充分地体现分发(dispatch)一词。首先看下三个处理消息msg的对象:msg.callback其实就是我们之前说的Handler的post方法中的Runnable对象,handleCallback方法直接调用Runnable对象的run方法。也就说,如果这里run方法阻塞的话,那后面的几个方法就麻烦了。mCallback则是Handler的内部接口Handler.Callback,只有一个handleMessage方法。前面说到,这个方法的返回值是布尔值,直接关系到Handler子类的方法是否会被执行,有点像事件的分发机制中的事件传递。this是调用者Handler对象本身,根据Java多态的动态绑定,优先调用派生Handler的子类的重写方法。羞耻地盗了一张图,总的来说,调用顺序是 post方法中的Runnable对象—->Handler.Callback方法——->Handler的子类/Handler的重写方法(取决于Callback的返回值)

讲到这里,依靠着Looper.loop方法MessageQueue的消息循环完成了,取出的Message对象找到了自己的归宿。但还有一个问题,传送带还没停掉呢。所以我们看看是Loope-quit/quitSafely方法如何收尾的。

Loope-quit()/quitSafely()

/** * Quits the looper. * <p> * Causes the {@link #loop} method to terminate without processing any * more messages in the message queue. * </p><p> * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. * </p><p class="note"> * Using this method may be unsafe because some messages may not be delivered * before the looper terminates. Consider using {@link #quitSafely} instead to ensure * that all pending work is completed in an orderly manner. * </p> * * @see #quitSafely */ public void quit() { mQueue.quit(false); } /** * Quits the looper safely. * <p> * Causes the {@link #loop} method to terminate as soon as all remaining messages * in the message queue that are already due to be delivered have been handled. * However pending delayed messages with due times in the future will not be * delivered before the loop terminates. * </p><p> * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. * </p> */ public void quitSafely() { mQueue.quit(true); } 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); } } 这两个方法都是调用MessageQueue的quit方法,顾名思义,它们的区别就是是否安全退出looper消息循环。源码注释里详细说明了,它们的区别在于是否处理完剩下的消息而退出。从mQuitAllowed的判定和异常信息可以看出,这确实是Handler消息机制留给主线程的后门。其实这里个人有个小小的疑问:为什么prepare和loop是static方法,而quit/quitSafely是非static方法?针对是否安全退出,相应的处理方法是removeAllMessagesLocked/removeAllFutureMessagesLocked,先对消息进行处理,然后调用native方法。 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); } } }

讲到这里,顺着Handler的上层接口的使用过程,我们就完成了对Handler运作过程的解析,虽然也保留了一部分疑问和C++/Linux层的问题,但对整个过程已经有了大体的掌握。 下面我们总结一下Handler、Looper、Message、MessageQueue。

总结Handler、Looper、Message、MessageQueue

再次翻出这张图

Message

Message是线程之间传递的消息,或者说是封装了消息内容的实体类。承担的角色相当于是流水线上的货物。常用的几个成员变量有:int类型的what字段、int类型的arg1和arg2、Object对象的obj字段。

MessageQueue

MessageQueue是存储消息的数据结构,被称为消息队列,其内部实现是单链表。MessageQueue和Looper对象时绑定在一起的,其角色相当于流水线上的传送带,需要Looper提供动力源和插入删除操作。其主要方法是enqueueMessage方法和next方法。

Looper

Looper是基于ThreadLocal的线程唯一的数据对象,与MessageQueue绑定在一起。其承担的角色相当于流水线上的发动机+机械臂,与传送带(MessageQueue)密不可分。其主要方法是prepare方法、loop方法、quit方法和quitSafely方法。与主线程相关的是prepareMainLooper方法、getMainLooper方法。

Handler

Handler是用于发送和处理消息的。其承担的角色相当于是处理者。(毕竟计算机世界和真实世界是有差别的,想不到什么恰当的比喻。)其主要方法是sendMessage方法、post方法和handleMessage方法。

主线程消息循环

也许看完上面的通用型的Handler机制后,有很多同学就在想,我们在主线程可不是这么用的,根本不需要什么Looper.prepare方法做准备,也根本不用quit方法来停止循环。

确实是这样,但原因仅仅是因为,Android的消息机制并不是只为了子线程更新UI或者网络异步处理这种场景而生的,它的适用范围甚至包括了Android主线程的所有消息,包括了Activity、Service这些组件的消息处理等,利用这些消息,Android就可以是先组件生命周期的管理和生命周期方法的回调。

主线程的消息机制确实是比较复杂的,涉及到Linux管道,所以接下来的篇幅我们对整个过程做大致的了解先。更加具体的分析会在后续进行更新。

ActivityThread

引用任玉刚大神的话来说,“Android的主线程是ActivityThread,主线程的入口是main,…”。但我之前在知乎看到关于Android主线程消息循环的回答,引用Gityuan(小米系统工程师、IBM、联想)大神的话,“…,ActivityThread实际上并非线程,不像HandlerThread类,ActivityThread并没有真正继承Thread类,只是往往运行在主线程,该给以线程的感觉,其实承载ActivityThread的主线程就是由Zygote fork而创建的进程。”ActivityThread是不是线程不重要,但绝对是程序的主入口,main方法都摆在那里了。下面我们来分析一下这个main方法。 public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); SamplingProfilerIntegration.start(); CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); AndroidKeyStoreProvider.install(); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("<pre-initialized>"); 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"); } } 从上面的代码可以看出,代码的逻辑确实是存在Looper.prepare系列方法来准备一个Looper对象的,也通过Looper.loop方法开启了消息循环,但是却没有关闭(整个程序的生命周期里Activity等组件的生命周期方法被调来调去,主线程必须始终响应),而且,前面也说道,Handler为主线程留了后门,Looper的消息循环已经是不可关闭的,才得以保证程序的稳定。所以,Android主线程使用正常的消息机制是断定无疑了。那么问题就变成了,为啥Android主线程不用关闭消息循环而不会阻塞主线程呢?我们平时不开启独立线程时,几乎任务都在主线程完成,主线程又能及时响应呢? -到这里, Android消息机制的学习和分析也差不多了,这里顺便把前面保留的问题和未分析的问题列一下。 1.Handler的内存泄露问题2.MessageQueue对同步消息和异步消息的处理问题3.MessageQueue的底层实现4.为什么prepare方法和loop方法是static,而quit/quitSafely方法是非static5.Android主线程如何避免阻塞对Android主线程阻塞问题感兴趣的同学,我就送上知乎Gityuan大神回答的传送门。

参考资料: - Android应用程序消息处理机制(Looper、Handler)分析 http://blog.csdn.net/luoshengyang/article/details/6817933/ - Android中为什么主线程不会因为Looper.loop()里的死循环卡死? https://www.zhihu.com/question/34652589

转载请注明原文地址: https://www.6miu.com/read-64711.html

最新回复(0)