Android:关于 Handler 消息传递机制(二)
写在前面
两年前的时候有写过一点对 Handler 的总结(Android:关于 Handler 消息传递机制),现在重新回顾,并增加一些东西和理解。
内容
内存泄露
在 Activity 里我们使用 Handler 的时候,这样写的话 IDE 会提示可能存在内存泄露的问题。
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
假设在一个这样的场景,我们通过 Handler 来发送一个延时的消息,在消息还没发送之前,我们就退出了 Activity,那么此时就会存在内存泄露的问题。
原因在于 Handler 在这里是一个匿名内部类,匿名内部类会持有外部类的引用,所以此时 Activity 退出,由于 Handler 里的内容还未处理完,就还持有 Activity 的引用。所以触发 GC 回收的时候,就无法回收这个 Activity。
那么 GC 不回收一些对象是因为GC Root 的可达性,这些对象在 GC Root 上还被引用着,所以就不会被回收。
在使用 Handler 的时候,我们需要Looper.prepare();
:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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 类型的 sThreadLocal,是一个静态变量,静态变量就可以作为一个 GC Root 。
那么 Looper 里面会有个 MessageQueue,MessageQueue 里会有 Message:
public final class Looper {
...
final MessageQueue mQueue;
...
}
而 Message 的 target 是个 Handler 类型:
public final class Message implements Parcelable {
...
Handler target;
...
}
Handler 在发送消息的时候,会把自己设置到这个 target 里:
public class Handler {
...
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
...
return queue.enqueueMessage(msg, uptimeMillis);
}
...
}
于是这样一条 GC Root 的引用链就出现了:
ThreadLocal --> Looper --> MessageQueue --> Message --> Handler --> Activity
所以 Activity 退出后,因为消息未处理,就导致 Activity 无法被回收,从而导致了内存泄露。
解决方法
清空消息
在 Activity 退出的时候,把消息队列里的消息都清掉
// MainActivity.java
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
// Handler.java
/**
* 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);
}
静态内部类和弱引用
由于静态内部类不会持有外部类 的引用,所以将 Handler 以这种方式呈现,解决到 Handler 与 Activity 的引用关系。
但在一些场景下,我们可能需要在 Handler 里通过持有 Activity 来调用它的一些方法,此时如果直接传入的话,显然就造成了一种直接引用的情况,所以在这种情况下我们需要弱引用,被标记为弱引用的对象,在 GC 回收的时候,就会被回收。因此在被回收的情况下,我们就需要做多一些判空的处理。
所以当我们这个 Handler 里不需要使用到 Activity 的方法的时候,也没必要引入这个 Activity 了,也就不需要多这个弱引用的关系。
private static class MyHandler extends Handler {
WeakReference<MainActivity> mainActivityWeakReference;
public MyHandler(MainActivity activity) {
mainActivityWeakReference = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity activity = mainActivityWeakReference.get();
if(activity != null) {
activity.setTextViewText("hello");
}
}
}
创建一个 Message
我们在创建一个 Message 的时候,可能会使用
Message message = new Message();
但官方里还是推荐我们使用 Handler 的 obtainMessage()
方法,它会在一个全局的 Message 池里返回一个新的 Message 给你,从效率上会更快,并且也起到了复用的作用。
Handler handler = new Handler();
handler.obtainMessage();
这里面是一个享元的设计模式。
/**
* Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
* creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
* If you don't want that facility, just call Message.obtain() instead.
*/
@NonNull
public final Message obtainMessage()
{
return Message.obtain(this);
}
每个线程只能有一个 Looper
在使用 Handler 的时候,我们需要 Looper.prepare()
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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,后续再调用的话就会抛出异常,提示每个线程里只能有一个 Looper 被创建。
因此我们要获取当前线程的 Looper 的话,就需要使用 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();
}
ThreadLocal
待续
为什么 Looper 循环不会被阻塞
待续
推荐阅读
-
android线程消息机制之Handler详解
-
android的消息处理机制(图文+源码分析)—Looper/Handler/Message
-
android的消息处理机制(图文+源码分析)—Looper/Handler/Message
-
android线程消息机制之Handler详解
-
Android消息处理机制Looper和Handler详解
-
Android Studio 之 Android消息机制 之简单Demo --- 使用Handler和Message类来完成消息传递
-
android 消息传递机制
-
Android技术点记录-handler消息传递机制
-
Android消息机制(3)- Handler和Looper
-
Handler异步消息传递机制(四)Handler发送消息流程,源码(Android 9.0)彻底解析