Android ListView 圖片非同步載入和圖片記憶體緩衝

來源:互聯網
上載者:User

中間偷懶了,好久沒有寫部落格了,今天寫一下我研究了好幾天的Android ListView 圖片非同步載入和圖片記憶體緩衝。嘿嘿。


開發Android應用經常需要處理圖片的載入問題。因為圖片一般都是存放在伺服器端,需要連網去載入,而這又是一個比較耗時的過程,所以Android中都是通過開啟一個非同步線程去載入。為了增加使用者體驗,給使用者省流量,一般把載入完的圖片先緩衝下來,下次載入的時候就不需要再連網去伺服器端載入。圖片緩衝一般分為一級緩衝(即記憶體緩衝)和二級緩衝(即磁碟緩衝)。這裡只講一級緩衝。

記憶體緩衝就是把載入完的圖片先放在手機記憶體中,等下次載入的時候再從記憶體中取出來。

優點是速度快,缺點是不能長久儲存,使用者退出應用程式之後記憶體緩衝就被回收了。而且載入太多會拋出java.lang.OutOfMemory異常。

磁碟緩衝就是把載入完的圖片放到手機內建儲存卡或SD卡中。下次載入的時候在從裡面取出來。

優點是能夠長期儲存,缺點是速度較慢,為了不影響使用者體驗,也一般是通過非同步線程去取磁碟中的緩衝圖片;還有一個就是使用者卸載應用程式後這些快取檔案不會隨著刪除,浪費了使用者的磁碟空間,需要使用者手動刪除。

     

圖片緩衝很多人都是用軟引用SoftRReference來處理,但是Android官方並不推薦這麼用。

Android官方給出的理由是:Note: In
the past, a popular memory cache implementation was a SoftReference or WeakReference bitmap
cache, however this is not recommended. Starting from Android 2.3 (API Level 9) the garbage collector is more aggressive with collecting soft/weak references which makes them fairly ineffective. In addition, prior to Android 3.0 (API Level 11), the backing
data of a bitmap was stored in native memory which is not released in a predictable manner, potentially causing an application to briefly exceed its memory limits and crash.

      
 

所以我們一般使用LruCache這個類,這個類可以這樣理解:它就是一個記憶體緩衝對象,你的圖片就緩衝在這個類裡面。(Java中什麼都是對象)這個類是在API
12中才有的,不過幸運的是Android官方把它加入了v4包中,所以1.6以後的都可以使用這個類。這個類用起來也比較簡單(官方文檔上也講的很詳細)。

    

下面講一下我是怎樣用的:

首先是給ListView 自訂一個Adapter(繼承自BaseAdapter),然後在這個類中申明一個成員變數LruCache ,而不能在getView()方法中申明。因為一個ListView所有的Item項只能維護這一個LruCache 。LruCache有一個構造方法LruCache(int maxSize) ,參數就是這個緩衝空間最大的容量(多少位元組)。申明一個LruCache對象一般需要複寫它的sizeOf(K key, V value),用這個方法改變每個緩衝條目計算大小的方式。Android源碼中預設是返回1,說預設是以個數來計算的。(其實這裡我也不太理解,有懂的朋友還望不吝賜教。)

 

Android sizeof()的源碼:

/**     * Returns the size of the entry for {@code key} and {@code value} in     * user-defined units.  The default implementation returns 1 so that size     * is the number of entries and max size is the maximum number of entries.     *     * <p>An entry's size must not change while it is in the cache.     */    protected int sizeOf(K key, V value) {        return 1;    }

不過我是參照官方文檔寫的。

private final int maxMemory = (int) Runtime.getRuntime().maxMemory();//擷取當前應用程式所分配的最大記憶體private final int cacheSize = maxMemory / 5;//只分5分之一用來做圖片緩衝private LruCache<String, Bitmap> mLruCache = new LruCache<String, Bitmap>(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap bitmap) {//複寫sizeof()方法// replaced by getByteCount() in API 12return bitmap.getRowBytes() * bitmap.getHeight() / 1024; //這裡是按多少KB來算}};

