Android学习感悟之消息机制
本篇Android消息机制的原理,包括四个方面ThreadLocal、MessageQueue、Looper和Handler,会通过消息机制流程来分析理解。
简介
由于所有UI操作都必须在主线程中完成,所以我们有时候在子线程得到的数据后需要转化到主线程去更改UI,而这就是Android提供消息机制的原因。这里有个细节,为什么UI操作必须要在主线程完成呢?其中最主要的原因是我们的View并不是线程安全的。这又有一个问题了,为什么不给View加锁呢?因为它会让View的访问变得复杂,而且会降低UI的访问效率,锁是会阻塞某些线程的执行,而使用单线程就可以避免这两个问题,然后使用Handler转换线程也不麻烦。
下面就进入正题,其实消息机制最主要的就是Handler、Looper以及MessageQueue,我简单的理了个消息机制的流程图,如下:
下面我们会根据这个流程去解读源码。
消息机制流程源码分析
Looper.prepare()
看了上边的流程图,发现我们自己在主线程中使用handler好像没有直接操作过Looper呀?其实在界面创建之前系统已经给我们初始化了Looper,源码在ActivityThread中的main方法中,如下:
public static void main(String[] args) { Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } Looper.loop();}
其中去掉了些这里不涉及的代码,可以看到其实主线程还是走了这个流程的,其中有一点不一样的地方,是主线程的looper是单独存起来的,来看源码:
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper();}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));}private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();}MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit();}
第一步prepare主要就做了两个事,在threadLocal中设置Looper,Looper中初始化MessageQueue;这里有个知识点,ThreadLocal是如何让不同线程的Looper都不一样的,如果不看系统的实现方式,我们也能大体想到,使用一个Map去存,key就是线程,value就是Looper,这样我们就能保证不同线程Looper不一样,且同一线程只有一个Looper了,这里就深入一步查看一下ThreadLocal的源码:
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value);}void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue);}ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY);}Entry(ThreadLocal k, Object v) { super(k); value = v;}private void set(ThreadLocal key, Object value) { // We don't use a fast path as with get() because it is at // least as common to use set() to create new entries as // it is to replace existing ones, in which case, a fast // path would fail more often than not. Entry[] tab = table; int len = tab.length; int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal k = e.get(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash();}
这段代码是刚才ThreadLocal保存Looper所调用的代码,大体含义还是比较简单,就是存入一个Entry数组中,每一项都有自己的key和value,其中索引是用一个hashCode和这个数组的长度进行&运算的来的。这样就会让查询变得便利,其实HashMap就是类似的储存方式。
然后再看看ThreadLocal的get方法:
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T) e.value; } return setInitialValue();}private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value;}ThreadLocalMap getMap(Thread t) { return t.threadLocals;}private Entry getEntry(ThreadLocal key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e);}private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) { Entry[] tab = table; int len = tab.length; while (e != null) { ThreadLocal k = e.get(); if (k == key) return e; if (k == null) expungeStaleEntry(i); else i = nextIndex(i, len); e = tab[i]; } return null;}
这样看来,其实ThreadLocal的功能就是类似HashMap一样,存取都是通过key,数据结构使用数组,所以是通过key的hashCode与数组长度的到。
这样对ThreadLocal应该就有了一定的了解了。
下一步。
创建Handler
创建Handler的方式有很多,但是最终不外乎两种,传入Looper和不传入Looper;如下:
不传Looper
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
public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async;}
其作用在于在handler中的得到Looper对象以及其MessageQueue,然后另外还有回调等数据。便于用户直接操作这一个类就可以了。
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; for (;;) { Message msg = queue.next(); // might block if (msg == null) { return; } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } msg.recycleUnchecked(); }}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; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } //其他代码... } }}public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}private static void handleCallback(Message message) { message.callback.run();}public void handleMessage(Message msg) {}
去掉了一些其他代码,可以看到loop这个方法是有一个死循环,只有在MeesageQueue的next返回null时,才会结束Looper的循环;而MessageQueue的next方法也是一个死循环,只有当mQuitting为true时返回null,其他时候都在循环中,当有要处理的消息时,就返回该Message,然后交给相应的Handler去处理,里边会判断callback是否为空,来判断是post的消息还是send的消息,然后再分别处理,而send的消息需要我们自己重写handleMessage方法去实现消息处理,而具体发送消息会在下文说到。
这里边涉及到的MessageQueue的数据结构其实是使用链式存储,具体的方式下文再分解,这里先知道大体流程。
Handler发送消息
Handler发送消息有两种方式:
- sendMessage(Message)
- post(Runnable)
其实他们两个方法最后都是转化成Mesasge去实现,来直接看源码。
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0);}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);}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;}
下面是post的源码,也就在第一步在构造Message对象,然后后边就一样了。
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0);}private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m;}
这里的post和send消息都还有其他的一些方法,比如延时发送,比如发送一个空消息,等等但是归根结底都是调用了enqueueMessage方法,来加入到MessageQueue中。
然后Looper的loop方法在需要处理的时候就能得到该消息,并处理。而这其中最主要的是,需要理解怎么加入到MessageQueue的。
所以我们需要先理解其存储结构,可以看到,Message.next属性,也是Message类型的,所以可以猜到应该是使用了链式存储,所以它并不是一个队列。
再来看一下enqueueMessage方法,这里跟一下代码:
当是第一条Message传入时,可以知道mMessages为空,所以进入if,然后让传入的msg.next = p, mMessages = msg,即当前消息的后一条时空,mMessages为第一条消息;
假设第一条的when是5,这时候第二条消息进入,when是10;所以会进入else,然后再进入循环遍历后加入到链表的最后;
这时候第三条消息进入,when是8;这时候还是会第三条消息的when比第一条消息的when大,所以还是会进入else,循环之后,发现不用加到最后,因为第三条的when小于第二条的when,就break了,这时候就插入到了第二条。
这时候链表的顺序就为:消息1->消息3->消息3。
最后再提供一个WeakHandler的源码,它能避免内存泄漏,原理是采用了弱引用。
import android.os.Handler;import android.os.Message;import net.arvin.afbaselibrary.listeners.IWeakHandler;import java.lang.ref.WeakReference;public class WeakHandler extends Handler { private WeakReference mActivity; public WeakHandler(IWeakHandler activity) { mActivity = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { if (mActivity != null) { IWeakHandler weakHandleInterface = mActivity.get(); if (weakHandleInterface != null) { weakHandleInterface.handleMessage(msg); } } }}//回调接口IWeakHandlerimport android.os.Message;public interface IWeakHandler { void handleMessage(Message msg);}
至此,整个消息的流程就基本分析完了。
更多相关文章
- Android四大组件:Service史上最全面解析
- Android(安卓)App 中简易的网络数据处理方法
- Android架构组件(一):Lifecycle
- android Gallery镜像倒影特效另一种方法
- Android@Kotlin 在Android(安卓)studio 中配置Kotlin
- [置顶] Android上传文件到Web服务器,PHP接收文件(一)
- Android集成微信支付功能
- Android开发之AlarmManager的用法详解
- Kotlin, Android的Swift