很久之前也写过Handler的相关文章,现在回过头去看,理解的还是较为浅薄。于是乎,决定再来深入研究一遍Handler。
首先抛出一个问题:子线程到主线程的通信方式有哪些?子线程到主线程通信的原理是什么?
你可能会回答:RxJava,Handler,EventBus,广播。但是这些表象背后的本质都是一套机制,就是Handler。可以这么说,Android线程间通信的核心就是Handler。
首先我们来看Handler的使用,具体的我就不说的,大家想必都很清楚:
子线程:handler.sendMessage()
主线程:handler.handleMessage()
大概就是通过这样的方式就实现了线程间通信,我们先从源码角度,去思考原理。Handler的源码位置在frameworks/base/core/java/android/os下面,我看的是android9.0的。因为我平时学习用的都是Mac,所以看源码的工具我用的是Sublime Text。大家一定要把系统源码下下来,直接从AS工具里面很多源码其实是隐藏的。
在Handler里面我们看到了这一行代码:
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);
}
重点来了,我们在sendMessageAtTime里面看到了一个数据结构:MessageQueue。翻译过来就是消息队列。那我们就顺着源码进去看一把这个数据结构:
Message mMessages;
我们在Message里面看到了维护的Message,再进去看一下Messaage的数据结构:
/*package*/ int flags;
/*package*/ long when;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;
Mesage里面又有一个属性:Message next。很明显了,Message是一个链表的数据结构。我们在回到sendMessageAtTime方法,发现最终调用的是enqueueMessage方法:
private boolean enqueueMessage(
MessageQueue queue,
Message msg,
long uptimeMillis){
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
最终调用的是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 {
// 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;
}
来重点看一下这一段代码:msg.target就是Handler本身对象,很明显不能为空。msg.isInUse对应的方法是msg.markInUse,如果这条消息被标记了,那么msg.isInUse就会返回true。mQuitting标记的是发送消息线程是否退出,如果退出了就没有继续走下去的必要了。当然前面都不是重点,我们来看if-else里面逻辑:
- p==null很明显不成立,上两行代码Message p = mMessage可见,一般情况不会是第一条消息(如果是第一条消息,走if逻辑,将msg赋给mMessages);
- when==0成立吗?有人会说:sendMessage里面调用了sendMessageDealyed不是传了0吗?再仔细看看,sendMessageDelayed里面做了什么,SystemClock.uptimeMillis() + delayMillis,这一段代码看到没有。然后层层传递下去,所以when更准确的理解是具体时间,而不是延迟时间;
- when<p.when成立吗?一般情况下也是不成立的。
那我们暂时只需要关注else里面代码就好了,if里面我们后面回过头再看。接下来看链表追加msg操作,我们把代码单独拿出来吧:
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;
就是这段代码,我估计很多人都看不懂,但是,却是核心中的核心!我们先将p赋给prev,很明显这个p是原有的msg链表,然后将p.next赋给p,这个时候就为null了,跳出循环。
再为msg.next赋值p,就是赋值null,然后将prev链表的next指向msg。这样就完成了消息的发送整个过程,可见:消息的发送本质逻辑就是将消息存储到MessageQueue持有的Message链表里面。
再来看一下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());
}
}
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;
}
可见,在初始化Handler的时候,Handler就和Looper建立了关联。来看下myLooper方法:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
我们不禁要想,我们可以get到一个Looper对象,那么这个对象是何时set进来的呢?
我们来看一个类:ActivityThead。这个类在frameworks/base/core/java/android/app下。
注意Main方法:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// 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());
// 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();//1
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
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();//2
throw new RuntimeException("Main thread loop unexpectedly exited");
}
代码很多,我们只需要关注标记1和标记2处。标记1处代码点进去看下:
(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();
}
}
继续看:
(Looper里面的方法)
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里面的Looper对象是何时set进来的了。
我们再来看标记2处,调用了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);
}
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;
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
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();
}
}
我们来分析一下这个方法:
- 通过myLooper拿到主线程Looper对象和Looper对象维护的MessageQueue;
- 通过一个死循环不断轮询消息队列(queue.next());
- msg.target.dispatchMessage(msg)。
重点来看下步骤3吧,我们知道msg.target就是Handler对象本身,于是我们再来看Handler的dispatchMessage方法:
public void handleMessage(Message msg) {
}
/**
* 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);
}
}
diapatchMsg最终调用的是handleMessage。这样发送消息的流程到接受消息的流程就形成了闭环。回过头来,再看一下:
子线程到主线程的通信方式有哪些?子线程到主线程通信的原理是什么?
给出简要的答案:子线程通过handler向消息队列发送消息,并且通过msg.target标志位保存handler对象引用,主线程通过Looper.loop不断轮询消息队列,并最终调用发送消息的handler的handleMessage方法。其实本质问题在于MessageQueue消息队列里面的Message链表数据结构,这一块在堆内存里面,属于线程共享区。
我们经常会听到Handler内存泄漏的问题,那么问题来了:Handler内存泄漏的原因是什么?
我们上面分析了这么长的源码,我们来看一下引用链:
MainActivity <- Handler <-Message(msg.target) <- MessageQueue <- Looper。
我们知道只要应用程序是活的,Looper对象都会在,而Handler是Acticity的内部类,内部类默认持有外部类引用,这样一来,引用链就不会断。根据GC可达性算法,MainActivity就不会被回收,引发内存泄露。注意:内存泄漏发生在子线程发送消息,而主线程还未收到消息的时候退出Acticity,比如说发送一个delay消息。如果主线程收到消息了,就会执行msg的recycleUnchecked()方法:
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
会将target置null,这样引用链也会在Handler <- Message这里断掉。