android内存泄漏记录
android内存泄漏点记录
常见的四种情况
1. 将context或者view置为static(view会默认持有一个context的引用,置为static的话会造成view在方法区中无法快速被回收,从而导致activity的泄漏)
public class TestActivity extends AppCompatActivity {
private static ImageView imageView;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
imageView = findViewById(R.id.test_img);
imageView.setImageResource(R.drawable.ic_launcher_background);
}
}
上述的代码中的imageView会导致TestActivity无法被GC回收
2. 常见的未解注册的各种listener(如广播,Observer的监听等)
3. 非静态Handler导致泄漏
//------------------------注释1-------------------------------
private Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
//------------------------注释2-------------------------------
private static class MyHandler extends Handler {
private final WeakReference<TestActivity> mActivity;
public MyHandler(TestActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
TestActivity testActivity = mActivity.get();
if (testActivity != null) {
// do something
}
}
}
上述代码中注释1是日常开发中写的样子也会造成Activity的内存泄漏,一般需要将其置为static,然后内部持有一个Activity的弱引用来避免内存泄漏。如注释2所示。
4.三方库使用的Context
三方库初始化的时候一般使用applicationContext作为上下文参数,一般如果我们自己开发相关的SDK的时候一般用context.getApplicationContext() 的方法避免内存泄漏
LeakCanary的使用及原理分析
原理:
LeakCanary 中对内存泄漏检测的核心原理就是基于 WeakReference 和 ReferenceQueue 实现的。
-
当一个 Activity 需要被回收时,就将其包装到一个 WeakReference 中,并且在 WeakReference 的构造器中传入自定义的 ReferenceQueue。
-
然后给包装后的 WeakReference 做一个标记 Key,并且在一个强引用 Set 中添加相应的 Key 记录
-
最后主动触发 GC,遍历自定义 ReferenceQueue 中所有的记录,并根据获取的 Reference 对象将 Set 中的记录也删除
-
经过上面 3 步之后,还保留在 Set 中的就是:应当被 GC 回收,但是实际还保留在内存中的对象,也就是发生泄漏了的对象。
主要触发
LeakCanary 中监听 Activity 生命周期是由 ActivityRefWatch 来负责的,主要是通过注册 Android 系统提供的 ActivityLifecycleCallbacks,来监听 Activity 的生命周期方法的调用
new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(@NonNull Activity activity) {
}
@Override
public void onActivityResumed(@NonNull Activity activity) {
}
@Override
public void onActivityPaused(@NonNull Activity activity) {
}
@Override
public void onActivityStopped(@NonNull Activity activity) {
}
@Override
public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
}
@Override
public void onActivityDestroyed(@NonNull Activity activity) {
}
};
当监听到 Activity 的 onDestroy 方法后,会将其传给 RefWatcher 的 watch 方法
核心类 RefWatcher
-
在其watch()方法中,主要操作
- 生成一个随机的字符串 key,key 就是用来标识 WeakReference 的,就相当于给 WeakReference 打了个标签
- 将被检测对象包装到一个 WeakReference 中,并将其标识为步骤 1 中生成 key
- 调用 ensureGoneAsync 开始执行检测操作
-
ensureGoneAsync 方法操作,其中实现了内存泄漏的检测
-
遍历 ReferenceQueue 中所有的元素,并根据每个元素中的 key,相应的将集合 retainedKeys 中的元素也删除
-
判断集合 retainedKeys 是否还包含被检测对象的弱引用,包含说明被检测对象并没有被回收,发生了内存泄漏
-
生成 Heap “堆”信息,并生成内存泄漏的分析报告
-
检测时机 为了尽量不影响 UI 线程的渲染,LeakCanary 也做了些优化操作
向主线程 MessageQueue 中插入了一个 IdleHandler,IdleHandler 只会在主线程空闲时才会被 Looper 从队列中取出并执行。通过 addIdleHandler 也经常用来做 App 的启动优化,比如在 Application 的 onCreate 方法中经常做 3 方库的初始化工作。可以将优先级较低、暂时使用不到的 3 方库的初始化操作放到 IdleHandler 中,从而加快 Application 的启动过程
//getMainLooper().myQueue()或者Looper.myQueue()
Looper.myQueue().addIdleHandler(new IdleHandler() {
@Override
public boolean queueIdle() {
//你要处理的事情
return false;
}
});
上一篇: Android 文件IO流的存储和读取
下一篇: Android自定义控件学习笔记1