Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android異步加載全解析之Bitmap

Android異步加載全解析之Bitmap

編輯:關於Android編程

Android異步加載全解析之Bitmap

在這篇文章中,我們分析了Android在對大圖處理時的一些策略——Android異步加載全解析之大圖處理 戳我戳我

那麼在這篇中,我們來對圖像——Bitmap進行一個更加細致的分析,掌握Bitmap的點點滴滴。

 

引入

Bitmap這玩意兒號稱Android App頭號殺手,特別是3.0之前的版本,簡直就是皇帝般的存在,碰不得、摔不得。雖然後面的版本Android對Bitmap的管理也進行了一系列的優化,但是它依然是非常難處理的一個東西。在Android異步加載中,很多時候,訪問網絡比較慢,慢的也就是圖片了,文字刷一下就出來了,但是圖片卻始終不出來,這讓很多看&**&%&)*&圖片的人,非常郁悶啊。但沒辦法,大家都喜歡看圖片,所以還是得好好搞搞圖像。

Bitmap內存

在曾經的版本中,也就是Android2.3時代,Bitmap的對象引用與它真實的像素數據其實是分開存儲的,Bitmap的對象引用存儲在Dalvik虛擬機堆內存heap中,但像素數據保存在系統內存中,我們需要調用recycle()方法來釋放像素信息的內存,但是如果你釋放之後又使用了Bitmap,那程序就會FC了。 進化到Android3.0之後,Bitmap的對象引用與像素數據都保存到Dalvik虛擬機堆內存heap中了。這樣,Bitmap內存資源的回收就完全交給GC了,所以這樣可以避免非常多的OOM問題,而且,在2.3時代,GC是單線程的,一回收就卡界面,而到3.0之後,GC就改為並發執行的了,不會在阻塞工作線程。 雖然系統給我們做了大量幕後的事,但是由於各種使用Bitmap的代碼問題導致的OOM同樣會讓程序FC,所以我們在使用Bitmap的時候,同樣還是非常需要注意的。例如避免內存洩露、內存溢出,當然,你也可以在mainifest文件的application中增加android:largeHeap = “true”來讓系統給你特殊照顧,使用更多的內存空間,但是,這樣畢竟不是解決問題的方法,只能作為保命技能來使用,切記、切記!

Android異步加載全解析之大圖處理

這個已經不用再講了,請參考 戳我戳我

Android異步加載全解析之引入緩存

緩存是非常重要的一個優化Bitmap存儲策略的方法,這個也不用再講了,請參考 先戳我,再戳我 不過這裡有幾點需要再提下,不管是LruCache還是DiskLruCache,設置緩存的大小,都不是一概而論的,需要根據項目中Bitmap的使用頻率、Bitmap像素大小等值來進行綜合判斷。

SoftReference和BitmapFactory.Options.inBitmap參數

從Android3.0開始,引入了BitmapFactory.Options.inBitmap,這果這個字段被設置了,則解碼時會把重用這個字段所引用的那張Bitmap,避免重新分配內存。但使用這個屬性時有一些條件:
1、重用的Bitmap和即將被解碼的Bitmap必須是相同的尺寸,且是JPEG或者PNG格式的。
2、 BitmapFactory.Options.inPreferredConfig字段設置無效,因為會被重用的Bitmap的configuration所覆蓋。
3、一定要使用解碼方法返回的Bitmap,因為重用可能會失敗。4、Bitmap一定要是可變的,即inmutable設置一定為ture,記住,加載進來的原圖,都是不可修改的。
另外,在Android 3.0 (API Level 11),及以上的版本中,當Bitmap被從LruCache中擠出時,我們可以把這個Bitmap的soft reference存起來當作以後解碼時的inBitmap來使用。 這種方式你可以在開發者網站上找到詳細的示例,我就不拿出來裝比拉~~ 開發者必備利器——http://developer.android.com/training/displaying-bitmaps/manage-memory.html
這裡我們對官網的Demo再做一點進一步的解釋。 我們在使用緩存來優化Bitmap的時候,使用到了LruCache,而LruCache中有這樣一個方法——entryRemoved,按照文檔給出的說法,在LruCache容器滿了需要淘汰存放其中的對象騰出空間的時候會調用此方法不過需要注意的是,這裡只是對象被淘汰出LruCache容器,但並不意味著對象的內存會立即被Dalvik虛擬機回收掉。這個時候,我們可以在此方法中將Bitmap使用SoftReference包裹起來,並用事先准備好的一個HashSet容器來存放這些即將被回收的Bitmap。 當滿足上面我們列舉出來的條件的時候,系統對圖片進行decoder的時候會檢查內存中是否有可復用的Bitmap,避免我們頻繁的去SD卡上加載圖片而造成系統性能的下降,畢竟SD卡比內存速度慢太多了。 剩下的大家就使用官網的Demo就OK了,好了,裝比結束。
再PS下,我們通常在使用decodeStream、decodeFile來解析獲取到的流信息,例如:
URL url = new URL(urlString);  
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
        is = new BufferedInputStream(conn.getInputStream());  
        bitmap = BitmapFactory.decodeStream(is);  

