Android中圖片處理優化

溫故而知新,系統整理一下。

1. 圖片的三級緩存

來源自網絡
來源自網絡
1.1 什么是三級緩存?
      一級: 內存中的緩存圖片對象(Bitmap), 用Map<url, Bitmap>
      二級: 手機sd卡的files或手機內部的files中緩存圖片文件(xxx.jpg/png)
      三級: 服務器端保存圖片文件
1.2 如何應用三級緩存?
      例如: 如何根據url根據圖片顯示?
      1). 根據url從一級緩存(Map<url, Bitmap>)中取圖片對象, 如果取到了, 直接顯示
                注意: 真實項目中使用LruCache<String, Bitmap>來緩存圖片()
      2). 如果沒有, 根據url中包含圖片名稱(文件名), 手機的sd卡或內部找對就圖片文件加載成bitmap
                如果找到了, 顯示并保存到一級緩存
      3). 如果沒有, 顯示一個默認的圖片, 根據url聯網請求獲取bitmap
                如果沒有, 顯示一張代表錯誤的圖片
                如果有:
                     a. 保存到一級緩存
                     b. 保存到二級緩存
                     c. 顯示圖片

2. 大圖片的加載顯示(避免OOM問題):

1). 問題: 如果將大圖片加載到內存中來顯示, 可能會導致內存溢出(OOM)
2). 解決思路: 對圖片進行壓縮加載(本質上只是讀取了圖片文件的部分數據)
3). 具體辦法:
①. 得到圖片的寬高的方式:

    1. BitmapFactory.Options options = new BitmapFactory.Options();   options.inJustDecodeBounds = true;   //設置只讀取圖片文件的邊框,這樣就不會加載整個圖片文件BitmapFactory.decodeResource(getResources(), R.id.myimage, options);   int imageHeight = options.outHeight;  //圖片的高 int imageWidth = options.outWidth;  //圖片的寬String imageType = options.outMimeType; 
 
               注意: 為了避免OOM異常,最好在解析每張圖片的時候都先檢查一下圖片的大小,
                       除非你非常信任圖片的來源,保證這些圖片都不會超出你程序的可用內存      

②. 計算取樣比例的方式:

    1. public static int calculateInSampleSize(BitmapFactory.Options options,           int reqWidth, int reqHeight) {       // 源圖片的高度和寬度       final int height = options.outHeight;       final int width = options.outWidth;       int inSampleSize = 1;       if (height > reqHeight || width > reqWidth) {           // 計算出實際寬高和目標寬高的比率           final int heightRatio = Math.round((float) height / (float) reqHeight);           final int widthRatio = Math.round((float) width / (float) reqWidth);           // 選擇寬和高中最小的比率作為inSampleSize的值,這樣可以保證最終圖片的寬和高           // 一定都會大于等于目標的寬和高。           inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;       }       return inSampleSize;   } 
 
           注意: 如果inSampleSize=3, 表示 寬和高上只讀取原來數據的1/3, 這樣整體大小壓縮為原來的1/9

③. 整體處理:

    1. public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,           int reqWidth, int reqHeight) {       // 第一次解析將inJustDecodeBounds設置為true,只獲取圖片寬,高大小       final BitmapFactory.Options options = new BitmapFactory.Options();       options.inJustDecodeBounds = true; //不會加載整個圖片文件    BitmapFactory.decodeResource(res, resId, options);       // 調用上面定義的方法計算inSampleSize值       options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);       // 使用獲取到的inSampleSize值再次解析圖片       options.inJustDecodeBounds = false; //會根據inSampleSize加載圖片的部分數據到內存      return BitmapFactory.decodeResource(res, resId, options);   } 
 
        調用代碼如下:
 
    1. mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100)); //將任意一張圖片壓縮成100*100的縮略圖,并在ImageView上展示

3. 緩存圖片對象

1). 在內存中緩存圖片對象(Bitmap), 不要直接用Map<String, Bitmap>類型的容器, 因為這樣會導致bitmap對象太多太大時而沒有去釋放, 最終導致OOM
2). 在Android2.3之前, 一般都用Map<String, SoftReference<Bitmap>>結構容器來緩存Bitmap對象, 這樣在內存不太充足時, 垃圾回收器會將軟引用對象釋放.
但從2.3開始, 垃圾回收器可能在正常情況下就回收軟引用對象, 這樣會降低緩存的效果
3). Android的v4兼容包中提供了LruCache來做緩存容器, 它的基本原理為:
把最近使用的對象用強引用存儲在 LinkedHashMap 中,并且把最近最少使用的對象在緩存值達到預設定值之前從內存中移除

