问题
昨天一老哥非说我负责的模块内存泄漏,导致anr了。
image.png
这个view看起来是有点恐怖呀,可是这个模块虽说是我负责的,but不是我写的呀。
过程
不过没关系,不是有个很强大的LeakCanary吗,上次就是借助这个工具发现了三处泄漏,而且这个导入非常简单,在build.gradle导入依赖就好了。
我就按照教程
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
}
。。。
sync一下
。。。
竟然成功了,本来想记录一次失败的操作(前面添加官网最新的失败了好几次),然后秀一下技术,表示我不用这个插件也能找到哪里泄漏的。进入成功了,不用白不用,那这个就改成 《手把手教你使用leakcanary找到内存泄漏》吧。
接下来
在这个应用的Application添加上
LeakCanary.install(this);
这句。
接下来
。。。
安装
。。。
image.png
接下来
。。。
在你的应用随便点点,多操作几次
。。。
操作一通后,没有发现有提醒内存泄漏
但是dumpsys activity activities之后发现了一些问题
这个应用所处的任务栈有有很多activity是一样的,难道是因为这个导致没有释放掉这个些view?
用mat抓出来看下。
具体操作可以看下我的这篇笔记。
文档:内存泄漏分析工具.note
链接:http://note.youdao.com/noteshare?id=05cb734880cb162ded31f429d9f215ae&sub=57AB6263DEC3423DBBB7CA4777408B13
image.png
dump出来确实看到有很多settings 实例。
在研究中,找到原因再更新
-------------------------------------两个小时后------------------------------------------------------------------------
hhhhhh我又回来了。
原因是这样:
我们的应用有两个入口进入,一个是launcher进入,一个是从systemUI进入。Launcher进入就是原生的操作,startActivity
String packageName = componentName.getPackageName();
String className = componentName.getClassName();
Intent intent = new Intent(Intent.ACTION_DELETE, Uri.fromParts(
"package", packageName, className));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
return true;
从systemUI进入的就是我们自己写的。
Intent intent = new Intent(action);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(CommonActionDefine.KEY_ACTION_FROM,from);
context.startActivity(intent);
启动都是FLAG_ACTIVITY_NEW_TASK,跟踪源码会发现。
如果从Launcher进入,那么任务栈里进入一个activity,然后在下拉通知栏从SystemUI进入,Activity作为SingleTask,那么系统就会去找当前是否有这个栈,对比栈
07-27 17:18:27.404 D/ActivityManager( 607): Looking for task of ActivityRecord{2c763290 u0 com.XXX.setting/.SettingActivity t-1}
07-27 17:18:27.404 D/ActivityManager( 607): Looking for task of ActivityRecord{2c763290 u0 com.XXX.setting/.SettingActivity t-1} in ActivityStack{1c759c1e stackId=1, 10 tasks}
07-27 17:18:27.404 D/ActivityManager( 607): Comparing existing cls=com.XXX.setting/.SettingActivity/aff=com.XXX.setting to new cls=com.XXX.setting/.SettingActivity/aff=com.XXX.setting
07-27 17:18:27.404 D/ActivityManager( 607): Found matching affinity!
07-27 17:18:27.404 D/ActivityManager( 607): Bring to front target: ActivityStack{1c759c1e stackId=1, 10 tasks} from ActivityRecord{17578b5c u0 com.XXX.setting/.status.StatusSettingActivity t75}
07-27 17:18:27.404 I/am_home_stack_moved( 607): [0,0,1,1,intentActivityFound]
找到之后直接将这个Activity移到前台。
看样子我们的应该是这样,不需要重新启动,直接将之前的应用给移到前台即可。
但是dumpsys activity activities之后发现,这个任务栈有两个一样的hist,除了一个是Resume状态一个是Stop状态之前,调用它启动的应用也不一样之外,其他一样,那为什么他不找之前那个应用呢。
(这部分源码在ActivityStack里,这里偷个懒就不拿出来了)
结果就是ActivityManager除了找亲和力一致之外,还要对比Intent是否一致,一致的话才是找对了,不能你叫彭于晏,你就是彭于晏吧。intent对比的内容是这样
public boolean filterEquals(Intent other) {
if (other == null) {
return false;
}
if (!Objects.equals(this.mAction, other.mAction)) return false;
if (!Objects.equals(this.mData, other.mData)) return false;
if (!Objects.equals(this.mType, other.mType)) return false;
if (!Objects.equals(this.mPackage, other.mPackage)) return false;
if (!Objects.equals(this.mComponent, other.mComponent)) return false;
if (!Objects.equals(this.mCategories, other.mCategories)) return false;
return true;
}
只要这里有一个不一样,就不是彭于晏哈哈。
所以我这里又启动了一个Activity,那么重复这样的操作,名义上一个Activity后台上存在N多个,所以View才多,所以周末这样的好日子我在这里被逼着改内存泄漏问题。
解决
那么怎么解决这个问题呢?把启动这个应用的Intent要求的都整一样?不行,我这边有个需求就是需要区分两个入口。就是settings要知道是launcher启动的还是systemUI启动的,而且这边不止这一个activity。
如果每次从systemUI启动之前能把这个栈的Hist给清一清就好了,
清?
有啊
FLAG_ACTIVITY_CLEAR_TASK
这个不就好了!!!
加上这个之后,每次启动systemUI的settings时,都会把栈清理干净,哈哈哈。没毛病
改完dump一下
ok,下班!!!!!!!!!!!!











网友评论