weakreference
Android中绝大部分内存泄漏都是context导致的,应为context的传递实在是太多了。
基本原理就一条,如果class B持有class A,而class B的生命周期比class A长,A要销毁但是因为B的引用而无法销毁那么leak就发生了。
实际代码中,class B一般是异步代码(执行时间长且不确定)或服务(一般和UI无关),class A是Context持有者或者就是Context子类。那么B持有A Context引用的方式是weakreference的话就能避免leak发生。
具体到android中有如下一些场景:
handler子类
handler造成的泄漏这个目前是android面试笔试必做题了,如果写一个handler的子类,又要传递个Context进去,那么应该用WeakReference
异步代码
Rxjava,Thread,AsyncTask,ThreadPoolExecutor等各种异步代码,如果持有context的话可能导致leak
static变量
static成员初始化需要context,那么要使用WeakReference,而且WeakReference.get()==null的话要重新初始化,使用static成员其实就是单例模式(代码比static稍多)或者成员注入(上一级Object成员,然后Object其他成员可以共享,但这不是单例模式)的偷懒写法,有的公司代码规范是不让用static和单例的。
单例模式
同static,其实一般传递APPlicationContext就ok,如有特殊情况就只能用WeakReferece了。
各种addListener的防御性写法
addXXListener在android中很常见,一般会有对应的removeXXListener。
但是不能保证removeXXListener一定会被使用的,但是很多人发现好像不写removeXXListener也没报啥异常啊,leakcanary也没有报leak。
那么有部分概率的可能是保存XXListener的容器类是使用了WeakReference
预防interface导致的内存泄漏
android中经常见到类之间用interface沟通的例子,举一个例子
public class A extends Activity implements View.OnClickListener{
...
View B;
...
B.setOnClickListener(this);
...
@Override
public void onClick(View v) {
...
}
...
}
A和B是交叉引用的,交叉应用也不一定非引起内存泄漏,例如大家应该很少有人会写
@Override
protected void onDestroy() {
...
B.setOnClickListener(null);
}
这是因为B的生命周期不会超出A的生命周期,如果B的生命周期超出A的周期,那么leak就发生了
那么B中保存interface实现的成员应该用WeakReference, 这个原理和上小节是一样的
注意传递this是个不大好的写法,特别是一个class实现一堆interface(后续用class A表示)的状况,对保存interface的class(后续用class B表示)来说,其实B只需要对应interface的实现,传递this会使B持有A的引用,例如B只需要A实现一个弹出toast的功能,却会多余的间接持有A其他成员的引用。例如A中有个TextView类型成员,TextView内部是有Context强引用的,进而会持有Activity的引用,那么B实际上会间接持有Activity的引用的,B可能会导致leak,而B实际上根本不需要A中context的引用
内部类
内部类是有外部类的引用的,特例是内部类有static修饰的话,可以排除这个引用,当然代码中把Context传进去,static实际上也就失去了作用。
lambda
lambda有两种情况,如果lambda是纯粹的功能性代码,例如打印个日志之类的,没有使用类中的其他成员,那么无需担心leak的问题。如果有使用其他成员,那么lambda就等同于一个匿名内部类实例,是持有外部类引用的。
例如用static Handler post一个Runnable的lambda,static Handler是没有外部类引用的,但是lambda可能有啊...
当然网上还看到过一个奇葩的情况,就是使用WeakReference保存纯代码lambda,结果反而提升了lambda的生命周期,具体见
https://zhuanlan.zhihu.com/p/20662522
BTW:
以上列举的情况大概率是不用任何WeakReference就可以解决的
1.Handler在退出的时候例如OnDestory使用removeCallbacksAndmessages可以取消所有message和Runnable
2.异步代码完成工作可以通过往主线程Handler发消息的方式进行,完成时主线程已经销毁那么就不用做后续工作了,而且rxjava是可以取消异步工作的,具体可见Disposable的用法
3.static和单例模式一般用ApplicationContext是足够的,如果非要用Activity和Fragment的Context,那么我觉的是设计出了问题,它应该作为是Activity和Fragment的成员,而不是单例或者static
3.addListener和removeListener的配对使用应该是常识啊,而不是通过WeakReference来规避Leak
4.避免一个class implements一堆interface的写法,避免引用的到处传递和扩大,这个设计和重构是能解决的
5.内部类和lAMDa使用要注意生命周期,如果是Handler post Runnable的Lamda,可以用Handler取消。不用通过Lamda来传递异步完成执行的代码,异步完成发Message通知即可,android framework源码中大量的见这种写法
其实只有很少的情况是非用WeakReference不可的。
相关阅读
Handler还需要用到弱引用(WeakReference)吗?
转自:https://blog.csdn.net/alcoholdi/article/details/54948058 写的很好网上很多文章都说写Hanlder,需要用static声明为静态的