前提
在bugly中有时候会看到加载很大的图片(如5m以上)导致OOM,打算把项目中ImageView占用内存大小显示出来,这样在测试阶段就能发现,然后就可以进行修改。最终结果如下:
实现
- 创建一个
BadgeImageView
类继承ImageView,覆写onDrawForeground方
法,在该方法中将内存大小信息画出来。最后我们会将代码中的ImageView
转换成该类
public class BadgeImageView extends AppCompatImageView implements BaseView {
private ImageViewBadgeHelper badgeHelper;
public BadgeImageView(Context context) {
super(context);
initBadgeHelper();
}
public BadgeImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initBadgeHelper();
}
public BadgeImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initBadgeHelper();
}
private void initBadgeHelper() {
if (keepImageSizeUtils.IS_DEBUG) {
badgeHelper = new ImageViewBadgeHelper(this);
}
}
@Override
public View getView() {
return this;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (badgeHelper != null)
badgeHelper.setWidthHeight(w, h);
}
@Override
public void onDrawForeground(Canvas canvas) {
if (badgeHelper != null) {
badgeHelper.drawBadge(canvas);
} else {
super.onDrawForeground(canvas);
}
}
}
-
ImageViewBadgeHelper
该类就是在ImageView
上画出内存大小
class ImageViewBadgeHelper {
private ImageView mImageView;
private Paint mBadgeBackgroundPaint;
private Paint mBadgeTextPaint;
private int mBadgePadding;
private Paint.FontMetrics mBadgeTextFontMetrics;
private RectF mBadgeTextRect, mBadgeBackgroundRect;
private PointF center = new PointF();
private int mWidth = 0;
private int mHeight = 0;
public ImageViewBadgeHelper(ImageView imageView) {
mImageView = imageView;
mBadgeBackgroundPaint = new Paint();
mBadgeBackgroundPaint.setAntiAlias(true);
mBadgeBackgroundPaint.setStyle(Paint.Style.FILL);
mBadgeBackgroundPaint.setColor(0xFFE84E40);
mBadgeBackgroundRect = new RectF();
mBadgeTextPaint = new Paint();
mBadgeTextPaint.setTextSize(keepImageSizeUtils.dip2px(imageView.getContext(), 11));
mBadgeTextPaint.setColor(0xFFFFFFFF);
mBadgeTextRect = new RectF();
mBadgePadding = keepImageSizeUtils.dip2px(imageView.getContext(), 5);
}
public void setWidthHeight(int width, int height) {
this.mWidth = width;
this.mHeight = height;
}
public void drawBadge(Canvas canvas) {
String mBadgeText = keepImageSizeUtils.getDrawableSizeInfo(mImageView);
if (!TextUtils.isEmpty(mBadgeText)) {
measureText(mBadgeText);
float rectWidth = mBadgeTextRect.height() > mBadgeTextRect.width() ? mBadgeTextRect.height() : mBadgeTextRect.width();
center.x = mWidth - (mBadgePadding + rectWidth / 2f);
//center.y = mBadgePadding + mBadgeTextRect.height() / 2f;
center.y = mHeight - mBadgePadding - mBadgeTextRect.height() / 2f;
mBadgeBackgroundRect.left = center.x - (mBadgeTextRect.width() / 2f + mBadgePadding);
mBadgeBackgroundRect.top = center.y - (mBadgeTextRect.height() / 2f + mBadgePadding * 0.5f);
mBadgeBackgroundRect.right = center.x + (mBadgeTextRect.width() / 2f + mBadgePadding);
mBadgeBackgroundRect.bottom = center.y + (mBadgeTextRect.height() / 2f + mBadgePadding * 0.5f);
float radius = mBadgeBackgroundRect.height() / 2f;
canvas.drawRoundRect(mBadgeBackgroundRect, radius, radius, mBadgeBackgroundPaint);
canvas.drawText(mBadgeText, center.x - mBadgeTextRect.width() / 2f,
(mBadgeBackgroundRect.bottom + mBadgeBackgroundRect.top
- mBadgeTextFontMetrics.bottom - mBadgeTextFontMetrics.top) / 2f,
mBadgeTextPaint);
}
}
private void measureText(String mBadgeText) {
mBadgeTextRect.left = 0;
mBadgeTextRect.top = 0;
if (TextUtils.isEmpty(mBadgeText)) {
mBadgeTextRect.right = 0;
mBadgeTextRect.bottom = 0;
} else {
mBadgeTextRect.right = mBadgeTextPaint.measureText(mBadgeText);
mBadgeTextFontMetrics = mBadgeTextPaint.getFontMetrics();
mBadgeTextRect.bottom = mBadgeTextFontMetrics.descent - mBadgeTextFontMetrics.ascent;
}
}
}
- 在
BaseActivity
的onCreate
方法之前调用下面方法
LayoutInflater.from(context).setFactory2(new LayoutInflater.Factory2() {
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return null;
}
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
if (TextUtils.equals(name, "ImageView")) {
return new BadgeImageView(context, attrs);
}
return null;
}
});
这样所有的imageView
就转化成了BadgeImageView
结论
通过这个方法我们在项目中发现了下面的问题
- 有的地方直接获取Bitmap,大小没有和ImageView对应上,导致Bitmap过大
- 项目中使用的Glide来加载图片,全局设置成RGB_565,但是有的地方部分开发设置成了ARGB_8888
- Glide的transform(比如说圆角,圆形变换)会将图片变透明,这样加载出来的图片就变成ARGB_8888,导致图片占用内存变大
- 有的图片资源长宽很大
ImageSizeUtils
public class ImageSizeUtils {
private static final String TAG = ImageSizeUtils.class.getSimpleName();
private static LruCache<Drawable, String> sizeCaches = new LruCache<>(30);
public static String getDrawableSizeInfo(ImageView imageView) {
Bitmap bitmap;
Drawable drawable = imageView.getDrawable();
if (drawable != null) {
if (sizeCaches.get(drawable) != null) {
// Log.d(TAG, "get from lruCache:" + drawable);
return sizeCaches.get(drawable);
}
if (drawable instanceof BitmapDrawable) {
bitmap = ((BitmapDrawable) drawable).getBitmap();
} else {
// Log.d(TAG, "not BitmapDrawable=" + drawable.getClass().getSimpleName());
if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
} else {
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(bitmap);
if (drawable.getConstantState() != null) {
Drawable drawDrawable = drawable.getConstantState().newDrawable(imageView.getContext().getResources());
drawDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawDrawable.draw(canvas);
}
}
int size = getBitmapSize(bitmap);
StringBuilder sb = new StringBuilder();
if (size > 500) {
sb.append(bitmap.getWidth()).append("x").append(bitmap.getHeight());
if (bitmap.getConfig() != null) {
sb.append("(").append(bitmap.getConfig().toString()).append(")");
}
sb.append(":").append(size).append("K");
}
sizeCaches.put(drawable, sb.toString());
return sb.toString();
}
return null;
}
public static int getBitmapSize(Bitmap bitmap) {
int size = 0;
if (bitmap == null) {
size = 0;
} /*else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
size = bitmap.getAllocationByteCount();
}*/ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
size = bitmap.getByteCount();
} else {
size = bitmap.getRowBytes() * bitmap.getHeight();
}
return size / 1024;
}
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}