开源自研内存分析利器Android Bitmap Monitor图片定位详解
作者:张拭心 发布时间:2023-02-04 13:45:36
在日常工作中,我们往往只关注 Java 内存使用情况,这主要是因为 Java 内存分析相关的工具比较多。与之不同的是,图片内存分析的工具比较少,当分析图片内存问题时我们需要花费很大的精力。
我们知道,在 Android 应用使用的内存中,图片总是占据不少比例。拿小米 12 来说,3200 x 1440 的分辨率,一张全屏的图片至少要占用 17MB(3200 x 1440 x 4 )。如果缓存里多几张,基本就要达到上百 MB。加载的图片稍有不当,就可能导致应用的内存溢出崩溃大大增加。
因此,我们需要这样的工具:可以快速发现应用内加载的图片是否合理,比如大小是否合适、是否存在泄漏、缓存是否及时清理、是否加载了当前并不需要的图片等等。
AndroidBitmapMonitor 正是为此而生!它是一个开源的 Android 图片内存分析工具,可以帮助开发者快速发现应用的图片使用是否合理,支持在线下和线上使用。
AndroidBitmapMonitor 提供了这些功能:
获取内存中的 Bitmap 数量及占用内存
查看 Bitmap 创建堆栈及线程
导出 Bitmap 图片,帮助直接定位问题所属业务
动态开关,可以在任意时间开始和结束
功能介绍
支持 Android 4.4 - 13 (API level 19 - 33)
支持 armeabi-v7a 和 arm64-v8a
支持线下实时查看图片内存情况 和 线上数据统计
可以提供的功能:
获取内存中的图片数量及占用内存
获取 Bitmap 创建堆栈及线程
全版本 Bitmap Preview,在堆栈无法看出问题时,可以用来定位图片所属业务
动图:
核心功能截图:
悬浮窗中可以实时查看到图片内存
内存中的图片信息
某张图片的具体信息
使用文档
主要有四步:
添加 gradle 依赖
初始化配置
在需要的时候调用 start 和 stop
获取数据
1. 在 build.gradle 中增加依赖
Android Bitmap Monitor 发布在 mavenCentral 上,因此首先需要确保您的项目有使用 mavenCentral 作为仓库。
您可以在根目录的 build.gradle 或者 setting.gradle 中添加以下代码:
allprojects {
repositories {
//...
//添加 mavenCentral 依赖
mavenCentral()
}
}
接着在具体业务的 build.gradle 文件中添加依赖:
android {
packagingOptions {
pickFirst 'lib/*/libshadowhook.so'
}
}
dependencies {
implementation 'io.github.shixinzhang:android-bitmap-monitor:1.0.2'
}
请注意:为了避免和其他库冲突,上面的 packagingOptions 中 pickFirst 'lib/*/libshadowhook.so'
是必要的。
添加完依赖并执行 gradle sync 后,下一步就是在代码里进行初始化和启动。
2. 初始化
初始化需要调用的 API 是 BitmapMonitor.init
:
long checkInterval = 10;
long threshold = 100 * 1024;
long restoreImageThreshold = 100 * 1024;;
String dir = this.getExternalFilesDir("bitmap_monitor").getAbsolutePath();
BitmapMonitor.Config config = new BitmapMonitor.Config.Builder()
.checkRecycleInterval(checkInterval) //检查图片是否被回收的间隔,单位:秒 (建议不要太频繁,默认 5秒)
.getStackThreshold(threshold) //获取堆栈的阈值,当一张图片占据的内存超过这个数值后就会去抓栈
.restoreImageThreshold(restoreImageThreshold) //还原图片的阈值,当一张图占据的内存超过这个数值后,就会还原出一张原始图片
.restoreImageDirectory(dir) //保存还原后图片的目录
.showFloatWindow(true) //是否展示悬浮窗,可实时查看内存大小(建议只在 debug 环境打开)
.isDebug(true)
.context(this)
.build();
BitmapMonitor.init(config);
当 showFloatWindow 为 true 时,首次启动 app 需要授予悬浮窗权限。
3. 开启和停止监控
初始化完成后,可以在任意时刻调用 start/stop 开启和停止监控:
//开启监控,方式1
BitmapMonitor.start();
//开启方式2,提供页面获取接口,建议使用
BitmapMonitor.start(new BitmapMonitor.CurrentSceneProvider() {
@Override
public String getCurrentScene() {
//返回当前顶部页面名称
if (sCurrentActivity != null) {
return sCurrentActivity.getClass().getSimpleName();
}
return null;
}
});
//停止监控
BitmapMonitor.stop();
上面的代码中,开启方式 2 的参数用来获取图片创建时的页面名称,这个接口可以帮助知道大图是在哪个页面创建的。如果不想提供这个接口可以使用开启方式 1。
那我们该在什么使用开启监控呢?
一般有「全局开启」和「分业务开启」两种使用方式:
全局开启:一启动就 start,用于了解整个 APP 使用过程中的图片内存数据
分业务开启:在进入某个业务前 start,退出后 stop,用于了解特定业务的图片内存数据
4. 获取数据
在初始化完成并开启监控后,我们就可以拦截到每张图片的创建过程。
Android Bitmap Monitor 提供了两种获取内存中图片数据的 API:
定时回调 addListener
主动获取数据 dumpBitmapInfo
定时回调 是指注册一个 listener,这个接口的回调会按照一定时间间隔被调用,可以用来做实时监控:
BitmapMonitor.addListener(new BitmapMonitor.BitmapInfoListener() {
@Override
public void onBitmapInfoChanged(final BitmapMonitorData data) {
Log.d("bitmapmonitor", "onBitmapInfoChanged: " + data);
}
});
间隔时间是初始化时传递的参数 checkRecycleInterval,返回的数据结构如下所示:
public class BitmapMonitorData {
//历史创建的总图片数
public long createBitmapCount;
//历史创建的总图片内存大小,单位 byte
public long createBitmapMemorySize;
//当前内存中还未回收的图片数
public long remainBitmapCount;
//当前内存中还未回收的图片内存大小,单位 byte
public long remainBitmapMemorySize;
//泄漏(未释放)的 bitmap 数据
public BitmapRecord[] remainBitmapRecords;
//...
}
主动获取数据 是指主动调用 BitmapMonitor.dumpBitmapInfo()
获取内存中的所有数据,可以用在内存升高时上报数据:
//获取所有数据
BitmapMonitorData bitmapAllData = BitmapMonitor.dumpBitmapInfo();
Log.d("bitmapmonitor", "bitmapAllData: " + bitmapAllData);
//仅获取数量和内存大小,不获取具体图片信息
BitmapMonitorData bitmapCountData = BitmapMonitor.dumpBitmapCount();
Log.d("bitmapmonitor", "bitmapCountData: " + bitmapCountData);
dumpBitmapInfo
会返回内存中所有图片的信息,如果只想获取到图片的总数和内存总量,可以调用 dumpBitmapCount
,速度更快更轻量。
来源:https://juejin.cn/post/7202612506148782137
猜你喜欢
- Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。本篇不涉及其原理,只用代码构建项目简单试用一下其回滚
- 本文实例为大家分享了Android弹窗控件CustomFiltControl的使用方法,供大家参考,具体内容如下效果:起初踩的坑:&nbs
- 近几天又温习了一下SpringMVC的运行机制以及原理我理解的springmvc,是设计模式MVC中C层,也就是Controller(控制)
- 本文实例为大家分享了Unity实现ScrollView滑动吸附的具体代码,供大家参考,具体内容如下最近在做一个展示模块的时候遇到了一个需要实
- 在我们将Winform自带的边框隐藏之后,我们需要自己编写窗口的移动。思路就是1.获得点击左键时当前鼠标的坐标2.获得移动后鼠标的坐标3.窗
- 题目我们可以用2×1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2×1的小矩形无重叠地覆盖一个2×n的大矩形,总共有多少种方法?程序核心
- 调研了一下目前的路由框架,ARouter(阿里的),ActivityRouter都使用了apt技术 编译时注解,个人想法是一口吃不成胖子,先
- Java读取properties文件中文乱码初用properties,读取java properties文件的时候如果value是中文,会出
- 大家好哇,又是我,梦辛工作室的灵,今天来给大家讲解下如何实现 安卓的侧边弹窗,先大概讲下基本原理吧,其实很简单,就是一个进出动效,用 位移
- 1介绍MVC框架是什么MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(control
- 首先我们看看为什么需要对象复制?为什么需要对象复制如上,是我们平时开发中最常见的三层MVC架构模型,编辑操作时Controller层接收到前
- 最近开发过程中遇到了一个问题,之前没有太注意,这里记录一下。我用的SpringBoot版本是2.0.5,在跟前端联调的时候,有个请求因为用户
- 本文实例讲述了C#实现在Form里面内嵌dos窗体的方法。分享给大家供大家参考。具体如下:using System;using System
- 本文简要介绍如何使用Spring Cloud Gateway 作为API 网关(不是使用zuul作为网关),关于Spring Cloud G
- 使用要点如下:1.利用ListAdapter(一般使用ArrayAdapter)为AutoCompleteTextView提供数据,若有需要
- 接上一篇写的截取电脑屏幕,我们在原来的基础上加一个选择区域的功能,实现自定义选择截图。个人比较懒,上一篇的代码就不重新设计了,就简单改一下呈
- 前言jdchain是京东数科开源的区块链平台,目标是实现一个面向企业应用场景的通用区块链框架系统,能够作为企业级基础设施,为业务创新提供高效
- 目录文件下载文件上传 * * 的配置多个 * 的执行顺序异常处理器基于配置的异常处理基于注解的异常处理总结文件下载使用ResponseEn
- 为什么要自定义缓存注解?Spring Cache本身提供@Cacheable、@CacheEvict、@CachePut等缓存注解,为什么还
- BeanUtils.copyProperties无法封装使用BeanUtils.copyProperties(user, memeber);