Android Bitmap圖片優化分析

圖片在移動開發中佔據中舉足輕重的地位,早期的android 應用頁面Ui相對簡單,但隨着Android系統不斷的升級發展, 界面元素越來越豐富,用戶對體驗要求越來越高,UI小姐姐們需要設計出精緻的界面元素,其中不乏很多好看的圖片,但是隨着手機性能提升(分辨率,cpu主頻,內存等),圖片質量也越來越大,拍個照動不動就3M,4M,8M, 大家都知道,android 應用在創建進程時候,會分配一個指定的內存大小,準確的說話是 google原生OS的默認值是16M,但是各個廠家的系統會對這個值進行修改,如果我們應用“毫不吝嗇”將這些大圖直接加載到內存中,很快內存就會耗盡,最終出現OOM異常,所以圖片的處理對於一個穩定、用戶體驗友好的應用來說非常重要,今天我們就來聊一聊Bitmap,在開發過程中把”圖片“給優化一番,保證我們項目在線上穩定、流暢運行。

Bitmap 初識

Bitmap圖像處理的最重要類之一,用它可以獲取圖像文件信息,進行圖像顏色變換、剪切、旋轉、縮放等操作,並可以指定格式保存圖像文件。

 

如圖,bitmap在sdk中算是元老級的人物了,從api1中就已經有了,可見其重要性。

繼承關係就不解釋了,實現了Parcelable 具備在內存中傳遞的特性。

bitmap中有兩個重要的內部類 CompressFormat 以及 Config;

下面分別介紹一下這兩個類

  • CompressFormat 

 CompressFormat 是用來設置壓縮方式的,是個枚舉類,內部提供了三種圖片壓縮方式類型,

  1. JPEG : 表示Bitmap採用JPEG壓縮算法進行壓縮,壓縮後的格式可以是.jpg或者.png,是一種有損壓縮方式。
  2. PNG : 表示Bitmap採用PNG壓縮算法進行壓縮,壓縮後的格式可以是.png,是一種無損壓縮方式。
  3. WEBP :表示以WebP壓縮算法進行圖像壓縮,壓縮後的格式可以是".webp",是一種有損壓縮,質量相同的情況下,WebP格式圖像的體積要比JPEG格式圖像小40%,美中不足的是,WebP格式圖像的編碼時間“比JPEG格式圖像長8倍”, 而且還需要注意,在官方文檔中有這樣的描述:As of Build.VERSION_CODES.Q, a value of 100 results in a file in the lossless WEBP format. Otherwise the file will be in the lossy WEBP format. 意爲Android10之後如果quality值(壓縮質量)爲100的話,bitmap壓縮採用無損壓縮格式,其他都爲有損壓縮;

這裏有的同志會問,這都是壓縮格式啊,具體怎麼操作壓縮呢,Bitmap爲我們提供了一個可靠的方法供開發者使用,我們來順便看看Bitmap都有什麼方法,如下:

第一個方法就是compress()方法, 沒錯就是這麼就這方法,一共有三個參數

  1. format :👆上面已經說明了,表示壓縮格式;
  2. quality : 壓縮質量,取值0-100,0表示最低畫質壓縮,100表示最高畫質壓縮,對於PNG壓縮格式來說,該參數可以忽略,對於WEBP格式來說,小於100爲有損壓縮格式,會對畫質產生直接影響, 等於100時候採用的是無損壓縮格式,畫質是不會有改變,但是圖片大小得到很好壓縮;
  3. stream : 將壓縮後的圖片寫到指定的輸出流中;

返回值:boolean, 返回true表示成功將bitmap壓縮到輸出流中,然後可以通過Bitmap.Factory從相應的輸入流中解析出來bitmap信息;

從官網介紹可知, 該方法在圖片壓縮過程中可能消耗較長時間,建議放在子線程中操作,至於爲什麼大家可以看看源碼, 源碼中會調用一個nativeCompress 的Native 方法,也就是壓縮處理是放在底層處理的;

 

  • Config

