Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Bitmap深入介紹(一)---基礎

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

編輯:關於Android編程

在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來進行圖片的傳輸。在6.0的時候,圖片的存儲又有了很大的變化,底層已經明顯增加了將圖片保存ashmem的接口了

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的存儲。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved