Android批量圖片載入經典系列——使用LruCache、AsyncTask緩衝並非同步載入圖片

來源:互聯網
上載者:User

標籤:

一、問題描述

使用LruCache、AsyncTask實現批量圖片的載入並達到下列技術要求

1、從緩衝中讀取圖片,若不在緩衝中,則開啟非同步線程(AsyncTask)載入圖片,並放入緩衝中

2、及時移除無效的非同步線程;保證非同步載入圖片時不會亂序

3、只對當前螢幕可見部分進行緩衝、非同步載入圖片

4、最佳化效能杜絕OOM

二、案例介紹

 案例實現照片牆效果

 

三、主要技術

  LruCache

  記憶體緩衝技術,在Android中 專門用來做圖片緩衝處理的組件,主要使用步驟

  (1) 設定緩衝圖片的記憶體大小,如設定為手機記憶體的1/8(當緩衝的圖片達到了預先設定的值的時候,那麼近期使用次數最少的圖片就會被回收掉)代碼如下:

// 擷取應用程式最大可用記憶體int maxMemory = (int) Runtime.getRuntime().maxMemory();int cacheSize = maxMemory / 8;// 設定圖片緩衝大小為程式最大可用記憶體的1/8mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {            @Override            protected int sizeOf(String key, Bitmap bitmap) {                return bitmap.getByteCount();            }        };

(2) 將圖片放入緩衝中(LruCache裡面的鍵值對通常分別是URL和對應的圖片)

mMemoryCache.put(key, bitmap);

(3) 從緩衝中取圖片

   mMemoryCache.get(key);

 

  AsyncTask  

  進行耗時操作比如載入圖片要求不要阻塞UI線程,就必須使用非同步任務。AsyncTask是不需要藉助thread+handler即可實現非同步任務的組件,使用起來比較簡單且更輕量級一些。實現AsyncTask步驟如下:

  (1) 擴充子AsyncTask,如

  class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap>

  (2) 重寫AsyncTask中的幾個方法  

  onPreExecute(), 該方法將在執行實際的後台操作前被UI線程調用。可以在該方法中做一些準備工作,如在介面上顯示一個進度條。 

  doInBackground(Params...), 將在onPreExecute 方法執行後、執行,該方法運行在後台線程中,主要負責執行那些很耗時的後台計算工作,如載入圖片

  onPostExecute(Result), 在doInBackground 執行完成後,該將被UI線程調用,背景計算結果將通過該方法傳遞到UI線程 

  (3) 在UI線程中通過調用AsyncTask的execute(Params…);

  開始執行非同步任務並向背景工作傳入資料,執行次序  onPreExecute()——> doInBackground(Params...)——> onPostExecute(Result)

  經驗:對大量圖片載入,要求每個圖片都要在一個背景工作中取運行,因此需要使用集合記錄所有正在下載或等待下載的任務

  Set<BitmapWorkerTask> taskCollection=new HashSet< BitmapWorkerTask >();

  當緩衝中沒有圖片,新增工作並啟動非同步處理,片段代碼如:

if (bitmap == null) {//如果緩衝沒有BitmapWorkerTask task = new BitmapWorkerTask();taskCollection.add(task);     task.execute(imageUrl);//執行非同步任務,並傳入載入的圖片url地址}

  要注意及時移除已完成的任務,如下代碼

protected void onPostExecute(Bitmap bitmap) {….taskCollection.remove(this);}

  並且在適配器控制項滑動停止時取消正在執行的任務,片段代碼如下:

public void onScrollStateChanged(AbsListView view, int scrollState) {// 僅當GridView靜止時才去下載圖片,GridView滑動時取消所有正在下載的任務if (scrollState == SCROLL_STATE_IDLE) {loadBitmaps(mFirstVisibleItem, mVisibleItemCount);} else {cancelAllTasks();//取消所有正在下載或等待下載的任務。}}public void cancelAllTasks() {if (taskCollection != null) {for (BitmapWorkerTask task : taskCollection) {task.cancel(false);}}}
三、完整代碼

   1、 MainActivity

public class MainActivity extends Activity {    private GridView mPhotoWall;    private PhotoWallAdapter adapter;private ArrayList<File> list;    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        list = new ArrayList<File>();        getAllFiles(new File("/sdcard"));        mPhotoWall = (GridView) findViewById(R.id.photo_wall);        adapter = new PhotoWallAdapter(this, 0, list, mPhotoWall);        mPhotoWall.setAdapter(adapter);    }    /**     * 獲得指定目錄片檔案     */    private void getAllFiles(File root) {        File files[] = root.listFiles();        if (files != null)            for (File f : files) {                if (f.isDirectory()) {                    getAllFiles(f);                } else {                    if (f.getName().indexOf(".png") > 0                            || f.getName().indexOf(".jpg") > 0                            || f.getName().indexOf(".jpeg") > 0)                        this.list.add(f);                }            }    }    protected void onDestroy() {        super.onDestroy();        adapter.cancelAllTasks();// 退出程式時結束所有的下載任務    }}

2、PhotoWallAdapter適配器

public class PhotoWallAdapter extends ArrayAdapter<File> implements        OnScrollListener {     //記錄所有正在下載或等待下載的任務。    private Set<BitmapWorkerTask> taskCollection; // 圖片緩衝技術的核心類,用於緩衝所有下載好的圖片,在程式記憶體達到設定值時會//將最少最近使用的圖片移除掉。    private LruCache<String, Bitmap> mMemoryCache;    //GridView的執行個體    private GridView mPhotoWall;    //第一張可見圖片的下標    private int mFirstVisibleItem;    // 一屏有多少張圖片可見    private int mVisibleItemCount;    // 記錄是否剛開啟程式,用於解決進入程式不滾動螢幕,不會下載圖片的問題。    private boolean isFirstEnter = true;    ArrayList<File> list = null;    public PhotoWallAdapter(Context context, int textViewResourceId,            ArrayList<File> objects, GridView photoWall) {        super(context, textViewResourceId, objects);        mPhotoWall = photoWall;        list = objects;        taskCollection = new HashSet<BitmapWorkerTask>();        // 擷取應用程式最大可用記憶體        int maxMemory = (int) Runtime.getRuntime().maxMemory();        int cacheSize = maxMemory / 8;        // 設定圖片緩衝大小為程式最大可用記憶體的1/8        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {            @Override            protected int sizeOf(String key, Bitmap bitmap) {                return bitmap.getByteCount();            }        };        mPhotoWall.setOnScrollListener(this);    }    public View getView(int position, View convertView, ViewGroup parent) {        final File url = getItem(position);        View view;        if (convertView == null) {            view = LayoutInflater.from(getContext()).inflate(                    R.layout.photo_layout, null);        } else {            view = convertView;        }        final ImageView photo = (ImageView) view.findViewById(R.id.photo);        // 給ImageView設定一個Tag,保證非同步載入圖片時不會亂序        photo.setTag(url.getAbsolutePath());        setImageView(url.getAbsolutePath(), photo);        return view;    }    /**     * 給ImageView設定圖片。首先從LruCache中取出圖片的緩衝,設定到ImageView上。如果LruCache中沒有該圖片的緩衝,     * 就給ImageView設定一張預設圖片。     * @param imageUrl     *            圖片的URL地址,用於作為LruCache的鍵。     * @param imageView     *            用於顯示圖片的控制項。     */    private void setImageView(String imageUrl, ImageView imageView) {        Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);        if (bitmap != null) {            imageView.setImageBitmap(bitmap);        } else {            bitmap=getLoacalBitmap(imageUrl);            imageView.setImageResource(R.drawable.empty_photo);        }    }    /**     * 將一張圖片儲存到LruCache中。     * @param key     *            LruCache的鍵,這裡傳入圖片的URL地址。     * @param bitmap     *            LruCache的鍵,這裡傳入從網路上下載的Bitmap對象。     */@SuppressLint("NewApi")    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {        if (getBitmapFromMemoryCache(key) == null) {            mMemoryCache.put(key, bitmap);        }    }    /**     * 從LruCache中擷取一張圖片,如果不存在就返回null。     * @param key     *            LruCache的鍵,這裡傳入圖片的URL地址。     * @return 對應傳入鍵的Bitmap對象,或者null。     */    @SuppressLint("NewApi")    public Bitmap getBitmapFromMemoryCache(String key) {        return mMemoryCache.get(key);    }    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {        // 僅當GridView靜止時才去下載圖片,GridView滑動時取消所有正在下載的任務        if (scrollState == SCROLL_STATE_IDLE) {            loadBitmaps(mFirstVisibleItem, mVisibleItemCount);        } else {            cancelAllTasks();        }    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem,            int visibleItemCount, int totalItemCount) {        mFirstVisibleItem = firstVisibleItem;        mVisibleItemCount = visibleItemCount;        // 下載的任務應該由onScrollStateChanged裡調用,但首次進入程式時onScrollStateChanged並不會調用,        // 因此在這裡為首次進入程式開啟下載任務。        if (isFirstEnter && visibleItemCount > 0) {            loadBitmaps(firstVisibleItem, visibleItemCount);            isFirstEnter = false;        }    }    /**     * 載入Bitmap對象。此方法會在LruCache中檢查所有螢幕中可見的ImageView的Bitmap對象,     * 如果發現任何一個ImageView的Bitmap對象不在緩衝中,就會開啟非同步線程去下載圖片。     *      * @param firstVisibleItem     *            第一個可見的ImageView的下標     * @param visibleItemCount     *            螢幕中總共可見的元素數     */    private void loadBitmaps(int firstVisibleItem, int visibleItemCount) {        try {            for (int i = firstVisibleItem; i < firstVisibleItem                    + visibleItemCount; i++) {                String imageUrl = list.get(i).getAbsolutePath();                Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);                if (bitmap == null) {//如果緩衝沒有                    BitmapWorkerTask task = new BitmapWorkerTask();                    taskCollection.add(task);                    task.execute(imageUrl);//執行非同步任務,並傳入載入的圖片url地址(這裡是sd卡上的圖片)                } else {                    ImageView imageView = (ImageView) mPhotoWall                            .findViewWithTag(imageUrl);                    if (imageView != null && bitmap != null) {                        imageView.setImageBitmap(bitmap);                    }                }            }        } catch (Exception e) {            e.printStackTrace();        }    }    /**     * 取消所有正在下載或等待下載的任務。     */    public void cancelAllTasks() {        if (taskCollection != null) {            for (BitmapWorkerTask task : taskCollection) {                task.cancel(false);            }        }    }    /**     * 非同步下載圖片的任務。     */    class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {        /**         * 圖片的URL地址         */        private String imageUrl;        @Override        protected Bitmap doInBackground(String... params) {            imageUrl = params[0];            // 在後台開始下載圖片            Bitmap bitmap = getLoacalBitmap(params[0]);            if (bitmap != null) {                // 圖片下載完成後緩衝到LrcCache中                addBitmapToMemoryCache(params[0], bitmap);            }            return bitmap;        }        @Override        protected void onPostExecute(Bitmap bitmap) {            super.onPostExecute(bitmap);            // 根據Tag找到相應的ImageView控制項,將下載好的圖片顯示出來。            ImageView imageView = (ImageView) mPhotoWall                    .findViewWithTag(imageUrl);            if (imageView != null && bitmap != null) {                imageView.setImageBitmap(bitmap);            }            taskCollection.remove(this);        }    }    private  Bitmap getLoacalBitmap(String url) {        try {            FileInputStream fis = new FileInputStream(url);            return BitmapFactory.decodeStream(fis); // /把流轉化為Bitmap圖片        } catch (FileNotFoundException e) {            e.printStackTrace();            return null;        }    }}

 

  想要瞭解更多內容的小夥伴,可以點擊查看源碼,親自運行測試。

  疑問諮詢或技術交流,請加入官方QQ群: (452379712)

 

傑瑞教育
出處:http://blog.csdn.net/jerehedu/ 
本文著作權歸煙台傑瑞教育科技有限公司和CSDN共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文串連,否則保留追究法律責任的權利。 

Android批量圖片載入經典系列——使用LruCache、AsyncTask緩衝並非同步載入圖片

聯繫我們

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