表示位圖像素的存儲格式,什麼意思呢?  就是bitmap在屏幕上顯示的每一像素在內存中存儲的格式,會影響Bitmap真實圖片的透明度以及圖片質量;

  1. Bitmap.Config.ALPHA_8:顏色信息只由透明度組成,佔8位;
  2. Bitmap.Config.ARGB_4444:顏色信息由透明度與R(Red),G(Green),B(Blue)四部分組成,每個部分都佔4位,總共佔16位;

  3. Bitmap.Config.ARGB_8888:顏色信息由透明度與R(Red),G(Green),B(Blue)四部分組成,每個部分都佔8位,總共佔32位,是Bitmap 默認的顏色存儲格式,也是最佔空間的一種配置;

  4. Bitmap.Config.RGB_565:顏色信息由R(Red),G(Green),B(Blue)三部分組成,R佔5位,G佔6位,B佔5位,總共佔16位;

上面說了 android 系統默認存儲位圖方式是 ARGB_8888, 4個通道組成,每個通道8位,分表代表透明度和RGB顏色值, 也就是說一個位圖像素佔用了4個字節(1個byte8個bit位),

同理:採用 Bitmap.Config.RGB_565 存儲,單像素佔用內存大小僅有2byte,換句話說一張圖片採用ARGB_565格式相對於默認的ARGB_8888內存將減少一半,所以通過改變bitmap像素存儲方式也是圖片內存優化的重要渠道,這個後面會講到;

BitmapFactory 

創建位圖bitmap對象途徑有很多種, 包括指定文件、流, 和字節數組等;

官方文檔中提供了從字節數組、指定路徑,系統Resource、二進制流等方式創建Bitmap, 當然有的方法需要一些特殊參數,例如通過字節數組方式需要指定解析的起始偏移位置,長度等,有的需要指定路徑 path , 或者指定 BitmapFactory.Option配置信息 , 它也是我們圖片優化的重要手段;

BitmapFactort.Options這個是什麼鬼呢,  很重要! bitmap加載的配置類,想要做圖片內存優化是少不了跟它打“打交道”,如下其內部屬性

這裏我們大概只說跟圖片優化相關的幾個重要屬性

  • insampleSize :採樣率,默認1表示無縮放,等於2表示寬高縮放2倍,總大小縮小4倍;
  • inBitmap  :被複用的bitmap;
  • inJustDecodeBound : 如果設置爲true,不獲取圖片,不分配內存,但會返回圖片的高度寬度信息;
  • inMutable :是否圖片內容可變,如果Bitmap複用的話,需要設置爲true;
  • inDensity : 加載bitmap時候對應的像素密度(後面會講到);
  • inTargetDensity : bitmap位圖被真實渲染出的像素密度,對應終端設備的屏幕像素密度(後面會講到);

好了,Bitmap的api我們就講到這裏,因爲我們今天不是主要講解他的用法,爲了給接下來的知識做一個鋪墊,簡單介紹bitmap的知識點,我們接下來回歸”正題“

Bitmap 佔用內存分析 

Bitmap 用來描述一張圖片的長、寬、顏色等信息。通常情況下,我們可以使用 BitmapFactory 來將某一路徑下的圖片解析爲 Bitmap 對象。

當一張圖片加載到內存後,具體需要佔用多大內存呢?

  • getAllocationByteCount 
  • getByteCount
  • getRowBytes

這三個方法是什麼意思呢? 跟內存佔用又有什麼關係呢,下面我們分別解釋一下這三個方法

先一下這張圖

上圖中 是保存在 res/drawable-mdpi 目錄下的一張 1920*1200,大小爲 270Kb 的圖片

爲什麼讓你看這張圖呢?  因爲眼睛👀看累了,順便。。。  不是的,注意上面紅色框中原始圖片大小和尺寸,爲後面壓縮設定主題;

 我們分別通過 Bitmap.getAllocationByteCount() 以及 Bitmap.getByteCount()和Bitmap.getRowBytes() 方法獲取 該Bitmap 的相關字節大小,比如以下代碼:

 

打印結果如下

2020-05-23 10:20:10.926 7669-7669/com.example.practicerecycerviewitemdecoration D/AAAA: density1:1.5 densityDpi:240
2020-05-23 10:21:52.422 7669-7669/com.example.practicerecycerviewitemdecoration D/AAAAA:  height:1800 
     width:2880 
     allocationByteCount:20736000 
     byteCount:20736000 
     rowBytes:11520 
     density:240 
     mutable:false 

大家看到 allocationByteCount = byteCount = 20736000 爲什麼呢?  兩者又有什麼差距呢?  

這裏我們看看官方文檔怎麼說的:

