@[TOC](【性能工具】一种简易hook bitmap创建的插件使用)
一、插件开源地址:
二、插件引入:
- 1、rootProject/build.gradle
classpath 'com.github.HairySnow.lancet:lancet-plugin:1.0.3'
- 2、app/build.gradle
apply plugin: 'com.hairysnow.lancet'
// 添加库依赖
compileOnly 'com.github.HairySnow.lancet:lancet-base:1.0.3'
三、编写插桩代码
因为加载Bitmap基本都要经过Bitmap.create()方法创建Bitmap对象,所以只需hook Bitmap.createBitmap方法的所有重载方法即可监控到图片的创建过程,在hook方法执行到时,通过width * height * 图片位深度的方式就可以计算出bitmap的内存占用大小。
@TargetClass(value = "android.graphics.Bitmap") 注解指定要插桩的类全路径,可以通过AS查找到这个类后鼠标放在类名上右键选择copy Reference就可以拿到该注解需要传入的参数值 @Proxy(value = "createBitmap") 注解指定要插桩的类的具体方法名 一般来说使用Glide加载图片用到的是createBitmap(int width, int height, Bitmap.Config config)这个重载方法的实现。
public class BitmapHook {
@TargetClass(value = "android.graphics.Bitmap")
@Proxy(value = "createBitmap")
public static Bitmap createBitmap(Bitmap source) {
float factor = 1;
if (source.getConfig().name().equals(Bitmap.Config.ARGB_8888.name())) {
factor = 4;
} else if (source.getConfig().name().equals(Bitmap.Config.ARGB_4444.name()) || source.getConfig().name().equals(Bitmap.Config.RGB_565.name())) {
factor = 2;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && source.getConfig().name().equals(Bitmap.Config.RGBA_F16.name())) {
factor = 8;
}
int width = source.getWidth();
int height = source.getHeight();
float size = width * height * factor / (1024f * 1024f);
Log.i("BitmapHook", "createBitmap: size = " + size + ", w = " + width + ", h = " + height);
return (Bitmap) Origin.call();
}
@TargetClass(value = "android.graphics.Bitmap")
@Proxy(value = "createBitmap")
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) {
float factor = 1;
if (source.getConfig().name().equals(Bitmap.Config.ARGB_8888.name())) {
factor = 4;
} else if (source.getConfig().name().equals(Bitmap.Config.ARGB_4444.name()) || source.getConfig().name().equals(Bitmap.Config.RGB_565.name())) {
factor = 2;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && source.getConfig().name().equals(Bitmap.Config.RGBA_F16.name())) {
factor = 8;
}
float size = width * height * factor / (1024f * 1024f);
Log.i("BitmapHook", "createBitmap: size = " + size + ", w = " + width + ", h = " + height);
return (Bitmap) Origin.call();
}
@TargetClass(value = "android.graphics.Bitmap")
@Proxy(value = "createBitmap")
public static Bitmap createBitmap(int width, int height, Bitmap.Config config) {
float factor = 1;
if (config.name().equals(Bitmap.Config.ARGB_8888.name())) {
factor = 4;
} else if (config.name().equals(Bitmap.Config.ARGB_4444.name()) || config.name().equals(Bitmap.Config.RGB_565.name())) {
factor = 2;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && config.name().equals(Bitmap.Config.RGBA_F16.name())) {
factor = 8;
}
float size = width * height * factor / (1024f * 1024f);
Log.i("BitmapHook", "createBitmap: size = " + size + "MB, w = " + width + ", h = " + height);
return (Bitmap) Origin.call();
}
}
ps: 上面并没有hook createBitmap所有重载方法,只是贴上了常见的,有新增需按照如上模板代码添加实现。
四、编写调用demo代码,验证插桩效果
BitmapHookActivity代码:
class BitmapHookActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_bitmap_hook)
val imageView = findViewById<ImageView>(R.id.imageview)
Glide.with(this@BitmapHookActivity)
.load(R.drawable.test)
.override(1920, 1080)
.into(imageView)
}
}
输出:
2025-05-07 19:30:18.776 28613-28700 BitmapHook com.demo.leanbackdemo I createBitmap: size = 3.1723022MB, w = 770, h = 1080
如上输出就拿到了使用Glide加载的Bitmap对象的内存占用大小,然后增加判断超过内存占用阈值的bitmap,可以增加一些后台上报信息。