场景
在大部分的Android开发过程中,因为项目管理等原因,往往会忽视应用性能问题,如一个尺寸为 N×N 的UI控件,背后加载的实际图片可能是 2N×2N 。
这样场景下的内存利用率是极低的。
那么有没有途径能够更好地发现这些场景并逐一进行优化呢?
答案是有的。
方案对比
ArtHook 方案
该方案采用 weishu 大佬写的 epic 库实现,通过对ART虚拟机的 hook,hook ImageView的 setImageBitmap 等方法,解析对比方法参数中的 bitmap 宽高和 ImageView 实例的宽高
添加依赖
implementation 'me.weishu:epic:0.11.0'
hook代码
public class ImageHook extends XC_MethodHook {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
//作检测逻辑
checkBitmap(...)
}
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
}
}
优点
- 侵入性极低,一次初始化配置即可 hook 全局的目标 View 控件
- 可以获取代码调用堆栈,方便开发者快速定位
缺点
- 兼容性存在问题,使用了 hook 系统 API ,不同机型、不同虚拟机上的表现可能不同
BaseActivity 方案
大部分应用工程在业务发展的过程中都会沉淀封装自己的 BaseActivity ,通过在 BaseActivity 中动态地检测各个 View 控件,从而获知图片加载情况
class BaseActivity : Activity(){
fun onCreate(){
//codes
}
fun onDestory(){
if(isOpenCheckBitmap){
checkBitmapFromView()
}
}
fun checkBitmapFromView(){
//1、遍历activity中的各个View控件
//2、获取View控件加载的Bitmap
//3、对比Bitmap宽高与View宽高
}
}
优点
- 兼容性强,无任何反射
- 如果应用中本身存在 onDestory 时释放各个 View 控件图片资源等逻辑,则可以复用 activity 中 View 的遍历过程
缺点
- 侵入性太强,BaseActvity 本身存在篡改风险,提高了代码管理成本
Glide 方案
Glide 是非常著名的图片加载组件,该组件在加载图片时可以依据控件本身的尺寸按需加载
优点
- 业务层可以专注业务开发,无需过分关注图片加载过程
缺点
- 无法管理 xml 文件中 background 等属性声明的 View 控件
ASM 方案
该方案在编译流程进行插桩,通过匹配 setImageBitmap 、 setBackground 等关键方法,插入 Bitmap 大小检测逻辑
优点
- 编译时插桩,对开发过程的无侵入性
缺点
- 后期维护成本高
BitmapCanary 诞生
通过前面一通对比,那么有没有无侵入性、强兼容性的方案呢? 有请 BitmapCanary 登场。
简介
由鄙人呕心沥血编写的大图检测工具,无侵入性,强兼容性,可及时通知开发人员获知加载大图时的性能问题。
可能是东半球最好用的大图检测工具。
如何使用
只需一步,添加以下依赖即可
debugImplementation(name: 'BitmapCanary-debug', ext: 'aar')
查看记录
吐司提示
当加载大图所在页面进行跳转或销毁时,可触发吐司。可以明确地获知页面名称、View控件类名、 xml 文件中对应的 id 名称
logcat提示
logcat中搜索关键字 BitmapCanary 即可
2021-04-28 15:12:33.313 26939-26939/com.pengyeah.bitmapcanarydemo I/BitmapCanary: Bitmap size too large!!!
Activity Name:(com.pengyeah.bitmapcanarydemo.TwoActivity)
View Id Name:(null)
View Class Name:(MaterialButton)
real width: (640)
real height: (360)
desired width: (30)
desired height: (30)
日志文件
日志文件记录在:/data/user/0/[packageName]/files/BitmapCanary_yyyyMMdd.log 例如:
/data/user/0/com.pengyeah.performancetooldemo/files/BitmapCanary_20210606.log
配置项
默认配置为当View控件加载的图片宽高大于自身的 1.2 倍时触发警告,可修改如下配置
警告倍率
默认 1.2F
BitmapCanary.rate = 2F
是否输出logcat
默认输出
BitmapCanary.isOpenLog = false
是否输出文件
默认输出
BitmapCanary.isWriteLog = false
架构图
源码地址
issue
后记
三流应用赶时间,二流应用堆业务,一流应用看体验。
二流应用堆完业务后,能支撑优秀用户体验的只有高性能。
可用户看到的现实往往是二流、三流应用,即使是头部app的体验往往也不尽人意,卡顿、高内存占用、白屏等依然是常见现象。
为什么会这样呢?因为一流应用没有市场。一是因为业务频繁更替,二是因为用户没得选择,或者说用户只能被动选择。
有太多打着“用户体验”的幌子,行剽窃“用户隐私”的勾当了。如果真是为了用户体验,为什么统一推送联盟做不起来?为什么不降后台内存占用从而提高内存利用率?为什么安装应用后 ROM 默认给了所有权限而用户不知情?
环境如此,我一时竟不知道是厂商的悲哀,还是用户的悲哀。