本文将图片重复分为两种类型:

1.APP运行时加载了多个相同的图片对象,造成了内存浪费

2.APK包中存在多个相同的图片文件,影响了APK包大小

下面分别进行讨论:

---------------------------------------------------------------------------------------------------

一.内存图片查重:

目的:降低运行时内存,防止程序发生OOM异常,以及降低程序由于内存过大被LMK机制杀死的概率。另一方面,不合理的内存使用会使GC大大增多,从而导致程序变卡。

思路:通过分析内存文件hprof快速判断内存中是否存在重复的图片,并且将这些重复图片的PNG、堆栈等信息输出

内存文件hprof快速判断内存中是否存在重复的图片,并且将这些重复图片的PNG、堆栈等信息输出

1.首先是获取我们需要分析的 hprof 文件,我们加载两张相同图片:

Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.mipmap.test);imageView1.setImageBitmap(bitmap1);
imageView2.setImageBitmap(bitmap2);

2.生成 hprof 文件

// 手动触发 GC
Runtime.getRuntime().gc();
System.runFinalization();
Debug.dumpHprofData(file.getAbsolutePath());

3.利用HAHA 库进行文件分析的核心代码:

主要思路是遍历所有的bitmap对象,用HashCode-List<AnalyserResults>,作为key-value,最后如果判断value的list的size大于1,则说明内存中存在重复图片

File heapDumpFile = new File(args[0]);//打开hprof文件HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);HprofParser parser = new HprofParser(buffer);//解析获得快照com.squareup.haha.perflib.Snapshot snapshot = parser.parse();snapshot.computeDominators();//获得Bitmap ClassCollection<ClassObj> bitmapClasses = snapshot.findClasses("android.graphics.Bitmap");//获取堆数据,这里包括项目app、系统、default heap的信息,需要进行过滤Collection<Heap> heaps = snapshot.getHeaps();
//            Tools.print("bitmapClasses size = " + bitmapClasses.size());
//            Tools.print("all heaps size in snapshot = " + heaps.size());//这里有一个坑,其实snapshot也是从每个heap上获取他的ClassObj列表的,但是可能出现这个heap上的//ClassObj对象出现在了另一个heap中的情况,因此我们不能直接获取heap的ClassObj列表,//需要直接从snapshot总获取ClassObj列表.long startTime = System.currentTimeMillis();Tools.print("---------------------- START ----------------------- ");for (Heap heap : heaps) {// 只需要分析app和default heap即可Tools.print("HeapName:" + heap.getName());if (!heap.getName().equals("app") && !heap.getName().equals("default")) {continue;}//  Tools.print("HeapName:" + heap.getName());Map<Integer, List<AnalyzerResult>> map = new HashMap<>();for (ClassObj clazz : bitmapClasses) {//从heap中获得所有的Bitmap实例List<Instance> instances = clazz.getHeapInstances(heap.getId());for (int i = 0; i < instances.size(); i++) {//从GcRoot开始遍历搜索,Integer.MAX_VALUE代表无法被搜索到,说明对象没被引用可以被回收if (instances.get(i).getDistanceToGcRoot() == Integer.MAX_VALUE) {continue;}List<AnalyzerResult> analyzerResults;int curHashCode = Tools.getHashCodeByInstance(instances.get(i));AnalyzerResult result = Tools.getAnalyzerResult(instances.get(i));result.setInstance(instances.get(i));if (map.get(curHashCode) == null){analyzerResults = new ArrayList<>();}else {analyzerResults = map.get(curHashCode);}analyzerResults.add(result);map.put(curHashCode, analyzerResults);}}if (map.isEmpty()){Tools.print("current head has no bitmap object");}for (Map.Entry<Integer, List<AnalyzerResult>> entry : map.entrySet()){List<AnalyzerResult> analyzerResults = entry.getValue();//去除size小于2的,剩余的为重复图片。if (analyzerResults.size() < 2){continue;}Tools.print("============================================================");Tools.print("duplcateCount:" + analyzerResults.size());Tools.print("stacks:[");for (AnalyzerResult result : analyzerResults){Tools.print("   [");Tools.getStackInfo(result.getInstance());Tools.print("   ]");}Tools.print("]");Tools.print(analyzerResults.get(0).toString());Tools.print("============================================================");}}long endTime = System.currentTimeMillis();Tools.print("---------------------- END ----------------------- ");Tools.print("Total cost time:" + (endTime - startTime) + "ms");

