Android中内存优化的那些事 - 一个有关图片的优化记录

客服群里叫喊着:这个用户图片不显示了,那个用户图片也不显示了。我拿着手上一切正常的测试机,what the hell……html

默默地打开bugly。java

满园春色关不住,遍地内存溢出来!是的,又闯祸了!android

内存问题永远是既陌生又熟悉的话题,并且大多数都发生在一个叫做用户家的手机上。安卓系统自己不断的在优化,三方框架也逐渐成熟,外加手机厂商的大内存加持,彷佛内存问题变得少见,但仍是不能忽视。数组

借着此次修复内存问题的记录,分享一些“自觉得”的解决思路,仅供参考。ok,let’s go!网络

修复问题的三部曲,先复现,再定位,最后修复。框架

复现

估计有的人会说,异常现象都在那,有啥好复现的,冲进代码直接开干。eclipse

修复bug永远是个惊心动魄的事,稍微一不当心就有可能天崩地裂。不是修复不彻底,就是引入新问题。从原由开始了解整个原因,一方面能加深对问题的理解,同时确保最终能验证问题是否获得修复。工具

内存的问题常常发生在一些比较特殊的环境下,并且不少时候不必定是必现,每每体如今一些中低端机型上。因此从机型上入手可能会是一个不错的选择。测试

最终,经过bugly查到了对应的问题机型及系统版本,上各种云测平台找到了台云测试机。按照进入问题页面的几个固定流程,反复执行,最终锁定了复现流程。字体

定位

知道问题如何复现,接下来就是定位问题到底出在哪。一般内存的问题,会碰到两种状况:

  1. 内存堆积:因为特殊状况形成的页面关闭但资源还遗漏在内存中。
  2. 内存高占用:因为业务须要或者使用不当致使内存占用量太高。

咱们先来看看此次的问题属于哪一种状况。

在Android Studio2.3及以前版本上自带的Android monitor中,能够直观的反应出当前应用的总体内存使用水平。[如何使用工具的分享估计你们都看腻了,此次就再也不重复了。]

142MB!!!!进入事故现场以前就已经被占用了这么多内存。难怪以后会内存异常。看来此次要先解决内存高占用的问题,咱们先要详细的了解内存的具体状况,才知道从哪下手去解决,不管是避免无心义的使用或者优化必要的占用。

先强制gc一下,而后dump java heap,看一下总体内存里的状况,按照shallow size排序。

首当其冲的byte数组映入眼帘,你们都明白的,bitmap一直都是大客户。咱们接着分析下byte[]中的各个对象。

从数据上看,有不少大小相同的内存使用,从理论上看应该是有不少尺寸相同的图片。可为何会有这么多呢?是相同的图片重复了?or other?

所谓耳听为虚眼见为实,若是能看到这些图片长什么样,是否就容易作出对应的判断了?来,开始行动:
来自Gracker的Android内存优化之三:打开MAT中的Bitmap原图 | Performance

感谢Gracker的分享,Get到一个新技能。具体流程参见传送门。主体思路就是经过MAT将对应的byte数组另存为图片原始文件,再用对应的工具打开预览便可。不过我记得之前Android Studio是能够直接看的,可如今不知道跑哪了。

步骤一:

由于Android Studio dump出来的文件mat是没法直接打开的,因此须要作一次转换。在Captures中找到刚刚dump出来的prof文件。右键 -> Export to standar .hprof 便可。

步骤二:

经过MAT Eclipse Memory Analyzer Open Source Project 打开。

步骤三:

右键想要查看的对象 -> Copy -> Save Value To File。保存为xxx.data。他推荐使用Gracker分享中的gimp。Photoshop不肯定是否是我使用方式有问题,在验证的时候一直没法正常显示。

步骤四:

查看对应图片的相关属性,主体是要宽高,由于上一步中保存的是图片的原始格式文件,其中不包含对应的参数信息,因此在导入gimp中须要指定对应的参数。

步骤五:

打开gimp GIMP - Downloads. 而后打开刚刚导出的问题。图像类型根据实际的来,通常都是8888或者565,选择RGB Alpha或者RGB565。而后宽度与高度填写刚刚查询到的参数。最后点击open就能看到实际的图片。

经过这个方式,能够直观的查看到内存中图片的实际状况。而后咱们就能够进一步分析产生问题的实际缘由。

经过以上方式,定位到了3个问题:

  1. 有大量图片资源占用,首页确实有好多图。
  2. 有暂未使用到的图片资源占用(gone状态)。
  3. 有大量蒙版图片占用,由于设计师要求的效果。

解决 - 大量图片占用

对于大量图片占用的问题,其实从如下几个个方向来看思考问题。

  1. 从效果设计的角度来避免,尽量的少使用满屏图片的方式来处理需求。但这方面我我的主张尊重设计师,专业的事情交给专业的人去处理。
  2. 图片资源自己,在知足效果的前提下,尽量的选用RGB565,也许少许图片不明显,但在量大的状况下,节省的内存资源仍是很客观。
  3. 图片资源在不使用的时候及时释放。

结合以上方向来看下咱们遇到的问题。设计角度目前没法调整,原因都是泪,这里就很少说了。资源自己已是RGB565。图片的释放应该是fresco的强项,可从现象上看彷佛并无。看来问题可能出在这,回ui页面上瞄一眼,明白了。

viewpager + fragment + recyclerview,至关于大量图片都属于使用状态,因此fresco不会去释放对应的资源。

临时解决方案:
为了确保核心逻辑的顺利,经过RxBus的方式,在进入和退出核心页面时发送Event事件,而后在大量使用图片的页面注册接收此系列事件,遍历全部SimpleDraweeView,调用其Controller的onDetach或onAttach来,从而实现图片资源引用的临时释放和加载恢复。

为何是临时解决方案,由于我总以为是一种取巧的方式,理论上看。是不该该直接调用方法来插手fresco的管理流程。因此此处留坑,以后再次深刻了解fresco的原理后再回填,也但愿你们提些建议或者意见。

解决 - 暂未使用到的图片资源占用

每一个页面中,都有处理网络异常及相关数据加载异常的提示。原先的处理方式是经过include统一导入后隐藏,在遇到异常的时候才显示出来。问题就出在这,这些异常提示自己是小几率触发,但经过include标签导入的话,会直接实例化完成,占用内存资源。

临时解决方案:
改用ViewStub标签,实现按需加载。

为何又是临时解决方案呢,由于有些机型在黑屏状态下是切断wifi的,当从新进入应用的时候都会通过一个联网的过程,因此会先触发联网异常,ViewStub只能加载一次,加载完后就占用内存了。

解决 - 蒙版图片

以前为了在图片上显示文字但又不想被图案所影响,因此在上面加一层阴影蒙版来保证字体的显示效果。习惯用fresco:overlayImage的方法来实现。但这种实现方式会形成蒙版自己是一个独立的内存资源。

解决方法:
尝试经过Processor的方式,预先把蒙版与要显示的图片合成,使得在内存中只保留一份资源。

结果

经过以上优化方式,一样的机型再次检测,内存占用下来了....

总结

此次从内存高占用入手,解决了因为内存使用量太高致使的内存溢出。等以后遇到内存遗留问题时,再来补下文。

内存问题的排查与解决算是一个老生常谈的话题,由于适配等等状况每每又是一个比较棘手的问题。开发的时候很难发现,因此建议一个需求完成后都例行的检查下内存情况,看下是否有问题后者须要调整的部分。