「性能優(yōu)化4.0」運行期間檢測不合理的圖片

Bitmap 內(nèi)存模型

  • 在 API10 之前,Bitmap 對象本身存在 Dalvik Heap 中,像素是存在 native 中,這樣像素并不會占用 Heap 空間,也就不會造成 Heap 內(nèi)存溢出。但是缺點是Bitmap 對象被回收了,但是 native 層像素回收的時機可能跟 Heap 中 Bitmap 的對象回收時機不對應(yīng)。

  • API10之后,像素也放在 Dalvik Heap

  • API26 像素又放在 native,在 bitmap 被回收時,通過某一種機制能迅速地通知到 native 層回收對應(yīng)的像素

如何計算 bitmap 占用內(nèi)存

  • 通過 Bitmap API 在運行時計算
  1. getRowBytes:Since API Level 1
  2. getByteCount:Since API Level 12
  3. getAllocationByteCount:Since API Level 19
public static int getBitmapSize(Bitmap bitmap) {  
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {  //API 19  
         return bitmap.getAllocationByteCount();  
     }  
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { //Since API 12  
         return bitmap.getByteCount();  
     }  

     return bitmap.getRowBytes() * bitmap.getHeight();               
 } 
getAllocationByteCount
  • 給定一張圖片,計算加載到內(nèi)存中的大小

size= width x height x (一個像素占用內(nèi)存) x 壓縮比例

檢測不合理圖片

在實際開發(fā)中,如果服務(wù)傳給客戶端的圖片的實際寬高是遠大于 ImageView 的寬高,那么這種情況是會浪費內(nèi)存,那么如何去檢測這種不合理的情況呢?

通用方式

給每一個 ImageView 設(shè)置一個 PreDrawListener ,在回調(diào)中計算 Bitmap 的大小和 ImageView 的大小。

這種方案是可以實現(xiàn)檢測功能,但是缺點也很明顯,不通用,侵入性比較大。

Hook 方式

Hook 意思就是掛鉤,鉤住原有方法,修改執(zhí)行邏輯,這種 Hook 方法是在運行時插樁。

下面介紹一個 Hook 框架 :Epic

該框架 Hook 的最小粒度為方法層面,該框架支持 4.0-9.0 機型

引入epic框架

  • 添加依賴

compile 'me.weishu:epic:0.3.6'

  • 創(chuàng)建 XC_MethodHook 的子類,在內(nèi)部實現(xiàn) hook 邏輯

使用 epic 來檢測不合理圖片

    1. 找到我們的 hook 點,在給一個 ImageView 設(shè)置一個 Bitmap 時一般都會調(diào)用 setImageBitmap,因此該方法就是 hook 點
public void setImageBitmap(Bitmap bm) {
    ...
}
  • 2.hook setImageBitmap 方法
DexposedBridge.findAndHookMethod(ImageView.class, "setImageBitmap", Bitmap.class, new XC_MethodHook() {
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        super.afterHookedMethod(param);
        if (param.thisObject instanceof ImageView) {
            final ImageView imageView = (ImageView) param.thisObject;
            if (imageView.getDrawable() instanceof BitmapDrawable) {
                BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
                final Bitmap bitmap = drawable.getBitmap();
                if (bitmap != null) {
                    //bitmap 寬高
                    int bitmapWidth = bitmap.getWidth();
                    int bitmapHeight = bitmap.getHeight();
                    //視圖寬高
                    int viewWidth = imageView.getWidth();
                    int viewHeight = imageView.getHeight();
                    if (viewHeight > 0 && viewWidth > 0) {//view 有寬高
                        //當圖片寬高都大于視圖寬高的2倍時就報出警告
                        if (bitmapWidth >= viewWidth << 2 && bitmapHeight >= viewHeight << 2) {
                            warn(bitmapWidth, bitmapHeight, viewWidth, viewHeight);
                        }
                    } else {
                        imageView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                            @Override
                            public boolean onPreDraw() {
                                int bitmapWidth = bitmap.getWidth();
                                int bitmapHeight = bitmap.getHeight();
                                int viewWidth = imageView.getWidth();
                                int viewHeight = imageView.getHeight();
                                if (bitmapWidth >= viewWidth << 2 && bitmapHeight >= viewHeight << 2) {
                                    warn(bitmapWidth, bitmapHeight, viewWidth, viewHeight);
                                }
                                imageView.getViewTreeObserver().removeOnPreDrawListener(this);
                                return true;
                            }
                        });
                    }
                }
            }
        }
    }
});
  • 3.顯示警告信息:檢測出圖片大小不合理
private void warn(int bitmapWidth, int bitmapHeight, int viewWidth, int viewHeight) {
    StringBuffer msg = new StringBuffer();
    msg.append("圖片大小不合理:")
            .append("bitmapWidth=").append(bitmapWidth)
            .append(",bitmapHeight=").append(bitmapHeight)
            .append(",viewWidth=").append(viewWidth)
            .append(",viewHeight=").append(viewHeight);
    //不合理
    Log.e(TAG, Log.getStackTraceString(new Throwable(msg.toString())));
}
  • 4.log 檢測結(jié)果
log

總結(jié)

本文使用的介紹了兩種方式來檢測不合理圖片:

通用的方法:

  • 侵入性大
  • 通用性差
  • 沒有兼容性問題

而 hook 的方式的優(yōu)缺點如下::

  • 無侵入性
  • 通用性強
  • 兼容性問題大,開源方案不能帶到線程環(huán)境

參考

本文是筆者學習之后的總結(jié),方便日后查看學習,有任何不對的地方請指正。

記錄于 2019年4月16號

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容