標籤:
使用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緩衝並非同步載入圖片