最终的输出结果:

$ java -jar DuplicatedBitmapAnalyzer-1.0.jar dump.hprof
---------------------- START -----------------------
HeapName:default
current head has no bitmap object
HeapName:app
============================================================
duplcateCount:2
stacks:[[android.graphics.drawable.BitmapDrawable$BitmapState@315598040 (0x12cfa4d8)android.graphics.drawable.BitmapDrawable@315232112 (0x12ca0f70)android.support.v7.widget.AppCompatImageView@315614208 (0x12cfe400)android.view.View[12]@315579008 (0x12cf5a80)android.widget.LinearLayout@315612160 (0x12cfdc00)android.support.v7.widget.AppCompatButton@315615232 (0x12cfe800)][android.graphics.drawable.BitmapDrawable$BitmapState@315597984 (0x12cfa4a0)android.graphics.drawable.BitmapDrawable@315232040 (0x12ca0f28)android.support.v7.widget.AppCompatImageView@315613184 (0x12cfe000)android.view.View[12]@315579008 (0x12cf5a80)android.widget.LinearLayout@315612160 (0x12cfdc00)android.support.v7.widget.AppCompatButton@315615232 (0x12cfe800)]
]
bufferHash:fac3180b7dd77454bb6c81bc02827945
width:90
height:90
bufferSize:32400
============================================================
---------------------- END -----------------------
Total cost time:49ms

用AS打开Hprof文件,发现drawable对象一样:

-----------------------------------------------------------------------------------------------------

二.APK文件查重(可以包括图片,布局,资源等等)

目的:降低程序占用的空间,防止由于ROM空间不足导致程序无法安装;另外在用到插件化技术时,不注意控制插件包大小,尤其会影响插件加载速度

思路:和上面类似,遍历项目目录下的图片资源,将图片MD5作为Key值,如果存在多个相同文件,则对应的value存储多个,最后输出重复结果。

可以考虑用脚本实现,在代码入库前做强制检查,具体代码就不贴了。

说到文件查重,顺便推荐一下冗余代码查重工具:Simian,重构的时候可以使用,实测效果不错。

最后:

好的架构,不要相信大家的口头约定,应该考虑将能自动化的工作,全部自动化。

只有保证实验室监控和线上监控的持续有效实施,才能够保证项目的线上质量。

