本文共 4709 字,大约阅读时间需要 15 分钟。
public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { // ... } };}
在实际编写中,我们往往会得到如下警告:
In Android, Handler classes should be static or leaks might occur.
//ListAdapter代码中有如下代码:private val handler = object : Handler() { override fun handleMessage(msg: Message) { if (msg.what == COUNT_DOWN) { notifyItemChanged(msg.arg1, COUNT_DOWN_TIME) } }}
编译后的 java 代码:
private finalhandler;this.handler = new Handler() { public void handleMessage(@NotNull Message msg) { Intrinsics.checkNotNullParameter(msg, "msg"); if (msg.what == 1) { SecondHandCarOrderListAdapter.this.notifyItemChanged(msg.arg1, "countDownTime"); } } };
一句话:
Activity
走完onDestory
之后应当被销毁但是实际并没有,handler
持有 该Activity
的引用导致它无法被销毁
Java
虚拟机中使用可达性分析的算法来决定对象是否可以被回收。即通过GCRoot
对象为起始点,向下搜索走过的路径(引用链),如果发现某个对象或者对象组为不可达状态,则将其进行回收。
而内存泄漏
指的就是有些对象(短周期对象)没有用了,但是却被其他有用的类(长周期对象)所引用,从而导致无用对象占据了内存空间,形成内存泄漏。
所以 Handler 内存泄漏
的问题只说 内部类持有了外部类的引用
是不完整的,没有指出内部类被谁所引用,那么按道理来说是不会发生内存泄漏的,因为内部类和外部类都是无用对象了,是可以被正常回收的。
所以为什么内部类 handler
无法被回收呢?以至于导致它持有的 Activity
也无法正常被回收
简单来说,HandlerActivity
发生了内存泄漏,从引用路径来看,是被匿名内部类的实例 mHandler
持有引用了,而 Handler
的引用是被 Message
持有了,Message
引用是被 MessageQueue
持有了…
//引用路径大致酱紫主线程 —> threadlocal —> Looper —> MessageQueue —> Message —> Handler —> Activity
当Android应用程序启动时,framework会为该应用程序的主线程创建一个Looper
对象。这个 Looper
对象包含一个简单的消息队列 Message Queue
, 并且能够循环的处理队列中的消息。这些消息包括大多数应用程序framework事件,例如Activity生命周期方法调用、button点击等,这些消息都会被添加到消息队列中并被逐个处理。主线程的 Looper
对象会伴随该应用程序的整个生命周期
然后,当主线程里,实例化一个 Handler
对象后,它就会自动与主线程Looper
的消息队列关联起来。所有发送到消息队列的消息 Message
都会拥有一个对 Handler
的引用,所以当 Looper
来处理消息时,会据此回调 Handler#handleMessage(Message)
在 java
里,非静态内部类
和 匿名类
都会潜在的引用它们所属的外部类。但是,静态内部类却不会
这是因为内部类虽然和外部类写在同一个文件中,但是编译后还是会生成不同的class文件,其中内部类的构造函数中会传入外部类的实例,然后就可以通过 this$0
访问外部类的成员
eg:
//原代码class InnerClassOutClass{ class InnerUser { private int age = 20; }}//class代码class InnerClassOutClass$InnerUser { private int age; InnerClassOutClass$InnerUser(InnerClassOutClass var1) { this.this$0 = var1; this.age = 20; }}
在这处理一下 1.2
中存在的内存泄漏问题
这是因为在kotlin中的匿名内部类分为两种情况:
同样 kotlin
中对于内部类也是和 Java
有区别的:
Kotlin
中所有的内部类都是默认静态的,也就都是静态内部类。inner
修饰,改成和Java一样的内部类,并且会持有外部类的引用,需要考虑内存泄漏问题。 inner
关键字就和 Java 内部类一样用法了public class SampleActivity extends Activity { /** * Instances of static inner classes do not hold an implicit * reference to their outer class. */ private static class MyHandler extends Handler { private final WeakReferencemActivity; public MyHandler(SampleActivity activity) { mActivity = new WeakReference (activity); } @Override public void handleMessage(Message msg) { SampleActivity activity = mActivity.get(); if (activity != null) { // ... } } } private final MyHandler mHandler = new MyHandler(this); /** * Instances of anonymous classes do not hold an implicit * reference to their outer class when they are "static". */ private static final Runnable sRunnable = new Runnable() { @Override public void run() { /* ... */ } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Post a message and delay its execution for 10 minutes. mHandler.postDelayed(sRunnable, 1000 * 60 * 10); // Go back to the previous Activity. finish(); }}
private val handler = MyHandler(WeakReference(this)) class MyHandler(var adapter: WeakReference):Handler(){ override fun handleMessage(msg: Message) { super.handleMessage(msg) if (msg.what == COUNT_DOWN) { adapter.get()?.notifyItemChanged(msg.arg1, COUNT_DOWN_TIME) } } }fun destroy() { handler?.removeCallbacksAndMessages(null) timer?.cancel() timer?.purge() timer = null task = null }
转载地址:http://atpgi.baihongyu.com/