Android消息机制(平时都称为Handler,虽然Handler只是其中的一个类,但是开发者都知道说到Handler时都是指 Android消息机制,所以本文用Handler指代Android消息机制)是Android系统中十分重要的模块,平时开发中经常会使用到,当然也是Android面试官比较喜欢考察的地方。初级开发者一般掌握怎么使用也就够了,中高级开发者是必须要掌握其中的原理的,掌握了其中的原理能更好地使用Handler以及在碰到问题时能快速定位。我最近认真地看了下Handler的源码,以下是我对Handler源码的一些理解,有不正之处还请各位同行不吝指正,谢谢!
-
Handler的使用
Handler平时用的较多的地方是在子线程中做完耗时操作后再通知UI线程更新UI,当然也可以在任意子线程与子线程间通信。使用Handler时,如果不注意容易导致内存泄漏,怎么使用Handler避免内存泄漏后文将单独讲解,此处先讲解子线程与UI线程之间及子线程与子线程间使用Handler通信。
1.1子线程与UI线程间通信
首先在UI线程内创建一个Handler,然后在子线程中使用刚创建的handler往UI线程发送消息,最后UI线程接收消息并处理。
public class TestActivity extends Activity {
private Handler mUiHandler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//(1)在UI线程创建Handler
mUiHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
//(3)子UI线程取出消息
int what = msg.what;
String obj = (String) msg.obj;
}
};
new Thread(new Runnable() {
@Override
public void run() {
//(2)在子线程发送消息
Message message = Message.obtain();
message.what = 0x100;
message.obj = "来自子线程" + Thread.currentThread().getName() + "的消息";
mUiHandler.sendMessage(message);
}
}).start();
}
}
在定义mUiHandler时,也可以定义一个Callback传进去作为处理消息的对象:
mUiHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
//Callback的handleMessage处理消息,
//返回true的话表示Callback处理了消息,Handler的handleMessage将收不到消息,
//返回false的话表示Callback没有处理消息,Handler的handleMessage能收到消息
return true;
}
}){
@Override
public void handleMessage(@NonNull Message msg) {
//Handler的handleMessage处理消息,handleMessage可以不实现
}
} ;
消息处理的原理将在后面通过源码解读来解释。
1.2子线程与子线程间通信
首先在子线程A内创建一个Handler,然后在子线程B中使用刚创建的handler往子线程A发送消息,最后子线程A接收消息并处理。
public class TestActivity extends Activity {
private Handler mThreadAHandler;
private Thread mThreadA;
private Thread mThreadB;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mThreadA = new Thread(new Runnable() {
@Override
public void run() {
//(a)准备looper
Looper.prepare();
//(b)创建 Handler
mThreadAHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
int what = msg.what;
String message = (String) msg.obj;
}
};
//(c)启动looper
Looper.loop();
}
}, "子线程A");
mThreadA.start();
mThreadB = new Thread(new Runnable() {
@Override
public void run() {
//在子线程B中向子线程A发送消息
Message message = Message.obtain();
message.what = 0x200;
message.obj = "来自" + Thread.currentThread().getName() + "的消息";
mThreadAHandler.sendMessage(message);
}
}, "子线程B");
mThreadB.start();
}
}
从上述子线程间的通信代码可以看出,在子线程A中创建Handler之前需要先调一下Looper.prepare(),在创建Handler之后需要调一下Looper.loop()以启动looper,且三者之间的顺序不能打乱,这是与在UI线程内创建Handler不同的地方。为什么会这样,是因为UI线程从一启动开始就在不断往消息队列放消息,同时不断从消息队列取消息以更新UI,所以UI线程从一开始就必须要创建looper并启动looper,所以在UI线程启动时就系统就创建了looper并启动了looper。
2 Handler原理
以上对Handler的使用做了简单的介绍,下面将根绝源码对Handler的实现原理做详细的介绍,鉴于子线程与UI线程之间的通信流程与子线程间的通信流程一样,所以这里只根据介绍子线程间的通信流程来分析Handler的原理。
2.1 创建Looper
创建Looper是通过调用Looper.prepare()来实现的,并同时创建消息队列。
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));
}
ThreadLocal用于实现在不同的线程中存储线程私有数据的类,这里线程的sThreadLocal存放的是Looper且线程的sThreadLocal中最多只能存放一个Looper,超出1个就会抛出异常"Only one Looper may be created per thread",所以一个线程只能有一个looper。再来看一下sThreadLocal怎么存储Looper的:
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);
}
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();
}
线程维护了一个ThreadLocalMap,当map不存在时就为线程创建一个map,并以ThreadLocal作为key存储value,这里的value是Looper ,当线程的map存在时,则以ThreadLocal作为key,将value存储在map中,这样就达到了线程与Looper的绑定。
2.2 创建消息队列
2.1分析到在创建looper时,new 了一个looper对象,并将该looper对象存在了ThreadLocal中:
sThreadLocal.set(new Looper(quitAllowed)),现在看一下new Looper(quitAllowed)源码,
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
进入Looper的构造函数创建了一个消息队列mQueue,这样便为looper创建了一个消息队列,前面分析可知一个线程只能有一个looper,而一个looper又对应一个消息队列,所以一个线程也只能有一个消息队列。
分析到这里,Looper.prepare()便分析完了,又前面分析可知,Looper.prepare()创建了一个looper,并将looper存储在线程的
ThreadLoca中,完成looper与线程的绑定;在L
ooper的构造函数中为L
ooper对象创建了一个消息队列。
2.3 创建Handler
在1.2中,使用Handler在子线程间的通信的第二步是通过无参构造函数创建了一个Handler,进入Handle的构造函数看一下源码:
/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public Handler() {
this(null, false);
}
又调了有参构造函数:
public Handler(@Nullable 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 " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从代码可以看到,先通过Looper.myLooper()给mLooper赋值,看一下Looper.myLooper()的源码:
/**
* 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();
}
从当前线程的sThreadLocal中取得一开始存在sThreadLocal中的Looper对象。
从if语句:
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
可知,如果当前线程的mLooper不存在,则抛出异常,这就是为什么在子线程中创建Handler之前需要先调一下Looper.prepare()创建了一个Looper。
mQueue = mLooper.mQueue;取到mLooper的消息队列。
mCallback = callback;表示处理消息的回调实例,就是在1.1中讲到的“在定义mUiHandler时,也可以定义一个Callback传进去作为处理消息的对象”。
mAsynchronous 不是Handler机制的核心内容,这里先不做分析。
2.4发送消息
在1.2中,线程B发送消息时,先创建了一个消息对象,
Message message = Message.obtain();再看一下Message.obtain()源码:
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool; //消息池
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
从注释以及代码可知,先判断消息池sPool中有没有消息,有消息池中有消息的话就复用息池中的第一个消息,没有的话就新创建一个消息,这样能避免重复创建消息对象,消息池sPool是一个链表。
1.2发送消息调用的是方法
mThreadAHandler.sendMessage(message),发送消息有很多方法,也可以指定延迟时间,,sendMessage源码如下:
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
0表示不延迟发送,
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
将延迟的时间与当前时间相加,转化成具体的发送时间,发送消息最终都会调到方法:
sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);再看一下sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)源码:
public boolean sendMessageAtTime(@NonNull 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);
}
取到了当前Handler的消息消息队列MessageQueue queue = mQueue,在创建Handler的时候已经给mQueue赋值了的,再进入Handler的enqueueMessage(queue, msg, uptimeMillis):
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
由msg.target = this;可知,将当前Handler对象赋给了消息msg的target,这个target是最终接收消息的目标,后续分析取消息🉑️的时候将会看到确实如此。再进入消息队列的MessageQueue的queue.enqueueMessage(msg, uptimeMillis):
boolean enqueueMessage(Message msg, long when) {
//这里表示如果没有创建Handler的话就会抛出没有目标异常
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;
}
往消息队列中插入消息的核心代码就在if else里,if中的逻辑是如果消息队列中的消息头为空也就是还没有消息,或者要送的消息即时发送,或者要发送的消息在消息队列中的消息头前面发送,那么就将新的要发送的消息插入到消息队列头部;else中的逻辑是:从消息头循环往后找,当消息队列中出现的消息的发送时间比将要插入的消息的发送时间靠后时,则将要发送的消息插入在消息队列中该消息的前面。由此可知消息队列消息的排列顺序是按照消息发送时间先后排列的,这样也就能达到延时发送的目的,后面分析接收消息的将会看到。
2.5 接收消息
1.2在线程A中调了Looper.loop()后,
线程A就开始了不断接收消息,进入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();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
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);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", 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();
}
}
首先获取了当前线程的looper:
Looper me = myLooper();
myLooper()在发送消息的时候已经分析,然后从Looper me中取出消息队列:
final MessageQueue queue = me.mQueue;
然后在无限for循环中不断从消息队列中取出消息:
Message msg = queue.next(); // might block,该代码可能会造成阻塞,进入queue.next()看一下为什么可能会造成阻塞
@UnsupportedAppUsage
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 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中的代码可知,从消息队列中取出队头的消息,然后将消息的时间与当前系统时间比较,如果消息的时间大于当前系统时间,则表示需要延迟发送,则进入阻塞状态,将阻塞时间传入原生方法进行阻塞,
nativePollOnce(ptr, nextPollTimeoutMillis);否则,表示消息需要立即发送,则取出消息队列中的该消息。在分析发送消息时往消息队列中插入消息就已经知道,消息队列中的消息是按照时间先后顺序排列的,所以队头的消息总是最先发送的也就是需要取出的消息。
通过分析往队列中按时间顺序插入消息,和从队列中取消息的机制就很清晰地知道了Handler是如何巧妙地实现延迟发送消息。
取出消息后,回到Looper中loop()中的无限for循环,当从消息队列中取出消息后会调:
msg.target.dispatchMessage(msg);
在前面分析发送消息时,已经说到这里msg.target也就是我们一开始在线程A中创建的Handler,也就是最终处理消息的目标,所以进入Handler的dispatchMessage看一下:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在1.1中说到,定义mUiHandler时,也可以定义一个Callback传进去作为处理消息的对象,从else中的代码可知,如果定义了Callback,则最终会调Callback的handleMessage来处理消息,否则就调Handler的handleMessage来处理消息。到这里就将Handler的核心代码分析的差不多了,Handler从发送消息到最终消息被处理的流程基本上就是这个样子了,源码里面的很多其他代码就不做分析了,不影响理解Handler的通信原理。
总结,本文核心模块总结梳理如下:
1、详细讲解了Handler的使用;
2、分析了Looper的创建;
3、在Looper的构造函数中创建了消息队列;
4、分析了Handler对象的创建,以及给Handler对象的looper、消息队列、CallBack赋值,以及分析了为什么要先创建Looper对象再创建Handler对象;
5、分析了发送消息时往消息队列中按时间顺序插入消息;
6、分析了消息最终又发送给Handler进行处理的原理;
7、分析了消息接收以及怎么实现延迟发送的原理;
8、分析了最终消息是由CallBack处理还是由Handler自身处理的原理。
以上是我对消息机制的理解,有不正之处还请大家不吝指正以期共同进步!