记录一次使用 Android Profiler 分析内存

版权声明:本文章原创于 RamboPan ,未经允许,请勿转载。

记录一次使用 Android Profiler 分析内存

最近手里开发了一个小应用,功能也不复杂,大致功能是:

  • 加载本地的视频及生成缩略图,用户选择一个点击按钮进行观看,再点击一次结束观看。

  • 记录时间差,然后跳转一个新界面,把时间差动态加载出来。

  • 点击按钮主动 或者 倒数30秒 后返回主菜单。

也不难,贴一个做完了的效果。

在这里插入图片描述

虽然这个应用不大,我们也需要严谨是吧,也分析下代码中有没有导致内存泄露的情况。
这次做个笔记。

这里使用 Android Profiler 进行分析,分析内存中的对象是否因为不合理的写法,在页面关闭后未能被回收。

先说说这个小应用,主要分为 3 个页面:配置页面播放页面结果页面

在这里插入图片描述

主要是把 assets 目录中的本地文件复制到需要使用的设备上。完成第一次使用配置。

做这种给他人使用的应用,多加一些操作说明方便他人,也给自己节省时间(别人询问的时间)。

加载视频以及生成缩略图,这里用了 LruCache 做了一层内存缓存(用户切换视频时加载更快),也生成了对应图片文件做了一层硬盘缓存(下一次进入时直接读取本地文件)。

在这里插入图片描述

这个界面就一个倒计时与动态显示秒数,因为并没有要求特别复杂的动态显示,直接采用间隔时间修改 TextView ,做了一个简单的任务定时修改 TextView 。返回可以通过点击按钮返回,也可以是倒计时到时间返回。


我们开始通过 Android Profiler 启动这个应用。

然后点击 Memory 这部分。
在这里插入图片描述
先看第一个界面的内存分析。
在这里插入图片描述

因为我点击复制文件之后,复制了几个较大的视频文件。从波浪的图形中能够看出内存不断使用。

每复制完成一个文件后,占用内存变小再进行下一次增大,结束最后一次复制后和刚开始时内存占用差不多。

这里看着没有问题,我们直接进入第二个界面分析。

才进入的时候,因为需要生成缩略图文件以及内存缓存,所以内存占用迅速增加。

这里使用 Dunp Java Heap 按钮,获取堆信息。

在这里插入图片描述

能看到有 PlayActivityPlayActivity$ ,这里 PlayActivity $ 表明 PlayActivity 的某个内部对象正在引用外部的 PlayActivity

从右边也能看出,有些 view 正在加载图片,这次是才进入播放界面采集的堆信息,有图片正在生成与加载肯定是正常的。

先说明下我们需要观察的几个参数,见下图:

在这里插入图片描述

- Allocations : 代表总的实例数量。
- Shallow Size : 代表这些实例自身所占的大小。
- Retained Size : 代表这些实例及其引用所占的所有大小。

现在对比下上面的图,我们只进入了一次 ResultActivity ,所以对应 PlayActivityPlayActivity$Allocations 都是 1 。

后面我们会测试几次进入,可以再观察这个值。

我们接着刚才的说,耐心等上一小会,我们手动 GC 一次进行清理,再获取堆信息。

一般应用在 GC 之后能发现明显的内存减少。

在这里插入图片描述

这次能看到因为图片已经加载完成,所以只留下了一个 PlayActivity 实例。这里看也是没有什么问题的。

(6 个 FrameImageView 是因为主界面有 6 个场景选择的 View。)

现在我们点击观看视频,等待一段时间后,结束观看,此处开始跳转结果页面。

在这里插入图片描述

第一个上升趋势是点击按钮后视频开始播放,第二个上升趋势是跳转了结果页面。

然后我们点击结果页面的返回按钮。再过一小段时间,我们点击 GC 按钮,再取一次堆信息。

在这里插入图片描述

第一个下降趋势是点击返回时,部分内存直接收回。第二个下降趋势是点击 GC 后再次释放的内存,

从图里能看见,除了 ResultActivity 没有被回收,还有一个 ResultActivity $ 1,我们点击之后能看到有一个 mOnClickListener in View$Listener ,那么这个代码对应在哪呢 ?

对应在屏幕中央的返回按钮,我使用了一个匿名的 OnClickListener


	btnBack.setOnClickListener(new View.OnClickListener() {
	    @Override
	    public void onClick(View v) {
	        finish();
	    }
	});
        

看了 ResultActivity$1 ,我们再来看 ResultActivity 这个实例,点开之后发现都是资源有关的引用。

在这里插入图片描述

从这来看,也没有什么大的问题,回收肯定会回收,只是时间会等得更久一点。

稍等一会再回来,再次点击 GC 按钮,只剩 PlayActivity,也符合之前的判断。

在这里插入图片描述

这次测试是从结果页面返回播放界面,等待了一会之后点击 GC 再进行堆信息获取。


那么如果点击返回之后立刻获取堆信息会怎么样 ? 现在来试一试。

在这里插入图片描述

怎么突然多了这么多 ResultActivity$ !!!

既然出现了这么多,肯定是有原因的。点开 ResultActivity$2 发现是倒数计时的 View 以及不断倒计时的 String

因为需要不断更新 TextView 的文字,肯定需要一个定时的任务或者线程来执行。我这是把 TextView 和线程封装了一下。

因为之前已经想到了可能会提前结束,所以在 onDestroy() 方法中加入了对任务的停止,
当然放在点击按钮时处理也是可以的。


	@Override
	protected void onDestroy() {
	    if (mCountDownTask != null) mCountDownTask.stopTask();
	        
	    super.onDestroy();
	}
	    

所以此处产生了很多 String 对象,也算正常。再点开 ResultActivity$3

在这里插入图片描述

能看到有个 val$alpha ,还有刚刚的 View ,因为这个 View 结束时会闪动一下,我这是通过修改透明度实现的,所以此处存在很多透明度值也是正常的。

还有个 ResultActivity$1 和之前是一样的,那个匿名 OnClickListener

在这里插入图片描述

此时我们再点击 GC 发现因为倒计时 View 产生的多个对 ResultActivity 的引用已经不在了。

在这里插入图片描述

剩下两个 ResultActivity$ 仍然是之前分析的匿名 OnClickListenerResultactivity 中的一些资源引用。

再次等待一会后 GC ,能看到 ResultActivity 相关的都已经回收了。

在这里插入图片描述


我们测试了单次(观看视频后跳转结果再退回主界面) ,现在我们来多测试几次,再获取一次堆信息。

在这里插入图片描述

能看到 ResultAcitivity 相关的都多了很多,至少都是 4+,点开其中一个发现都是重复的,说明确实是因为几次使用增加了相关的对象,然后我们耐心等待后再 GC 一次,发现 ResultActivity 还剩一个,后面就没再截图了。

在这里插入图片描述

从这些分析暂时看出代码中没有内存泄露的迹象。

如果有不妥之处或不同意见,烦请留言,互相提高。


Android Profiler 参考资料:

https://developer.android.com/studio/profile/android-profiler?hl=zh-cn

https://www.youtube.com/watch?v=LGVbpobV-Yg)=

https://www.youtube.com/watch?v=O5V9ZSL0BsM&t=614s

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章