文章目录
- 一、原因
- 二、可能造成内存泄漏
- 三、解决方法
- 四、内部类为什么会持有外部类的引用
- 五、Runable 的内存泄漏解决方案
一、原因
Handler造成内存泄露的原因。非静态内部类,或者匿名内部类。使得Handler默认持有外部类的引用。在Activity销毁时,由于Handler可能有未执行完/正在执行的Message。导致Handler持有Activity的引用。进而导致GC无法回收Activity。
二、可能造成内存泄漏
匿名内部类:
//匿名内部类
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
非静态内部类:
//非静态内部类
protected class AppHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
// TODO: 2019/4/30
}
}
}
三、解决方法
静态内部类+弱引用
private static class MyHandler extends Handler{
private final WeakReference<MineActivity> mMineActivityWeak;
public MyHandler(MineActivity mineActivity){
mMineActivityWeak = new WeakReference<>(mineActivity);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
MineActivity mineActivity = mMineActivityWeak.get();
if(mineActivity != null){
mineActivity.number = 5;
}
}
}
Activity销毁时,清空Handler中,未执行或正在执行的Callback以及Message。
// 清空消息队列,移除对外部类的引用
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
//Handler源码中removeCallbacksAndMessages()注释含义
/**
* 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);
}
四、内部类为什么会持有外部类的引用
这是因为内部类虽然和外部类写在同一个文件中,但是编译后还是会生成不同的class文件,其中内部类的构造函数中会传入外部类的实例,然后就可以通过this$0访问外部类的成员。
其实也挺好理解的吧,因为在内部类中可以调用外部类的方法,变量等等,所以肯定会持有外部类的引用的。
贴一段内部类在编译后用JD-GUI查看的class代码,也许你能更好的理解:
//原代码
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;
}
}
那为什么静态内部类不持有外部类引用呢?你可以自己试试写一个静态内部类,外部再写几个成员变量,你看在静态内部类中能不能调到,调不到就是不持有外部类的引用
使用弱引用的一个常见目的是为了访问外部类的成员变量。
当我们在静态内部类中需要访问外部类的成员变量时,可以使用弱引用来解决。弱引用允许在没有其他强引用指向对象时,对象可以被垃圾回收器回收。
五、Runable 的内存泄漏解决方案
public class MineFragment extends Fragment {
private RecycleView reclcleView;
private static class PositionRunnable implements Runnable {
private final int lastPosition;
private final int lastOffset;
private WeakReference<MineFragment> mineFragmentWeak;
public PositionRunnable(MineFragment mineFragment, int lastPosition, int lastOffset) {
this.mineFragmentWeak = new WeakReference<>(mineFragment);
this.lastPosition = lastPosition;
this.lastOffset = lastOffset;
}
@Overried
public void run() {
mineFragmentWeak.get().reclcleView.scrollToPosition(lastPosition);
}
}
// 用的地方直接
PositionRunnable pRunnable = new PositionRunnable(this, 100, 200);
reclcleView.post(pRunnable);
}