先来看一下Message从发出到处理的过程。
先看一下Message的结构
Message中的变量:
//变量
public int what;
public int arg1;
public int arg2;
public Object obj;
Bundle data;
Handler target;//处理这个message的handler
Runnable callback;//我们post的Runnable
Message next;//他有next,相当于一个链表
//静态变量,也就是相当于全局变量
private static Message sPool;//已经用完的message,作为message池用来复用。(message.obtain()方法取的就是池子里的)最大长度50.
private static int sPoolSize = 0;//当前池子的大小。
再来看一下把Message放到MessageQueue中的地方:
boolean enqueueMessage(Message msg, long when) {
//省略一些代码
synchronized (this) {
//省略一些代码
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;
}
代码比较长,我省略了一些不影响主流程的代码
- 将message标记为正在使用
- 给message的when赋值,也就是我们的是否postdelay,是delay则在当前时间加上delay时间否则when就是当前时间
- 根据when的值确定是将message添加到队列头还是添加到队列中间,如果是添加到队列头部则还会涉及到是否需要唤醒线程去处理消息。如果是插入队列中间则不需要去唤醒。
(是否唤醒这块没看懂,涉及到native的方法。)
再来看一下调用方looper
looper会不断从messageQueue中获取消息处理
loop方法中比较重要的几句
public static void loop() {
//...
for (;;) {
Message msg = queue.next(); // might block
//...
msg.target.dispatchMessage(msg);//调用handler的dispatchMessage处理消息
//...
msg.recycleUnchecked();//将msg加入到复用msg队列
}
}
从上面的代码中可以看出
- 他会调用MessageQueue的next方法获取Message
- 获取到Message之后会通过handler的dispatchMessage方法来处理消息
- 消息处理完成之后会回收消息。
再看看MessageQueue的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 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;
}
}
这个方法比较长,也比较难读懂,而且也有会调用C++的方法
其实就是遍历Message队列,找到when不小于当前时间的Message返回,如果队列为空,或者Message的when都是未来时间,这时候会处理IdleHandler的东西。
再来看handler的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从发出到处理需要经过的过程。
- post的不管是一个Message还是一个Runnable都会封装成一个Message。
- 被handler加入到messageQueue中,会根据Message的when来排序,也就是根据时间排序。
- Looper会不断从messageQueue中通过调用next获取message,这个next里有很多逻辑,涉及到idleHandler。
- Looper获取到Message之后会调用Message的target其实就是他引用的handler的dispatchMessage方法处理Message。
- 如果message有callback也就是Message本身是一个runnable的话就会执行这个runnable。
- 如果不是一个runnable,会先去调用handler的成员变量mCallback的handleMessage方法,如果这个的callback返回一个true则直接结束了,如果返回false会继续调用handler本身的handleMessage方法。
到此这个message就被处理了。但是其实中间还有很多细节,比如
在加入到messageQueue的时候,会通过message的when时间来插入队列,并且确定要不要唤醒队列(native方法,这里没太搞明白)
一些细节:
-
Message中是含有handler的引用的,所以Handler的handlerMessage中如果引用了外部activity的非static的方法或者变量,就可能产生泄露,
-
在sendMessage的时候他会把handler赋值给message的target
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
- postRunnable其实也是发送一个message:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
这个message有一个callback,
- Message的复用机制
Message使用完成之后会被标记为使用加入到一个static的链表的表头,下次通过Message的obtainMessage的时候就会从这里取而不是重新创建并且清除使用的flag标记,这个链表的最大长度是50.
removeMessage如何操作的
也就是通过what来removeMessage
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h && p.what == what
&& (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 && n.what == what
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
第一个while循环直接找当前节点以及当前节点的下一个节点是不是对应handler的以及what对不对(循环条件就是这个判断条件),如果中间不断开则一直执行,如果有一个节点不属于这个就会走到下一个循环,这时候的while循环的条件不再是判断条件,而是p等不等于null,也就是到没到链表结尾,中间的if做了判断,如果有节点满足条件则会被回收。
removeCallback如何操作的
跟上面的通过what来removeMessages差不多,不过这里是通过runnable来remove的
void removeMessages(Handler h, Runnable r, Object object) {
if (h == null || r == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h && p.callback == r
&& (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 && n.callback == r
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
从上面两个方法看来removeCallbacks等等都需要具体的runnable或者what参数,所以这两个方法直接传null并不会执行任何东西。
如何remove这个Handler发出的所有Message
如果想要移除对应handler所有的message可以使用这个:
/**
* Remove any pending posts of callbacks and sent messages whose
* <var>obj</var> is <var>token</var>. If <var>token</var> is null,
* all callbacks and messages will be removed.
*/
public final void removeCallbacksAndMessages(Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
object传一个null。
因为这个代码是这么写的:
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;
}
}
}
如果object传null会移除这个handler的所有的message。
所以总结一下怎么使用handler会产生泄露,如何避免
先了解一下什么是内存泄露,什么时候会内存泄露:
如上图,当一个生命周期长的对象B引用了一个什么周期短的对象A,这个时候生命周期短的对象A就泄露了,他本来早就应该被回收的。比如Message可能长时间存活于MessageQueue中,而Message引用了Runnable,Runnable隐式引用了activity,这时候activity的对象的生命周期就被迫和Message的生命周期一样长了,如果一个MessagePostDelay10分钟,如果用户反复打开这个activity,每次打开创建一个实例,该销毁的销毁不了这时候就会出现很多这个activity的实例,这时候这个activity就泄露了。
在看看具体的使用场景
使用handler有两种方式:
- 一种postRunnable,在Runnable里做更新操作。
- 一种postMessage,在handleCallback里做操作(不管是callback的handleMessage还是handler本身的handleMessage)
使用Runnable,如果是在activity中使用Runnable,Runnable是一个非static的内部类的时候,他是会持有外部类的引用的,所以runnable引用activity,message引用runnable,而message的生命周期又超长,activity就会被泄露。
这时候可以在activity的onDestroy里使用handler的handler.removeCallbacksAndMessages(null)
这个方法会移除这个handler所发出的所有的mssage(runnable也是message),(当然这里也可以通过removeMessage传入一个Runnable来移除消息,但是你需要一直引用你的那个Runnable)
使用Message,然后通过message.what在handleMessage中处理。
这里会涉及到两种方式:
在创建handler的时候传入了一个callback,在这个callback的handleMessage里处理消息。
还有就是直接复写handler的handleMessage方法。
这两种在dispatch的时候还是有区别的:
/**
* 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);
}
}
会先处理callback的handleMessage,如果这里返回了true,则不会再去调用handler的handleMessage,否则会调用handler的handleMessage方法。
这两种方法的泄露点在哪里呢?
Message是引用handler的,handler如果不是static的那他就会持有一个外部对象的引用,很多时候他的外部对象都是activity,所以也就是长生命周期的Message间接引用了activity。
避免这两种用法带来的内存泄露有两种方法:
通过what来removeMessage
还是通过handler.removeCallbacksAndMessages(null)
来移除这个handler发送的所有的消息。
网友评论