Android Bitmap深入介紹(一)---基礎

來源:互聯網
上載者:User

標籤:

在Android應用開發中,我們經常需要跟圖片打交道,而圖片一個很麻煩的問題是佔用記憶體非常大,經常導致OOM,瞭解Bitmap相關資訊,不同sdk版本中Android圖片處理的變化,以及一些最佳化處理的方式對我們平時開發中對圖片的會非常有協助。

這篇先介紹Bitmap基礎內容,關於像素,儲存資訊,以及載入。

像素

Bitmap的儲存可以說包括兩個部分,像素以及長,寬,顏色等描述資訊。像素是Bitmap最佔用記憶體的地方,長寬和像素位元是用來描述圖片的,可以通過這些資訊計算出圖片的像素佔用的記憶體大小。具體到Bitmap的API是下面這幾個介面:

public final int getWidth()public final int getHeight()public final Config getConfig()

Config是一個枚舉類型,表示圖片像素類型,總共有下面幾種類型:ALPHA_8 (1),RGB_565 (3),ARGB_4444 (4),ARGB_8888 (5);。表示每一個像素圖片組成。實際上下面兩種方式擷取的數值是相等的:

int b = 1;switch (bitmap.getConfig()) {    case ALPHA_8:        b = 1;        break;    case ARGB_4444:        b = 2;        break;    case ARGB_8888:        b = 4;        break;}int bytes1 = bitmap.getWidth() * bitmap.getHeight() * b;int bytes2 = bitmap.getByteCount(); //從api12才有的介面

這是由Bitmap相關參數可以計算出Bitmap所佔用的像素數,實際上我們放入drawable裡面的圖片都是已經知道了圖片的長寬以及像素組成的,但是直接在Android外面算出的圖片像素數量與通過上面的代碼計算會有出入的。因為Android對圖片做了縮放,這個跟你將圖片放入的drawable位置相關。

我們都知道android資來源目錄中會有drawable-hdpi, drawable-xhdpi,drawable-xxhdpi等目錄。這裡每個目錄都會對應一個density。下面看BitmapFactory.decodeResource方法載入Bitmap的例子:

BitmapFactory.Options options = new BitmapFactory.Options();Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test4, options);Log.i(LOGTAG, "options: " + options.inDensity + "," + options.inTargetDensity);

decodeResource就是Android內部對Resource的載入方式,這裡就不從源碼上面一步一步介紹了,它最終會調用decodeResourceStream方法,直接看decodeResourceStream:

public static Bitmap decodeResourceStream(Resources res, TypedValue value,        InputStream is, Rect pad, Options opts) {    if (opts == null) {        opts = new Options();    }    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);}

options.inDensity表示圖片自身預設的像素密度,TypedValue會有一個density,對應著圖片來自於哪個drawable目錄,因為每一個drawable目錄(drawable-hdpi,drawable-xhdpi,drawable-xxhdpi)都對應著一種螢幕,而螢幕就有density,TypedValue的density對應著DisplayMetrics的densityDpi,densityDpi表示每英尺的像素數。options.inTargetDensity是當前手機螢幕對應的densityDpi,最終的像素數是:

bytes = 原始圖片寬*(options.inDensity/options.inTargetDensity)*原始圖片長*(options.inDensity/options.inTargetDensity)*每個像素點位元
儲存與傳輸

Android圖片在不同的sdk版本中儲存的地方是不一樣的。在2.3及2.3以前,圖片像素是儲存在native記憶體中。Android記憶體分為Java堆和native記憶體。Android會限制每個應用能使用的最大記憶體。但是Android對記憶體的限制是Java堆和native記憶體的和,把像素資料存放在native區,虛擬機器無法自動進行記憶體回收,必須手動使用bitmap.recycle()導致很容易記憶體流失。因為Android的裝置monitor也只能夠看到Java堆的記憶體變化,這樣其實也不方便調試Bitmap記憶體。比如在應用中新建立一個圖片,根本無法在monitor中看到記憶體變化。

從3.0開始Android將圖片儲存在Java堆中,新載入一張圖片的時候,也能夠立刻從monitor反映出來。另外Java的記憶體回收機制也能夠自動回收。然後在4.0後,圖片又有了一些變化,那就是在parcel傳輸的時候,當圖片很大時,它會使用ashmem來進行圖片的傳輸,具體可以看我這篇文章Android4.0之後Parcel傳輸Bitmap源碼分析。在6.0的時候,圖片的儲存又有了很大的變化,底層已經明顯增加了將圖片儲存ashmem的介面了,具體可以可以看我這篇文章Android6.0 Bitmap儲存以及Parcel傳輸源碼分析

BitmapFactory

BitmapFactory是用來載入圖片的,這個類主要分為三種圖片的載入,先把它的API拿出來看一下:

    public static Bitmap decodeResourceStream(Resources res, TypedValue value,            InputStream is, Rect pad, Options opts)     public static Bitmap decodeResource(Resources res, int id, Options opts)     public static Bitmap decodeResource(Resources res, int id)     public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {    public static Bitmap decodeByteArray(byte[] data, int offset, int length) {    public static Bitmap decodeFile(String pathName, Options opts)     public static Bitmap decodeFile(String pathName)    public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)     public static Bitmap decodeStream(InputStream is)     public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)    public static Bitmap decodeFileDescriptor(FileDescriptor fd)

我們直接看BitmapFactory提供的nativeDecode介面:

    private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,            Rect padding, Options opts);    private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,            Rect padding, Options opts);    private static native Bitmap nativeDecodeAsset(long nativeAsset, Rect padding, Options opts);    private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,            int length, Options opts);

BitmapFactory對File的decode都會轉換為InputStream採用nativeDecodeStream來decode,對Resource的decode會採用decodeAsset,而如果FileDesciptor可以轉換為native的fd,會通過nativeDecodeFileDescriptor來decode,另外ByteArray會直接採用nativeDecodeByteArray來decode。需要注意的是,對Resource的decode,BitmapFactory會設定Option的相關參數,最終進行相應的縮放,圖片的大小會跟原圖有所區別。 具體的內容建議去看看BitmapFactory,瞭解每種方式的區別,才能夠更好地使用介面,選擇的時候採用更有效率的方法。

BitmapFactory.Options

下面看一下Options類,我們在載入的時候,可以通過這個參數對圖片進行一些處理,前面已經說了inDensity和inTargetDensity。下面看看其他的參數。

inPurgeable

這個參數的用途是當需要使用Bitmap的時候再載入Bitmap,不需要的時候回收Bitmap。在4.1中,使用inPurgeable,載入圖片後記憶體基本不會增高,而不使用inPurgeable載入圖片後記憶體會有明顯的增加。

inSampleSize

這是表示採樣大小,長和寬會對應乘以1/inSampleSize。用於將圖片縮小載入出來的,以免站佔用太大記憶體,適合縮圖。

inJustDecodeBounds

這個設定了true後,用於擷取圖片的寬度長度資訊。下面是個例子:

BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;Bitmap bmp = BitmapFactory.decodeFile(path, options);// options.outWidth 和 options.outHeight就能夠擷取結果

這篇先主要介紹了Bitmap相關基本的資訊,Bitmap,BitmapFactory和Options類,以及bitmap的儲存。

Android Bitmap深入介紹(一)---基礎

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.