WeakReference在android中的使用场景

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不可的。

猜你喜欢

转载自blog.csdn.net/firedancer0089/article/details/83023412