該方法在api19 之後加入的,用來返回一個存儲Bitmpa像素信息的內存大小, 什麼意思呢? 就是爲Bitmpa分配的內存大小而已, 它跟getByteCount有什麼關係呢?  文檔上有說明,一般情況下這兩個值相等,當bitmap用來複用存儲另外一個比原bitmap大小更小一點圖片時候getAllocationByteCount是大於getByteCount的值,換句話說通過複用Bitmap來解碼圖片,如果被複用的Bitmap的內存比待分配內存的Bitmap大,那麼getByteCount()表示新解碼圖片佔用內存的大小(並非實際內存大小,實際大小是複用的那個Bitmap的大小),getAllocationByteCount()表示被複用Bitmap真實佔用的內存大小。

而且在api19之後系統推薦使用getAllocationByteCount,看源碼

所以上面日誌信息兩者相等是成立,那這兩者又跟getRowBytes()有什麼關係呢? 我們接着往下看,當我打開getByteCount的源碼你就瞬間明白了

getByteCunt內存大小其實就是一行像素所佔據字節大小 *  Bitmap高度 

我們可以驗證一下: 

11520 * 1800 = 20736000 

結算結果非常準確,沒有任何偏差,大小類似理解一個矩形面積等於長*寬一樣 ,  getRowBytes代表就是該bitmap一行像素所佔據的內存大小,然後再乘以高度就是整張bitmap所佔用內存; 

或許有的朋友又會問,那getRowBytes大小怎麼來的呢? 總得給個解釋吧, 剛纔上面解釋了,它代表了bitmap一行的像素內存,這又什麼意思呢? 一行像素所佔用內存=bitmap寬度 * 1像素所佔字節大小 ,計算如下

2880 * 4 =  11520 

計算結果同樣沒有任何偏差,此時大家是不是似乎明白了一些什麼, 我這裏是根據 bitmap 內存相關api 從內到外跟大家分析內存佔用, 最終得出結論

Bitmap佔用內存= 寬 * 高 * 一像素所佔用字節內存 ,如下

2880 * 1800 * 4 = 20736000 

可能有的同志發現了,內存中bitmap圖片高度、寬度跟原始圖片寬高不一樣,這是爲什麼呢?

是的,確實不一樣,這裏有個細節知識點,我們上面在講Bitmap相關api時候也提到過inDensity和InTargetDensity,我這裏先說出結論,然後在帶大家從源碼角度上找答案; 

實際上 BitmapFactory 在解析圖片的過程中,會根據當前設備屏幕密度和圖片所在的 drawable 目錄來做一個對比,根據這個對比值進行縮放操作。具體公式爲如下所示:

縮放比例 scale = 當前設備屏幕密度 / 圖片所在 drawable 目錄對應屏幕密度 

Bitmap 實際佔用內存 = 寬 * scale * 高 * scale * 一像素所佔用字節內存,在 Android 中,各個 drawable 目錄對應的屏幕密度分別爲下:

111.png

在回頭看我們上面那個問題,爲什麼圖片原始寬高跟bitmap寬高不等,從我們打印的日誌可知我們設備density=1.5 densityDpi=240,而圖片放在drawable-mdpi , 該bitmap的desityDpi爲160 ,

 bitmap 真實高= 1.5 (設備densityDpi 240/圖片所在drawable的densityDpi 160 ) * 1200 = 1800

 

bitmap 真實寬= 1.5 (設備densityDpi 240/圖片所在drawable的densityDpi 160 ) * 1920 = 2800

同樣結果非常準確,也就是說明我們Bitmap內存大小除了跟我們圖片寬高有關係、Bitmap.Config 以及 縮放比,而縮放比大小取決於 設備屏幕密度和圖片所在drawable對應密度。

如果我們把圖片放到drawable-hdpi下面,bitmap內存大小會有變化麼?  是變大了還是變小了? 

我是打印一下日誌試一下, 然後再根據上面那個規則驗證一下結果,打印如下

2020-05-23 12:01:45.358 9182-9182/com.example.practicerecycerviewitemdecoration D/AAAA: density1:1.5 densityDpi:240
2020-05-23 12:01:47.018 9182-9182/com.example.practicerecycerviewitemdecoration D/AAAAA:  height:1200 
     width:1920 
     allocationByteCount:9216000 
     byteCount:9216000 
     rowBytes:7680 
     density:240 
     mutable:false 

Bitmap的寬高等於原始寬高,內存大小 9216000 ,原因就是圖片的drawable的densityDpi變化了,根據公式大小計算

1920 * 240/240 * 1200 * 240/240  * 4 = 9216000

