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

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

默默地打开bugly。

 

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

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

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

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

复现

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

修复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的方式,预先把蒙版与要显示的图片合成,使得在内存中只保留一份资源。

结果

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

总结

这次从内存高占用入手,解决了由于内存使用量过高导致的内存溢出。等之后遇到内存遗留问题时,再来补下文。

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

本文作者:佚名

来源:51CTO

时间: 2024-10-30 18:23:26

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

android中内容提供者的本质是一个类,那么广播接收器的本质是一个方法吗

问题描述 android中内容提供者的本质是一个类,那么广播接收器的本质是一个方法吗 android中内容提供者的本质是一个类,那么广播接收器的本质是一个方法吗 解决方案 广播接收器,也可以设计成一个类,并不一定是方法. 就算现在的 Android 设计成一个方法,但也并不是说必须,或者只能设计成一个方法.

在android中使用webview加载完一个网页后,如何知道一共加载了多少资源?

问题描述 在android中使用webview加载完一个网页后,如何知道一共加载了多少资源? RT,现在有一个需求要知道用webview加载完任意一个网页后一共有多少个资源,现在问题是不知道什么时候网页完全加载完,因为当webclient回调onPageFinished()之后,还是会继续回调onLoadResource()来加载资源,求给个思路 解决方案 用抓包工具(wireshark)即可查看 解决方案二: 可以在底层抓包实现哦... 解决方案三: 多少资源? 包括多少个图片js 吗 ? 分

位图-在android中如何实现以下效果,即在图片上设置button,并且可以控制button的位置?

问题描述 在android中如何实现以下效果,即在图片上设置button,并且可以控制button的位置? 我是菜菜还没有入门,求大神不吝指教 解决方案 你发的图片我没看到,根据你的描述,可以这样做. 1.在layout里面写布局 2.整个布局用相对布局来实现,因为相对布局对位置的控制比较精细. 3.把这张图片设为背景 4.这样button就可以随意摆放,而且都在图片上方. 解决方案二: 通过布局来实现按钮的位置放置

Android中内存泄露代码优化及检测

  内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况.内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费.         正如下文所说,内存泄漏与许多其他问题有着相似的症状,并且通常情况下只能由那些可以获得程序源代码的程序员才可以分析出来.然而,有不少人习惯于把任何不需要的内存使用的增加描述为内存泄漏,即使严格意义上来说这是不准确的. 一.内存泄露         内存泄漏会因为减少可

Android中Glide获取缓存大小并清除缓存图片

清除Glide缓存 Glide自带清除缓存的功能,分别对应Glide.get(context).clearDiskCache();(清除磁盘缓存)与Glide.get(context).clearMemory();(清除内存缓存)两个方法.其中clearDiskCache()方法必须运行在子线程,clearMemory()方法必须运行在主线程,这是这两个方法所强制要求的,详见源码. 获取Glide缓存空间大小 这个网上也有过一些介绍,但是给出的实现代码存在一些问题,我这里做了一定的修改.一下方法

android 中的Fragment,写了一个下拉框,总是报: The specified child already has a parent.

问题描述 SuppressLint("NewApi")publicclassLoginFragmentextendsFragmentimplementsOnClickListener{EditTextsever_name;EditTextuser_name;EditTexttext_pass;MyHelperhelper;SQLiteDatabasedb;//保存登录过用户账号的下拉框PopupWindowpopView=null;ArrayList<SeverInfor>

Android中ViewPager组件的基本用法及实现图片切换的示例

ViewPager是android-support-v4.jar包里的组件.在布局文件里标签需要连包名一起 写全称<android.support.v4.view.ViewPager /> 基本用法 ViewPager的基本用法我概括为三步 第一步 在主布局文件里放一个ViewPager组件 第二步 为每个页面建立布局文件,把界面写好 第三步 在主Activity里获取ViewPager组件,并为它设定Adapter. Adapter详细讲讲,ViewPager对应的Adapter继承自Pag

环信3.0,Android中,发送方发送一张本地图片,退出再进来,图片路径不正确。

问题描述 我发送一张本地图片后,用imgBody.getLocalUrl()这个方法去看这个图片的路径,是正确的,在sd卡中,显示也正常.等我退出之后,在进来,这个图片就不能正常的显示了,在查看路径就不对了,为null,这个怎么解决? 解决方案 再次进来看看获取到的localurl下有没有对应的文件存在

Android中SparseArray性能优化的使用方法_Android

之前一篇文章研究完横向二级菜单,发现其中使用了SparseArray去替换HashMap的使用.于是乎自己查了一些相关资料,自己同时对性能进行了一些测试.首先先说一下SparseArray的原理.   SparseArray(稀疏数组).他是Android内部特有的api,标准的jdk是没有这个类的.在Android内部用来替代HashMap<Integer,E>这种形式,使用SparseArray更加节省内存空间的使用,SparseArray也是以key和value对数据进行保存的.使用的时