这次blog主要分析Handler,关于其他的部分会在以后的blog中进行分析,Handler是异步消息处理机制的基础,并且其他异步消息处理也与Handler有着莫大的联系。(我的源码分析为参考相关资料,自己实际源码调试检测,从实际调试的过程中去深入理解,希望大家有实际调试经验或者技巧,可以共同分享,望共勉)网上关于源码分析的文章数不胜数,但是我没有发现过文章是从实际调试过程中去引导我们去学习源码(个人认为只读资料这种源码学习方式很被动),所以我就以个人源码调试的过程为记录,与大家共同学习源码。
此处默认读者已经会Handler的基本使用方法,所以就不再于此赘述Handler的基本用法。但是接下来我会先给出Handler执行流程的执行流程图,让大家先对整体的过程中涉及到的对象以及相关方法有一定的了解:
考虑再三,还是决定附一段代码截图,因为此代码可以让大家更好的参照流程图理解(代码很简单,主要是看实现):
主要的实现就是这两个,主要知道我怎么做的就好,流程图中的内容会参考此部分。
先给大家看一张整体的流程图,从onClick触发器,到handleMessage执行,到最后的Toast得到处理的全过程。
那么,接下来就看具体的调试的流程截图了:
上图就是onClick执行,所以接下来会涉及到消息的封装和发送。
如上图,Bundle就是在onClick中的实现,所以接下来看Messgae的截图:
如上图,这就是Message的实现过程,最后setData()包装了Bundle对象。
如上图,在onClick最后,我们向handler发送了消息,所以此处调用sendMessage(),还许哟啊注意enqueueMessage方法的调用。
上图调用的是MessageQueue的next方法,留下一个疑问,MessageQueue是什么时候启动的呢?怎么此处直接调用了其next()方法,难搞哦,继续流程图:
这就涉及到了Handler的dispatchMessgae方法,和实现Handler并重写其handleMessage()方法实现自定义逻辑(我在这里用了一个Toast的显示作为handleMessage的处理,如上图的红框,我们就可以知道handleMessage方法得到执行)
但是可能都会有一个疑问,为什么没有Looper相关的东西,Handler不是还有其他三个兄弟吗?Message, MessageQueue, Looper。我们为什么看流程图的时候没有出现Looper,请看下图:
你可能会好奇,怎么Looper实在ActivityThread之后就执行loop方法了,还有MessageQueue也被涉及了,不过此处只需要知道Looper不是没有启动,只是在程序启动时已经得到执行了(相信分析过Looper相关内容的同志,可以知道此处的逻辑了),Looper的作用在此流程图中没有很好体现,在分析部分会做说明。知道了整体的执行流程,接下来我们先从概念入手,先看以下Handler比较概念性的东西。
首先,对于异步消息的四部分先做简要说明:
Message:是在线程之间传递的消息,它可以携带少量的信息,用于在不同线程之间交换数据
Handler:主要用于发送和处理消息,常用方法sendMessage()和post()
MessageQueue:消息队列,主要用于存放所有通过Handler发送的消息,这部分消息会一直存放在消息队列中,等待被处理,每个线程只能有一个此对象
Looper:Looper相当于MessageQueue的管理者,调用其loop()方法后,就会进入到无限循环中,只要有消息存在,就会将它转发到Handler。同样,每个线程一直会有一个Looper对象。
下面就有一个简单的示例进入分析:
//这里只有主要用到的代码,但是希望大家对基本使用已经熟练
private Handler mhandler = new Handler(){
@Override
public void handleMessage(Message msg) {
...// 执行的UI操作
}
};
......
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "string";
......
mHandler.sendMessage(msg);
在正式开始分析之前,我想以Message的源码先作为热身,这是整个Handler分析中的消息载体,而且会是不是的就用到它,所以就以它作为铺垫:
/*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;
/** @hide */
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;
......
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();
}
public static Message obtain(Message orig) {
Message m = obtain();
m.what = orig.what;
m.arg1 = orig.arg1;
m.arg2 = orig.arg2;
m.obj = orig.obj;
m.replyTo = orig.replyTo;
m.sendingUid = orig.sendingUid;
m.workSourceUid = orig.workSourceUid;
if (orig.data != null) {
m.data = new Bundle(orig.data);
}
m.target = orig.target;
m.callback = orig.callback;
return m;
}
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
public static Message obtain(Handler h, int what) {
Message m = obtain();
m.target = h;
m.what = what;
return m;
}
public static Message obtain(Handler h, int what, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.obj = obj;
return m;
}
public static Message obtain(Handler h, int what, int arg1, int arg2) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
return m;
}
public static Message obtain(Handler h, int what,
int arg1, int arg2, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
m.obj = obj;
return m;
}
从以上Message的源码中,我们看见了频繁使用的参数,当然还有一个next引用,相信大家已经明了它的作用,对于target指向的也是一个Handler对象,其作用会在以后的源码分析中看到。继续看,Message内部维护了一个消息池sPoolSync,使用obtaion()则直接从池内获取,由此我们知道尽量可以使用obtain()方法创建Message对象,避免new重新分配内存。然后,若池内无消息对象可复用,则会调用new关键字创建,Message的分析就简单的到这。
然后,我们可以看到一个内名内部类方式创建的Handler类对象,所以就看一下Handler的几个构造方法:
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
//请读者主要注意此构造方法
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());
}
}
//关联当前线程的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//关联当前线程的MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
从以上源码可以找到我们调用的构造方法最后的执行者是Handler(Callback callback, boolean async),它的内部自动关联了当前线程的Looper对象和MessageQueue对象,所以实现了绑定实现创建Handler对象的操作线程。但是Looper和MessageQueue是在构造方法里就关联的,那么他们又是在何时创建的呢?刚好也对应了我们流程图中的问题,我们发现在最后一张流程图实在ActivityThrad.main执行后就有了Looper和MessageQueue的影子,所以我们就先来看ActivityThread.main()的源码:
// 在Android应用进程启动时,会默认创建1个主线程(ActivityThread,也叫UI线程)
// 创建时,会自动调用ActivityThread的1个静态的main()方法 = 应用程序的入口
public static void main(String[] args) {
... ...
Looper.prepareMainLooper();
/* 作用:为 主线程(UI线程) 创建1个循环器对象(Looper),同时也生成了1个消息队列对象(MessageQueue),该方法在主线程(UI线程)创建时自动调用*/
ActivityThread thread = new ActivityThread();
Looper.loop();
}
我们看到此处执行了Looper的一个prepareMainLooper(),看其名字我们大概能猜出其作用,为了验证,再看下面源码:
//Looper的prepareMainLooper()
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//prepareMainLooper()中调用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));
}
//sThreadLocal.set(new Looper(quitAllowed))调用,关联了一个MessageQueue对象
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
当创建主线程时,会自动调用ActivityThread
的静态main()
;而main()
内则会调用Looper.prepareMainLooper()
为主线程生成1个Looper
对象,然后通过ThreadLocal来保存这个Looper(ThreadLocal是一个线程级的单例),同时Looper在创建的时候创建了一个MessageQueue对象,通过Looper中的一个final成员变量保存起来,这样就保证了一个线程中只能有一个MessageQueue。到了这里,我们就能知道Looper和MessageQueue的创建时机了。因为Looper是MQ的管理者,那么我们就先来看被管理者的实现,然后再看管理者管理的实现。
所以呢,接下来分析MessageQueue:
public final class MessageQueue {
......
Message mMessages;
private final ArrayList mIdleHandlers = new ArrayList();
private SparseArray mFileDescriptorRecords;
private IdleHandler[] mPendingIdleHandlers;
private boolean mQuitting;
......
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
.......
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;
}
// 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;
}
}
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);
}
}
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;
}
......
}
这里我们主要看他的next(),enqueueMessage()方法(quit()方法就是经过判断各个参数,然后安全的退出MQ),每次使用Handler发送一个Message的时候,最终会先调用MessageQueue的enqueueMessage方法将Message方法放入到MessageQueue里面(我们在流程图中也看到当调用sendMessage后,就会调用enqueueMessage方法,忘记的可以回看以下流程图)。对比分析,它的执行过程由以上源码可知:
关于next()方法,这里首先做铺垫,因为在Looper的分析中也会用到此方法。next()方法的执行过程如下:
分析完MessageQueue,我们就该分析管理MQ的Looper了(知道了MQ,可以更容易分析Looper),在上面分析ActivityThread的时候,我们就知道Looper和MQ的创建,以及Looper在其构造方法中关联MQ的过程,最后在main()中执行了一个Looper.loop()方法,所以我们应该看loop方法到底做了什么,其源码如下:
......
public static void loop() {
final Looper me = myLooper();
......
final MessageQueue queue = me.mQueue;
......
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
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)) {
slowDeliveryDetected = true;
}
}
}
msg.recycleUnchecked();
}
}
现在开始分析,当此方法获取到Looper和消息队列对象,然后通过无限for循环,从消息队列中取出消息(next()方法),并通过
msg.target.dispatchMessage(msg);
将消息派发到对应的目标Handler(通过把消息派发给target(target实际是一个handler对象)),最后完成消息资源的释放。所以接下来一个重要的方法,dispatchMessage(msg):
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
观察源码,如果Callback为空,则执行的是handleMessage(msg),若不为空,则调用handleCallback(msg);
此处进行说明:
msg.callback
属性不为空,则代表使用了post(Runnable r)
发送消息,则直接回调Runnable
对象里复写的run()
msg.callback
属性为空,则代表使用了sendMessage(Message msg)
发送消息,则回调复写的handleMessage(msg)
最后通过自定义handleMessage()方法,完成对消息的处理。
所以读者对于post和sendMessage的使用有了更深的认识。(post()方法内部与sendMessage()原理雷同)
现在我们知道了MQ, Looper,Message,Handler的handleMessage也有我们自己重写,那么还剩下什么呢?没错,就是发送消息了。所以,现在分析sendMessage(msg)方法,他在工作线程(自己创建的子线程)发送消息到消息队列:
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) {
......
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
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;
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
相信大家可以从以上的源码中看出,经过层层调用,最后交给了enqueueMessage()方法执行(此是MessageQueue的方法)。【注意:在enqueueMessage方法调用的:
msg.target = this;
在此之前分析的Looper的loop()消息循坏时,从消息队列中取出每个消息的target,并去执行
msg.target.dispatchMessage(msg)
最重要的关于MessageQueue的方法enqueueMessage,内部用一个队列来保存消息。之后,随着Looper对象的无限循环,不断从消息队列中取出Handler发送的消息,并发布到相应的Handler,最终回调Handler.handMessage()处理消息。
虽然直到了基础的源码,也基本了解了Handler的执行过程。但是我们对于Handler的常见问题就会作答了吗?以下是我在平时遇到的问题,与大家分享(关于Handler还有很多很多的问题,请大家自行百度):
首先,对于这个问题,我们必须要知道子线程中是可以更新UI的
测试代码:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); tv_thread = findViewById(R.id.tv_thread); new Thread(new Runnable() { @Override tv_thread.setText("子线程加载"); }).start(); }
这段代码读者可以自行测试,同时,在测试时让其休眠一段时间,再观察结果,我们就可以知道异同(读者请自行测试)。
当应用程序启动时,mThread(MainThread / UIThread)就被初始化了,当我们访问UI的时候,ViewRoot会检查是哪个线程在访问UI,若不是主线程,则会抛出异常;但为什么在onCreate()方法中就可以访问呢?那是ViewRootImpl的创建在onResume()方法回调之后。所以在onCreate()方法中进行访问时,它还没来的及创建。
那问题来了,为什么UI更新一定要在UI线程里实现呢?
目的在于提高移动端更新UI的效率和和安全性。
原因是:
Android的UI访问是没有加锁的,多个线程可以同时访问更新操作同一个UI控件。也就是说访问UI的时候,android系统当中的控件都不是线程安全的,这将导致在多线程模式下,当多个线程共同访问更新操作同一个UI控件时容易发生不可控的错误,而这是致命的。所以Android中规定只能在UI线程中访问UI,这相当于从另一个角度给Android的UI访问加上一个伪锁。
这个问题在于直接在子线程中创建了Handler对象,而没有在子线程中创建一个Looper对象。这句话已经给出了答案,
原因如下:
handler构造函数中 需要持有一个looper的对象,如果没有,提示这个错误;
looper的主要作用是与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。然后looper的loop()方法就是不断从MessageQueue中取出消息,交给handler去发送消息。解决方法:
只需要调用prepare()方法,新建looper对象
当然,这只是这个问题的浅显回答,关于更深入的理解,我们还得多理解源码。
如何在子线程创建Looper?
上一题中已经有答案,那就是调用Looper的prepare()方法
Handler.post的逻辑在哪个线程执行的,是由Looper所在线程还是Handler所在线程决定的?
从源码分析中,我们可以知道是由Looper所在的线程决定(在Looper.loop()方法中,从MsgQueue中拿出msg,并且执行其逻辑)
Looper和Handler一定要处于一个线程吗?子线程中可以用MainLooper去创建Handler吗?
可以,子线程中
Handler handler = new Handler(Looper.getMainLooper());
,此时两者就不在一个线程中。
Handler的post方法发送的是同步消息吗?可以发送异步消息吗?
用户层发送的都是同步消息,不能发送异步消息;异步消息只能由系统发送。
主线程如何向子线程发送消息?
自己创建继承Thread的子线程,内部实现一个handler,并将Looper启动起来(子线程默认不会启动Looper),然后主线程调用子线程的Handler对象发送消息。
具体的代码请读者自行实现。
Looper.loop()是如何阻塞的?MessageQueue.next()是如何阻塞的?
请读者自行看源码。
Handler的内存泄漏问题?
内存泄漏:
Java使用有向图机制,通过GC自动检查内存中的对象,如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收。(一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收);同样,如果一组对象中只包含互相的引用,而没有来自它们外部的引用,仍然属于不可到达,同样会被GC回收。
Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { //逻辑代码 ...... } }
如上代码所示,当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是Activity)的引用,而Handler通常会伴随着一个耗时的后台线程(eg:从网络下载图片)任务,这个后台线程在任务执行完毕之后,通过消息机制通知Handler,然后Handler把图片更新到界面。此时,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用,这个Handler又持有Activity的引用,就导致该Activity无法被回收(内存泄露),直到网络请求结束。
内存泄漏后果:
VM占用内存过高,导致了OOM,程序报错。
解决方法;
- 在关闭Activity的时候停掉后台线程
- 使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除(此方法适用于Handler被delay的Message持有引用)
为什么Android程序中的Looper.loop()不会造成ANR异常?
关于这个老生常谈的问题,此处我也因水平有限,只能给出浅显的回答。大家请自行深入学习理解,共勉。
首先:关于引发ANR异常的原因:
- 当前事件没有机会得到处理(MainThread正在处理前一个事件,没有及时完成或looper被某种原因阻塞)
- 当前事件正在处理,但没有及时完成
Android是由事件驱动的,Looper.loop()不断接受事件,处理事件,每一个点击触摸 / Activity的lifecycle 欧式在loop()的控制之下,如果它停止,则应用也会随着停止。所以换句话说,是某一个消息 / 对消息的处理 阻塞了loop(),而不是loop()阻塞它。(我们的代码实际会在这个循环里去执行)