9216000/20736000 = 0.44..... 把圖片放到mdpi下比在hdpi內存多消耗了60% 左右,

由此可見,我們在進行圖片適配時候要準備多張圖片放到不同drawable目錄下,一方面保證了我們圖片在各設備下的顯示效果一致,另一方面系統加載適合的bitmap可以節省非常多內存空間,試想一下如果我們設備是640 Dpi的呢? 而我們只准備了一張圖片放在mdpi或者hdpi中,那麼我們這張圖片會消耗多大內存呀!!!  

講了這麼多,Bitmap 佔用內存大小我們已經總結出來了,那我們再看看源碼驗證一把,前面我們講過BitmapFactory 解析Bitmap 相關api, 如:

public static Bitmap decodeResource(Resources res, int id, Options opts) {   
         validate(opts);   
         Bitmap bm = null;    
         InputStream is = null;       
          try {       
            final TypedValue value = new TypedValue();        
            is = res.openRawResource(id, value);       
            bm = decodeResourceStream(res, value, is, null, opts);   
          } catch (Exception e) {     
             /*  do nothing. If the exception happened on open, bm will be null.   If it happened on close, bm is still valid.        */  
         } finally {             try {          
               if (is != null) is.close();  
           } catch (IOException e) {            // Ignore  
      }    }    
  if (bm == null && opts != null && opts.inBitmap != null) {           throw new IllegalArgumentException("Problem decoding into existing bitmap");      }    
   return bm;
}

繼續跟進decodeResourceStream裏面(這裏就截圖吧)

如果option爲空就重新new一個出來,如果TypeValue不爲空取出TypeValue的density信息,TypeValue是Resource解析對應資源時候的結果封裝,這裏就不詳細解釋了,大家可以自己學習一下, 從resource裏面讀取到圖片信息後,包括該圖片所在的drawable對一個的dpi,也就是TypeValue裏面的density值,如果這個值爲0的話此時就會用到系統的 認 DENSITY_DEFAULT,也就是這個值 

public static final int DENSITY_DEFAULT = DENSITY_MEDIUM;

public static final int DENSITY_MEDIUM = 160;

而inTargetDesity 大小爲設備的屏幕密度 densityDpi

有了這兩個值我們就可以計算bitmap的大小了, 我們接着看 decodeStream , 最終會跟到nativeDecodeStream 中, 很明顯這是個native方法,因此我們知道Bitmap的內存計算其實是放在 native層做的, 那麼我直接貼出native 層處理的代碼吧,

static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) { //初始縮放係數float scale = 1.0f; 
        if (env->GetBooleanField(options, gOptions_scaledFieldID)) { 
            const int density = env->GetIntField(options, gOptions_densityFieldID); 
            const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID); 
            const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
            if (density != 0 && targetDensity != 0 && density != screenDensity) { //縮放係數是當前係數密度/圖片所在文件夾對應的密度; 
                scale = (float) targetDensity / density; 
             } 
         } 
        //原始解碼出來的Bitmap; SkBitmap decodingBitmap; 
      if(decoder->decode(stream, &decodingBitmap, prefColorType, decodeMode) != SkImageDecoder::kSuccess) { 
         return nullObjectReturn("decoder->decode returned false"); } 
         //原始解碼出來的Bitmap的寬高; 
         int scaledWidth = decodingBitmap.width(); 
         int scaledHeight = decodingBitmap.height(); 
         //要使用縮放係數進行縮放,縮放後的寬高; 
         if(willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) { 
             scaledWidth = int(scaledWidth * scale + 0.5f); 
             scaledHeight = int(scaledHeight * scale + 0.5f); 
         } 
         //源碼解釋爲因爲歷史原因;sx、sy基本等於scale。 
        const float sx = scaledWidth / float(decodingBitmap.width()); 
        const float sy = scaledHeight / float(decodingBitmap.height()); 
        canvas.scale(sx, sy); canvas.drawARGB(0x00, 0x00, 0x00, 0x00); 
        canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint); 
        // now create the java bitmap 
        return GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset(), bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);

native層計算如上處理,先獲取圖片原始寬高,根據decodeMode計算出縮放係數, 最後對canvas進行縮放,最後將Bitmap畫出來從而完成Bitmap的加載操作, 如果看到這裏大家基本已經瞭解Bitpmap加載到內存的流程和底層縮放策略了,不要停!繼續聊,關於bitmap的優化還沒開始講。。。

  • assets 中的圖片大小 