1.    private LruCache<String, Bitmap> mMemoryCache; //緩存Bitmap的容器    @Override   protected void onCreate(Bundle savedInstanceState) {       // 獲取到可用內存的最大值,使用內存超出這個值會引起OutOfMemory異常。       // LruCache通過構造函數傳入緩存值,以KB為單位。       int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);       // 使用最大可用內存值的1/8作為緩存的大小。       int cacheSize = maxMemory / 8;       mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {           @Override           protected int sizeOf(String key, Bitmap bitmap) {               // 重寫此方法來衡量每張圖片的大小,默認返回圖片數量。               return bitmap.getByteCount() / 1024;           }       };   }    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {       if (getBitmapFromMemCache(key) == null) {           mMemoryCache.put(key, bitmap);       }   }    public Bitmap getBitmapFromMemCache(String key) {       return mMemoryCache.get(key);   }

4. 主動釋放bitmap對象

if(!bmp.isRecycle() ){
bmp.recycle() // 回收圖片所占的內存
system.gc() // 提醒系統及時回收
}

5. 設置加載圖片的色彩模式:

Android中有四種,分別是:
ALPHA_8:每個像素占用1byte內存
ARGB_4444:每個像素占用2byte內存
ARGB_8888:每個像素占用4byte內存(默認)
RGB_565:每個像素占用2byte內存

默認的模式顯示的圖片質量是最高的, 但也是占用內存最大的, 而如果使用ARGB_4444模式, 占用的內存就會減少為1/2, 但顯示效果差別不明顯
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
Bitmap img = BitmapFactory.decodeFile("/sdcard/1.png", options);

========================================================
Android 三大圖片加載框架比較

1.哪三大圖片加載框架?
1) Picasso
2) Glide
3) Fresco

2.介紹:

Picasso :和Square的網絡庫一起能發揮最大作用,因為Picasso可以選擇將網絡請求的緩存部分交給了okhttp實現。

Glide:模仿了Picasso的API,而且在他的基礎上加了很多的擴展(比如gif等支持),Glide默認的Bitmap格式是RGB_565,比 Picasso默認的ARGB_8888格式的內存開銷要小一半;Picasso緩存的是全尺寸的(只緩存一種),而Glide緩存的是跟ImageView尺寸相同的(即5656和128128是兩個緩存) 。

FB的圖片加載框架Fresco:最大的優勢在于5.0以下(最低2.3)的bitmap加載。在5.0以下系統,Fresco將圖片放到一個特別的內存區域(Ashmem區)。當然,在圖片不顯示的時候,占用的內存會自動被釋放。這會使得APP更加流暢,減少因圖片內存占用而引發的OOM。為什么說是5.0以下,因為在5.0以后系統默認就是存儲在Ashmem區了。

3.總結:
Picasso所能實現的功能,Glide都能做,無非是所需的設置不同。但是Picasso體積比起Glide小太多如果項目中網絡請求本身用的就是okhttp或者retrofit(本質還是okhttp),那么建議用Picasso,體積會小很多(Square全家桶的干活)。Glide的好處是大型的圖片流,比如gif、Video,如果你們是做美拍、愛拍這種視頻類應用,建議使用。
Fresco在5.0以下的內存優化非常好,代價就是體積也非常的大,按體積算Fresco>Glide>Picasso
不過在使用起來也有些不便(小建議:他只能用內置的一個ImageView來實現這些功能,用起來比較麻煩,我們通常是根據Fresco自己改改,直接使用他的Bitmap層)

------相關資料推薦
android 圖片自定義三級緩存實現以及原理、圖片錯位解決
android中圖片的三級cache策略(內存、文件、網絡)
Android高效加載大圖、多圖解決方案,有效避免程序OOM
Android 編程下圖片的內存優化
Android圖片內存優化的幾點心得
圖片優化實例: Android照片墻應用實現,再多的圖片也不怕崩潰
Android 三大圖片加載框架比較
Android的Glide庫加載圖片的用法及其與Picasso的對比

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容