其實,系統還給我們提供了一個更加高效的方法——decodeFileDescriptor,它為啥高效呢,其實是一個歷史原因,大家都知道,C比Java略快,所以,decodeFileDescriptor比decodeStream略快,就是因為decodeFileDescriptor在源碼中是使用底層庫來解析的,例如:
FileInputStream is = = new FileInputStream(path);
bmp = BitmapFactory.decodeFileDescriptor(is.getFD(), null, opts);
因此,在調用DiskLruCache來使用的時候,這個方法就可以比decodeFile更快。這裡我們也提供一個完整的調用代碼:
public static OutputStream decodeBitmap(String path) {
 
        BitmapFactory.Options opts = new BitmapFactory.Options();
        opts.inJustDecodeBounds = true;// 設置成了true,不占用內存,只獲取bitmap寬高
        BitmapFactory.decodeFile(path, opts);
        opts.inSampleSize = computeSampleSize(opts, -1, 1024 * 800);
 
        opts.inJustDecodeBounds = false;// 這裡一定要將其設置回false,因為之前我們將其設置成了true
        opts.inPurgeable = true;
        opts.inInputShareable = true;
        opts.inDither = false;
        opts.inPurgeable = true;
        opts.inTempStorage = new byte[16 * 1024];
        FileInputStream is = null;
        Bitmap bmp = null;
        InputStream ins = null;
        ByteArrayOutputStream baos = null;
        try {
            is = new FileInputStream(path);
            bmp = BitmapFactory.decodeFileDescriptor(is.getFD(), null, opts);           
            double scale = getScaling(opts.outWidth * opts.outHeight, 1024 * 600);
            Bitmap bmp2 = Bitmap.createScaledBitmap(bmp,
                    (int) (opts.outWidth * scale),
                    (int) (opts.outHeight * scale), true);
            bmp.recycle();
            baos = new ByteArrayOutputStream();
            bmp2.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            bmp2.recycle();
            return baos;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                ins.close();
                baos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.gc();
        }
        return baos;
    }
 
private static double getScaling(int src, int des) {
/**
 * 目標尺寸÷原尺寸 sqrt開方,得出寬高百分比
 */
    double scale = Math.sqrt((double) des / (double) src);
    return scale;
}

Bitmap二次采樣技術

請原諒我有一次裝比了,其實二次采樣技術,聽上去很牛逼,實際上就是利用我們在《大圖處理》中使用的方法 戳我戳我 也就是inSampleSize,當我們加載大圖的時候,可以先快速的加載一張質量非常低的縮略圖,等下載原圖完畢後,再加載。

Bitmap圖像壓縮

大小壓縮

通過inSampleSize方法,其實我們就已經實現了對Bitmap的壓縮,不過這種壓縮是基於大小來進行壓縮的,由於前面我們已經講過了,這裡就不再說了。

質量壓縮

下面我們介紹一種基於質量的壓縮方法,當然,這種方式也是API——compress方法,所以也不需要太多算法。
private Bitmap compressImage(Bitmap bitmap) {
	ByteArrayOutputStream baos = new ByteArrayOutputStream();
	//質量壓縮方法,這裡100表示不壓縮,把壓縮後的數據存放到baos中
	bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
	int options = 100;
	//循環判斷如果壓縮後圖片是否大於100kb,大於繼續壓縮
	while (baos.toByteArray().length / 1024 > 100) {
		//重置baos即清空baos
		baos.reset();
		//這裡壓縮options%,把壓縮後的數據存放到baos中
		bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);
		//每次都減少10
		options -= 10;
	}
	//把壓縮後的數據baos存放到ByteArrayInputStream中
	ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
	//把ByteArrayInputStream數據生成圖片
	Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);
	return bitmap;
}

 






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