更优雅地检测android应用中的大图

4,247 阅读4分钟

场景

在大部分的Android开发过程中,因为项目管理等原因,往往会忽视应用性能问题,如一个尺寸为 N×N 的UI控件,背后加载的实际图片可能是 2N×2N 。

image.png

这样场景下的内存利用率是极低的。

那么有没有途径能够更好地发现这些场景并逐一进行优化呢?

答案是有的。

方案对比

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 登场。

简介

由鄙人呕心沥血编写的大图检测工具,无侵入性,强兼容性,可及时通知开发人员获知加载大图时的性能问题。

可能是东半球好用的大图检测工具。

image.png

如何使用

只需一步,添加以下依赖即可

debugImplementation(name: 'BitmapCanary-debug', ext: 'aar')

查看记录

吐司提示

当加载大图所在页面进行跳转或销毁时,可触发吐司。可以明确地获知页面名称、View控件类名、 xml 文件中对应的 id 名称

image.png

logcat提示

logcat中搜索关键字 BitmapCanary 即可

image.png

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

架构图

image.png

源码地址

点我

issue

点我

后记

三流应用赶时间,二流应用堆业务,一流应用看体验。

二流应用堆完业务后,能支撑优秀用户体验的只有高性能。

可用户看到的现实往往是二流、三流应用,即使是头部app的体验往往也不尽人意,卡顿、高内存占用、白屏等依然是常见现象。

为什么会这样呢?因为一流应用没有市场。一是因为业务频繁更替,二是因为用户没得选择,或者说用户只能被动选择。

有太多打着“用户体验”的幌子,行剽窃“用户隐私”的勾当了。如果真是为了用户体验,为什么统一推送联盟做不起来?为什么不降后台内存占用从而提高内存利用率?为什么安装应用后 ROM 默认给了所有权限而用户不知情?

环境如此,我一时竟不知道是厂商的悲哀,还是用户的悲哀。