一,前期基础知识储备
关于内存泄漏一些基础点可以参考笔者之前的文章《Android中内存泄漏超级精炼详解》
补充一些说明:《LeakCanary:检测所有的内存泄露》
我们曾看错了方向
bitmap的大小并不是问题,当内存将要用完的时候,OOM可以随处发生。
它们更多的发生在你创建一个像Bitmap这种比较大的对象的情况下。OOM只是一个症状源自于更深层次的问题:内存泄露。
什么是内存溢出?
某些对象生命周期有限,当它们的工作完成以后,将会被回收。如果一个对象在其生命周期结束以后仍被内存中引用,将会导致内存泄露。当泄露积累过多,该应用将耗尽内存。
比如,在Activity.OnDestroy()方法被调用以后,这个activity的各种层级视图和它们关联的位图都应该被回收掉,如果一个后台运行的线程有该activity的引用,activity相对应的内存将不能被回收,这最终会导致OutOfMemoryError崩溃。
二,上代码,具体实现
PS:这是笔者在自己实际项目中遇到的内存泄漏问题,最后用LeakCanary定位并且解决。
1)添加依赖,配置LeakCanary;
项目build.gradle中添加:
debugApi'com.squareup.leakcanary:leakcanary-android:1.6.2'
项目的Application下的onCreate()方法中添加以下代码,配置LeakCanary:
LeakCanary.install(this);
2)运行项目,报错信息如下:
① 操作为:从选图页进入到图片编辑页,然后点退出,从编辑页回到选图页,此时得到如图的信息;
② 接着logcat 显示开始分析并显示分析过程。分析完成之后,打印出 leak trace 并在通知栏显示通知。分析过程较长,大约10s左右。手机通知栏的信息如下:
:
③ 回到手机桌面,可以看到应用图标旁边的信息如下:
④ 点开leaks,查看堆栈显示的信息,如下:
注意,看leaks的堆栈,要从下往上看,最下面的是调用栈,然后开始找自己的代码信息,一般是上下文相关的信息,如上图,笔者最终定位到BillingManager这个类里面的一个mActivity的上下文没有释放掉。BillingManager持有Activity的上下文,当Activity回收掉时,BillingManager没有释放,仍然持有实例,所以导致内存泄漏。我们开始加上释放Activity上下文的代码。
public void destroy() {
Log.d(TAG, "Destroying the manager.");
if (mActivity != null) {
mActivity = null;
}
if (mBillingUpdatesListener != null) {
mBillingUpdatesListener = null;
}
if (mBillingClient != null && mBillingClient.isReady()) {
mBillingClient.endConnection();
mBillingClient = null;
}
}
注意,因为此时已经将mActivity释放掉了,所以之后要调用mActivity都要进行判空处理,比如:
if (mActivity != null) {
int responseCOde = mBillingClient.launchBillingFlow(mActivity, purchaseParams);
Log.d(TAG, "run: responseCOde,,," + responseCOde);
if(responseCOde != 0){
//付费失败,发送广播
}
}
至此,一次内存泄漏就已经处理完毕。
另外,需要注意的是,正式发包时,需要注释掉LeakCanary的库,不能打进正式包里。
PS:当然,忘记注释的话,release的包也无法正常打出,这是该库自己的预防处理方式。
最后给出该库的地址:https://github.com/square/leakcanary squar出品,必属精品。
![](https://i-blog.csdnimg.cn/blog_migrate/719fc3b4dfb40b0034e96c073975b263.jpeg)
目前,仍未有好消息,大家在外都注意安全。愿平安!