我們知道,Android 中的圖片不僅可以保存在 drawable 目錄中,還可以保存在 assets 目錄下,然後通過 AssetManager 獲取圖片的輸入流。那這種方式加載生成的 Bitmap 是多大呢?同樣是上面的 girl.png,這次將它放到 assets 目錄中,使用如下代碼加載:

 

最終打印結果如下:

2020-05-23 14:32:33.799 11603-11603/com.example.practicerecycerviewitemdecoration D/AAAA: density1:1.5 densityDpi:240
2020-05-23 14:32:35.335 11603-11603/com.example.practicerecycerviewitemdecoration D/AAAAA:  height:1200 
     width:1920 
     allocationByteCount:9216000 
     byteCount:9216000 
     rowBytes:7680 
     density:240 
     mutable:false 

可以看出,加載 assets 目錄中的圖片,系統並不會對其進行縮放操作。

Bitmap 加載優化 上面的例子也能看出,一張 270Kb 大小的圖片被加載到內存後,竟然佔用了 9216000 個字節,也就是 9M 左右。因此適當時候,我們需要對需要加載的圖片進行縮略優化。

修改圖片加載的 Config

修改佔用空間少的存儲方式可以快速有效降低圖片佔用內存。比如通過 BitmapFactory.Options 的 inPreferredConfig 選項,將存儲方式設置爲 Bitmap.Config.RGB_565。這種存儲方式一個像素佔用 2 個字節,所以最終佔用內存直接減半。如下:

 

打印日誌如下:

2020-05-23 14:37:06.213 11949-11949/com.example.practicerecycerviewitemdecoration D/AAAA: density1:1.5 densityDpi:240
2020-05-23 14:37:07.047 11949-11949/com.example.practicerecycerviewitemdecoration D/AAAAA:  height:1200 
     width:1920 
     allocationByteCount:4608000  // 相比9216000減少一半的內存
     byteCount:4608000 
     rowBytes:3840 
     density:240 
     mutable:false 

這個結論我們在介紹Bitmap 的 Config時候已經介紹過了,這裏不多說了;

另外 Options 中還有一個 inSampleSize 參數,可以實現 Bitmap 採樣壓縮,這個參數的含義是寬高維度上每隔 inSampleSize 個像素進行一次採集。比如以下代碼:

因爲寬高都會進行採樣,所以最終圖片會被縮略 4 倍,最終打印效果如下:

2020-05-23 14:42:59.440 12182-12182/com.example.practicerecycerviewitemdecoration D/AAAA: density1:1.5 densityDpi:240
2020-05-23 14:43:00.332 12182-12182/com.example.practicerecycerviewitemdecoration D/AAAAA:  height:600 
     width:960 
     allocationByteCount:2304000 // 爲9216000的1/4,極大降低了內存
     byteCount:2304000 
     rowBytes:3840 
     density:240 
     mutable:false 

Bitmap 複用 

場景描述 

如果在 Android 某個頁面創建很多個 Bitmap,比如有兩張圖片 A 和 B,通過點擊某一按鈕需要在 ImageView 上切換顯示這兩張圖片,

可以使用以下代碼實現上述效果:

但是在每次調用 switchImage 切換圖片時,都需要通過 BitmapFactory 創建一個新的 Bitmap 對象。當方法執行完畢後,這個 Bitmap 又會被 GC 回收,這就造成不斷地創建和銷燬比較大的內存對象,從而導致頻繁 GC(或者叫內存抖動)。像 Android App 這種面相最終用戶交互的產品,如果因爲頻繁的 GC 造成 UI 界面卡頓,還是會影響到用戶體驗的。可以在 Android Studio Profiler 中查看內存情況,多次切換圖片後,顯示的效果如下:

 

  • 使用 Options.inBitmap 優化

實際上經過第一次顯示之後,內存中已經存在了一個 Bitmap 對象。每次切換圖片只是顯示的內容不一樣,我們可以重複利用已經佔用內存的 Bitmap 空間,這個概念上面也講過,這裏具體做法就是使用 Options.inBitmap 參數。修改如下:

解釋說明:

第一個紅框處創建一個可以用來複用的 Bitmap 對象。 第二處紅框,將 options.inBitmap 賦值爲之前創建的bitmap 對象,從而避免重新分配內存。 重新運行代碼,並查看 Profiler 中的內存情況,可以發現不管我們切換圖片多少次,內存佔用基本處於一個水平線狀態。

