使用BitmapFactory不同方法解析Bitmap的簡單分析

使用BitmapFactory不同方法解析Bitmap的簡單分析

相信大家在平時進行Android的開發過程中,都有使用過Bitmap。

其實Google自己也有一個簡單介紹Bitmap如何使用的文章,Manage Bitmap Memory。在這裏對一些簡單的Bitmap內存優化做了介紹。

其實我寫這篇文章的初衷是由於最近在寫一個涉及到很多張Bitmap顯示的控件,但是由於圖片過多,假如完全顯示會導致OOM,因此需要建立了一個圖片緩存,

現在我手上有800多張136px*128px大小的圖片,我現在分別通過將圖片放在磁盤文件,assets目錄,drawable的各個目錄下,來測試通過BitmapFactory創建圖片的性能。

測試平臺爲:Genymotion 模擬機上的 Preview-GoogleNexus6-5.1.0-API22-1440x2560。

對圖片混存的最大限制爲當前應用程序的 1/8 最大內存。

具體讀取Bitmap的代碼就不再放上來了,反正就是使用BitmapFactory的一些方法去讀取,也挺簡單的。

測試結果如下:

性能點 File Assets drawable drawable-ldpi drawable-mdpi
讀取時間總耗時 1360 406 2550 4100 2545
相同內存最大圖片數 181 181 15 9 15
生成圖片尺寸 136*128 136*128 476*448 635*597 476*448
drawalbe-hdpi drawable-xhdpi drawable-xxhdpi drawable-xxxhdpi
讀取時間總耗時 1479 1132 940 619
相同內存最大圖片數 34 60 134 237
生成圖片尺寸 317*299 238*224 159*149 119*112

從上表中可以看出,對於同一套圖片而言,從file,assets以及drawable下所取出的大小內存,以及消耗時間均不相同,總的來說從Assets目錄下獲取速度最快,消耗的內存相對較少。

而從drawable各目錄下取出的圖片隨着屏幕像素的遞減,其尺寸越大,佔用內存越大,讀取耗時越多。

我們仔細觀察BitmapFactory中各個方法,我們會發現,不論是decodeResource ,decodeFile,他們最後都會調用decodeStream這個方法來獲取Bitmap對象。

對於從文件和assets對象中獲取bitmap而言,他們之間唯一的區別就是一個是從本地磁盤上讀取,一個是從assets中讀取,那麼對一個應用而言,讀取自身的速度會快一些,我們也可以理解。

而對於drawable目錄裏的圖片而言,爲什麼他們讀取出來的圖片尺寸會不一樣呢?

我記得在Android開發者官網上,Google曾經說過

If your application’s minSdkVersion is 4 or greater, you do not need default drawable resources when you provide alternative drawable resources with the screen density qualifier. Even without default drawable resources, Android can find the best match among the alternative screen densities and scale the bitmaps as necessary. However, for the best experience on all types of devices, you should provide alternative drawables for all three types of density.

這句話的大概意思就是對於最低sdk版本爲4的應用而言,假如我們在某一個drawable路徑下已經提供了一個drawable資源,那我們就不需要在默認的drawable目錄下提供默認資源(要知道類似於values,layout這些xml文件,必須要有一個默認的配置項,而drawable只要你有一個就夠了)。Android系統會自動找到最匹配的想,然後對找到的bitmap進行縮放,以供使用。

因此我們可以推斷出之所以放置在各個drawable目錄下導致生成的bitmap尺寸不同就是因爲Android系統爲我們進行了自動縮放。

接下來我們來看看Android是如何實現bitmap資源自動縮放的。

先從Java層的代碼入手,我們發現在BitmapFactory中,decodeResource獲取Bitmap的代碼如下:

final TypedValue value = new TypedValue();
is = res.openRawResource(id, value);
bm = decodeResourceStream(res, value, is, null, opts);

在deoceResourceStream中代碼如下:

if (opts.inDensity == 0 && value != null) {
    final int density = value.density;
    if (density == TypedValue.DENSITY_DEFAULT) {
        opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
    } else if (density != TypedValue.DENSITY_NONE) {
        opts.inDensity = density;
    }
}

if (opts.inTargetDensity == 0 && res != null) {
    opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
}

return decodeStream(is, pad, opts);

在這裏我們也可看出來,Android首先從TypeValue中獲取到了最匹配的drawable對象的屏幕像素密度,然後將drawable對象的屏幕像素同設備自身的屏幕像素密度傳入opts對象中,再調取decodeStream方法,進行最後的bitmap解析。

因此Android通過BitmapFactory解析drawable中的圖片文件時其實會按照圖片所處文件夾的圖片像素密度同現有設備的像素密度進行縮放。

根據bitmap的定義,長寬尺寸越大,其佔用內存也會成平方級數的增長,因此就會發現對於同一張圖片,放置圖片的drawable文件夾的屏幕像素越低,其在同一設備上佔用的內存就會越大。(Ps:真實使用的時候,放置在不同drawable文件夾下的相同圖片的尺寸也必定不同,故這其實沒有什麼大問題。)

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