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

更多相关文章

  1. 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
  2. 换一种方式理解 Android协程
  3. Android(安卓)APK签名对比及说明
  4. Android混淆代码proguard,内存溢出
  5. Android小项目之二 代码的组织结构
  6. 刚用andriod studio 的一点心得感受
  7. Android上 用Html5做界面,javascript调用摄像头实例
  8. Android(安卓)APK DEX分包总结
  9. Android目录结构(详解)

随机推荐

  1. android xml解析
  2. 【边做项目边学Android】小白会遇到的问
  3. Android(安卓)blueZ HCI(一个):hciconfig实
  4. Android之Handler用法总结
  5. 【视频课程】Android底层开发关键技术—A
  6. 详解Android中AsyncTask的使用
  7. Android系统启动流程 - 1
  8. Android内核和驱动程序
  9. Activity切换动画无效(android:windowIsT
  10. Android(安卓)隐藏/显示 Actionbar之后不