然後寫一個非同步載入圖片的類AsyncImageLoader ; 這個類採用AsyncTask來非同步載入圖片。

package com.folyd.tuan.util;import java.io.IOException;import android.graphics.Bitmap;import android.os.AsyncTask;import android.support.v4.util.LruCache;import android.widget.ImageView;import com.folyd.tuan.util.SimpleImageLoader;/** * 圖片非同步載入類,有圖片記憶體緩衝 *  * @author Folyd *  */public class AsyncImageLoader extends AsyncTask<String, Void, Bitmap> {private ImageView image;private LruCache<String, Bitmap> lruCache;/** * 構造方法,需要把ImageView控制項和LruCache 對象傳進來 * @param image 載入圖片到此 {@code}ImageView * @param lruCache 緩衝圖片的對象 */public AsyncImageLoader(ImageView image, LruCache<String, Bitmap> lruCache) {super();this.image = image;this.lruCache = lruCache;}@Overrideprotected Bitmap doInBackground(String... params) {Bitmap bitmap = null;try {bitmap = SimpleImageLoader.getBitmap(params[0]);} catch (IOException e) {e.printStackTrace();}addBitmapToMemoryCache(params[0], bitmap);return bitmap;}@Overrideprotected void onPostExecute(Bitmap bitmap) {image.setImageBitmap(bitmap);}        //調用LruCache的put 方法將圖片加入記憶體緩衝中,要給這個圖片一個key 方便下次從緩衝中取出來private void addBitmapToMemoryCache(String key, Bitmap bitmap) {if (getBitmapFromMemoryCache(key) == null) {lruCache.put(key, bitmap);}}        //調用Lrucache的get 方法從記憶體緩衝中去圖片public Bitmap getBitmapFromMemoryCache(String key) {return lruCache.get(key);}}

  SimpleImageLoader
的getBitmap方法是一個簡單的從網上擷取圖片的方法。

package com.folyd.tuan.util;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.URL;import android.graphics.Bitmap;import android.graphics.BitmapFactory;/** * 簡單的圖片載入工具類,此類沒有涉及到圖片緩衝。 * @author Folyd * */public class SimpleImageLoader {public static Bitmap getBitmap(String urlStr) throws IOException{Bitmap bitmap;URL url = new URL(urlStr);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");conn.setReadTimeout(5*1000);conn.setDoInput(true);conn.connect();InputStream is = conn.getInputStream();bitmap = BitmapFactory.decodeStream(is);is.close();return bitmap;}}

然後在自訂的Adapter中封裝一個載入圖片的方法。這裡涉及到AsyncTask 的一些知識,可以查看官方文檔學習。

/** *  * @param urlStr 所需要載入的圖片的url,以String形式傳進來,可以把這個url作為緩衝圖片的key * @param image ImageView 控制項 */private void loadBitmap(String urlStr, ImageView image) {AsyncImageLoader asyncLoader = new AsyncImageLoader(image, mLruCache);//什麼一個非同步圖片載入對象Bitmap bitmap = asyncLoader.getBitmapFromMemoryCache(urlStr);//首先從記憶體緩衝中擷取圖片if (bitmap != null) {image.setImageBitmap(bitmap);//如果緩衝中存在這張圖片則直接設定給ImageView} else {image.setImageResource(R.drawable.thum);//否則先設定成預設的圖片asyncLoader.execute(urlStr);//然後執行非同步任務AsycnTask 去網上載入圖片}}

然後在自訂Adapter的getView()方法方法中調用上面的loadBitmap()方法。


這樣就實習了圖片非同步載入和記憶體緩衝了。

看一下:

 
              


當然沒有磁碟緩衝的ListView
絕對是不行的。ListView還有很多需要最佳化的技巧需要我們去不斷學習。比如說圖片懶載入。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.