Bitmap内存模型
API10之前Bitmap自身在Dalvik Heap中,像素在Native
API10之后像素也被放在Dalvik Heap中
API26之后像素在Native
获取Bitmap占用内存
getByteCount
宽高一像素占用内存(如果图片放在drawable目录下,宽高需要乘上缩放比例)
常规方式
背景:图片对内存优化至关重要,图片宽高大于控件宽高
实现:继承ImageView,,覆写实现计算大小
侵入性强
不通用
ARTHook实践
ARTHook介绍
挂钩,将额外的代码构住原有方法,修改执行逻辑
运行时插桩
性能分析
Epic简介
Epic是一个虚拟机层面、以Java Method为粒度的运行时Hook框架
支持Android4.0-9.0
ARTHook实现
public class ImageHook extends XC_MethodHook {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
// 实现我们的逻辑
ImageView imageView = (ImageView) param.thisObject;
checkBitmap(imageView,((ImageView) param.thisObject).getDrawable());
}
private static void checkBitmap(Object thiz, Drawable drawable) {
if (drawable instanceof BitmapDrawable && thiz instanceof View) {
final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
if (bitmap != null) {
final View view = (View) thiz;
int width = view.getWidth();
int height = view.getHeight();
if (width > 0 && height > 0) {
// 图标宽高都大于view带下的2倍以上,则警告
if (bitmap.getWidth() >= (width << 1)
&& bitmap.getHeight() >= (height << 1)) {
warn(bitmap.getWidth(), bitmap.getHeight(), width, height, new RuntimeException("Bitmap size too large"));
}
} else {
final Throwable stackTrace = new RuntimeException();
view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
int w = view.getWidth();
int h = view.getHeight();
if (w > 0 && h > 0) {
if (bitmap.getWidth() >= (w << 1)
&& bitmap.getHeight() >= (h << 1)) {
warn(bitmap.getWidth(), bitmap.getHeight(), w, h, stackTrace);
}
view.getViewTreeObserver().removeOnPreDrawListener(this);
}
return true;
}
});
}
}
}
}
private static void warn(int bitmapWidth, int bitmapHeight, int viewWidth, int viewHeight, Throwable t) {
String warnInfo = new StringBuilder("Bitmap size too large: ")
.append("\n real size: (").append(bitmapWidth).append(',').append(bitmapHeight).append(')')
.append("\n desired size: (").append(viewWidth).append(',').append(viewHeight).append(')')
.append("\n call stack trace: \n").append(Log.getStackTraceString(t)).append('\n')
.toString();
LogUtils.i(warnInfo);
}
}
应用启动的时候hook方法
DexposedBridge.hookAllConstructors(ImageView.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
DexposedBridge.findAndHookMethod(ImageView.class, "setImageBitmap", Bitmap.class, new ImageHook());
}
});
无侵入性
通用性强
兼容问题大,开源方案不能带到线上环境