Android内存优化之图片查重相关推荐

  1. Android内存优化之图片优化

    关于图片优化,大概如下 为什么要进行图片优化 相信大概刚开始学习Android的时候有过图片过大而直接报错的情况,下面简单介绍一下OOM问题,Android支持的图片格式及图片优化的几种方式 什么是O ...

  2. 【Android 内存优化】图片文件压缩 ( Android 原生 API 提供的图片压缩功能能 | 图片质量压缩 | 图片尺寸压缩 )

    文章目录 一. 图片压缩 二. 图片文件压缩类型 三. Android 原生 API 提供的质量压缩 四. Android 原生 API 提供的尺寸压缩 一. 图片压缩 图片压缩 : ① 文件压缩 : ...

  3. 【Android 内存优化】Android 原生 API 图片压缩原理 ( 哈夫曼编码开关 | 哈夫曼编码原理 | libjpeg-turbo 函数库 )

    文章目录 一. 哈夫曼编码开关 二. 哈夫曼编码原理 三. libjpeg-turbo 函数库 四. libjpeg-turbo 函数库下载 [Android 内存优化]图片文件压缩 ( Androi ...

  4. 【Android 内存优化】Android 原生 API 图片压缩原理 ( Bitmap_compress 方法解析 | Skia 二维图形库 | libjpeg 函数库 | libpng 函数库 )

    文章目录 一. 图片质量压缩方法 二. Skia 二维图形库 三. libjpeg.libpng 函数库引入 在博客 [Android 内存优化]图片文件压缩 ( Android 原生 API 提供的 ...

  5. 【Android 内存优化】Android 原生 API 图片压缩原理 ( 图片质量压缩方法 | 查找 Java 源码中的 native 方法对应的 C++ 源码 )

    文章目录 一. 图片质量压缩方法 二. 查找对应的 Native 方法源码 三. 分析 Bitmap.cpp 中动态注册 Native 方法 在博客 [Android 内存优化]图片文件压缩 ( An ...

  6. 【Android 内存优化】Android 原生 API 图片压缩代码示例 ( PNG 格式压缩 | JPEG 格式压缩 | WEBP 格式压缩 | 动态权限申请 | Android10 存储策略 )

    文章目录 一. 图片质量压缩 二. 图片尺寸压缩 三. Android 10 文件访问 四. 完整源码示例 上一篇博客 [Android 内存优化]图片文件压缩 ( Android 原生 API 提供 ...

  7. 【Android 内存优化】Android 工程中使用 libjpeg-turbo 压缩图片 ( 初始化压缩对象 | 打开文件 | 设置压缩参数 | 写入压缩图像数据 | 完成压缩 | 释放资源 )

    文章目录 一.使用 libjpeg-turbo 压缩图片流程 二.初始化 JPEG 压缩对象 三.打开文件 四.设置压缩参数 五.开始压缩 六.循环写入压缩数据 七.完成图片压缩及收尾 八.libjp ...

  8. 【Android 内存优化】Android 工程中使用 libjpeg-turbo 压缩图片 ( JNI 传递 Bitmap | 获取位图信息 | 获取图像数据 | 图像数据过滤 | 释放资源 )

    文章目录 一.Bitmap 图像数据处理 二.Java 层 Bitmap 对象转为 JNI 层 bitmap 对象 三.获取 bitmap 中的图像数据 四.过滤 bitmap 中的图像数据 ( 获取 ...

  9. 关于Android内存优化

    介绍 在Android系统中,内存分配与释放分配在一定程度上会影响App性能的-鉴于其使用的是类似于Java的GC回收机制,因此系统会以消耗一定的效率为代价,进行垃圾回收. 在中国有句老话:" ...

最新文章

  1. 10小时,这回一次搞定 Kafka 源码!
  2. 设计模式--工厂方法(Factory Method)模式
  3. 转:linux设置进程优先级
  4. 2021年高考英语卷三成绩查询,2021年全国3卷高考外语卷难不难,今年全国3卷高考外语卷难度系数点评...
  5. ubuntu安装后需了解的基本操作
  6. python学习小结
  7. 【英语学习】【WOTD】accolade 释义/词源/示例
  8. 蚂蚁集团官宣启动上市计划,上交所、港交所表示热烈欢迎...
  9. 由浅入深了解EventBus:(五)
  10. Fiddler抓包工具之详细使用步骤(超详细)
  11. 线性同余法生成随机数
  12. 机器学习实战---入门篇
  13. 筛选中很容易粘贴到被隐藏部分_excel数据复制到筛选表格被隐藏了-为什么数据粘贴至筛选后EXCEL 表格显示不完全?...
  14. 你还不了解QQ聊天是如何实现的吗?手把手教你实现网络聊天室
  15. 2019高考数学必考知识点,高考数学知识板块
  16. cvte java_cvte一面——java开发工程师
  17. win10系统开机自动修复失败的解决方法
  18. Navicat连接云端服务器上的MySQL数据库
  19. App自动化界面操作_按键模拟(模拟键盘信号)、driver.keyevent(keycode)
  20. yaourt 之 Curl 错误

热门文章

  1. python程序员培训_Python程序员学习路线图
  2. PDN仿真笔记5-电容走线影响寄生电感的因素分析
  3. 《大数据》笔记 Bonferroni correction
  4. USCD行人异常数据集使用指南 | 快速下载
  5. Java File删除文件夹及其子文件
  6. 康特EPON OLT开局配置
  7. base64 string类 放不下_千夜空的推荐 | LOFTER(乐乎) - 让兴趣,更有趣
  8. 玩转Qml(18)-用户向导
  9. 合并m3u8文件 okfun文件夹 .ok文件
  10. win10软件安装出现错误代码2503/2502,解决方案