我們再來看日誌:

2020-05-23 15:33:46.515 21739-21739/com.example.practicerecycerviewitemdecoration D/AAAAA:  height:1200 
     width:1920 
     allocationByteCount:9216000 
     byteCount:9216000 
     rowBytes:7680 
     density:240 
     mutable:true 
2020-05-23 15:34:09.031 21739-21739/com.example.practicerecycerviewitemdecoration D/AAAAA:  height:322 
     width:640 
     allocationByteCount:9216000 
     byteCount:824320 
     rowBytes:2560 
     density:240 
     mutable:true 

第二張圖片內存明顯複用了第一張Bitmap內存大小 9216000,而第二張圖片byteCount大小爲824320,而不等於allocationByteCoung大小,就在文章開頭我們也講解過getAllocationByteCoun和getByteCount的區別,很好的解釋了這個結果;

但是這裏需要注意,我們第一張Bitmap比第二張Bitmap大,如果第一張Bitmap比第二章小的話,這裏就不能複用了,前面我們也是提到過的,否則會直接崩潰掉, 如下:

我們默認在onResume裏面 imageView?.setImageBitmap(bitmap) 此時這個bitmap是上圖image【1】對應的Bitmap,他的內存分配上面也打印過爲 824320 ,然後點擊切換時候我們就複用這個Bitmap內存,將image【0】內容再填充到這個bitmap中,我們試着運行一下結果發現

直接給我們拋出異常了, 我們在decordResource源碼中找到答案了,如下

如果bm爲空,而且開啓了bitmap複用,這裏就會崩掉。。。 

這是因爲 Bitmap 的複用有一定的限制:

在 Android 4.4 版本之前,只能重用相同大小的 Bitmap 內存區域, 4.4 之後你可以重用任何 Bitmap 的內存區域,只要這塊內存比將要分配內存的 bitmap 大就可以。

那麼我們需要做一下處理了,如下

第一步:先初始化一個bitmap,這個bitmap我用的是bitmap[1]中的 加載到內存後的大小爲824320 ;

第二步:取第一張圖片也就是image[0],實際內存大小爲9216000,由於我們把inJustDecodeBound=true 此時並沒有正真加載到內存中,爲了獲取該bitmap配置信息;

第三步:判斷bitmap 能否複用, 方法如下

獲取option中的預加載bitmap的大小,然後根據位圖存儲格式計算預加載的bitmap大小,最後返回比較結果, 這裏默認採用ARGB_8888所以✖️4;

如果預加載的bitmap所佔內存大小<=被複用bitmap大小,

option.InMutable=true;  
option.InBitmap=bitmap

最後一步:再次加載bitmap並實現複用;

細心的你可能也發現了在每次加載之前,除了 inBitmap 參數之外,我還將 Options.inMutable 置爲 true,這裏如果不置爲 true 的話,BitmapFactory 將不會重複利用 Bitmap 內存,並輸出相應 warning 日誌:

W/BitmapFactory: Unable to reuse an immutable bitmap as an image decoder target

Bitmap 緩存

 當需要在界面上同時展示一大堆圖片的時候,比如 ListView、RecyclerView 等,由於用戶不斷地上下滑動,某個 Bitmap 可能會被短時間內加載並銷燬多次。這種情況下通過使用適當的緩存,可以有效地減緩 GC 頻率保證圖片加載效率,提高界面的響應速度和流暢性。

最常用的緩存方式就是 LruCache,基本使用方式如下:

解釋說明:

圖中 指定 LruCache 的最大空間爲 20M,當超過 20M 時,LruCache 會根據內部緩存策略將多餘 Bitmap 移除。 

圖中 sizeOf () 方法指定了插入 Bitmap 時的大小,當我們向 LruCache 中插入數據時,LruCache 並不知道每一個對象會佔用大多內存,因此需要我們手動指定,並且根據緩存數據的類型不同也會有不同的計算方式。 

最後就是我們存取操作了。

上面就是今天的內容,講解類Bitmap的相關基礎知識點和優化,Bitmap實際問題的處理遠不止這麼多,像截屏長圖的處理,如果不處理這張”超大圖“,應用很容易就崩掉,這裏需要用到分片加載, 這裏不多說了,大家可自行查閱官方文檔學習一下。 

如果大家對這篇文章感興趣,歡迎收藏點贊!!!

或者扣扣掃碼加入粉絲裙領取福利。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章