通過BitmapFun在項目中使用,結合代碼瞭解一下BitmapFun加載圖片的原理,以及最佳使用實踐。本文說明不包括BitmapFun的緩存部分。
Android開發在使用ListView和GridView時,可能會有很多網絡圖片需要加載,通常我們會爲每個圖片加載啓動一個Thread或者直接使用官方提供的AsyncTask,來做Http異步加載,但當每個ImageView子視圖都觸發一個AsyncTask來異步加載圖片時,這樣就會產生如下問題:
1. 當用戶快速滑動時,ImageView已經被回收,而綁定的線程還在運行,浪費CPU,浪費內存。
2. 無法確保當前視圖在結束時,分配的視圖已經進入循環隊列中給另外一個子視圖進行重用,意思就是,圖片顯示錯位了,不該顯示到當前問題的圖片卻顯示了,這個是經常遇到的問題。可以結合Adapter中的getView方法的convertView參數理解,ListView是回收和重複利用item的。
3. 無法確保所有的異步任務能夠按順序執行。
在這些問題下,官網給出的答案是,使用下面的方法來保證:
1. ImageView和Task綁定準確的加載對應圖片;
2. ImageView和Task無法對應時則取消任務;
不多說直接上代碼:
BitmapFun實現多線程併發加載圖片的原理:
一個類,兩個方法:
class
AsyncDrawable
extends
BitmapDrawable{...}
boolean
cancelPotentialWork(String
url, ImageView imageView){...}
ImageViewResizeTask
getBitmapWorkerTask(ImageView imageView){...}
具體代碼實現如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
/** *
擴展BitmapDrawable類,通過弱引用關聯任務BitmapWorkerTask *
BitmapDrawable被用來作爲佔位圖片,綁定任務到ImageView中 */ private class AsyncDrawable
extends BitmapDrawable
{ private final WeakReference<BitmapWorkerTask>
viewResizeTaskReference; public AsyncDrawable(Resources
res, Bitmap bitmap, BitmapWorkerTask viewResizeTask) { super (res,
bitmap); viewResizeTaskReference
= new WeakReference<BitmapWorkerTask>(viewResizeTask); } public BitmapWorkerTask
getBitmapWorkerTask() { return viewResizeTaskReference.get(); } } /** *
確保ImageView執行的是它對應的Task,否則取消任務 *
@param url *
@param imageView *
@return */ private boolean cancelPotentialWork(String
url, ImageView imageView) { //
獲得ImageView對應的Task final BitmapWorkerTask
bitmapWorkerTask = getBitmapWorkerTask(imageView); if (bitmapWorkerTask
!= null )
{ final String
imgUrl = bitmapWorkerTask.url; if (imgUrl
== null ||
!imgUrl.equals(url)) { //
Cancel previous task bitmapWorkerTask.cancel( true );
}
else {
//
The same work is already in progress return false ;
}
}
//
No task associated with the ImageView, or an existing task was cancelled
return true ;
}
/** *
獲得已經被分配到ImageView的指定的Task *
@param imageView *
@return */ private BitmapWorkerTask
getBitmapWorkerTask(ImageView imageView) { if (imageView
!= null )
{ final Drawable
drawable = imageView.getDrawable(); if (drawable
instanceof AsyncDrawable)
{ final AsyncDrawable
asyncDrawable = (AsyncDrawable) drawable; return asyncDrawable.getBitmapWorkerTask(); } } return null ; } |
使用方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
/** *
異步加載圖片 *
@param url *
@param imageView */ private void loadBitmap(String
url, ImageView imageView) { if (cancelPotentialWork(url,
imageView)) { final BitmapWorkerTask
task = new BitmapWorkerTask(imageView);
final AsyncDrawable
asyncDrawable = new AsyncDrawable(context.getResources(),
null ,
task); imageView.setImageDrawable(asyncDrawable);
task.execute(url);
}
}
/* *
異步加載圖片Task類 */ class BitmapWorkerTask
extends AsyncTask<Integer,
Void, Bitmap> { ... @Override protected void onPostExecute(Bitmap
bitmap) { if (isCancelled())
{ bitmap
= null ; } if (imageViewReference
!= null &&
bitmap != null )
{ final ImageView
imageView = imageViewReference.get(); final BitmapWorkerTask
bitmapWorkerTask = getBitmapWorkerTask(imageView); if ( this ==
bitmapWorkerTask && imageView != null )
{ imageView.setImageBitmap(bitmap); } } } } |
最佳使用實踐,提高流暢度
1. 設置ListView的OnScrollListener事件,在滑動的時候不加載圖片
1
2
3
4
5
6
7
8
9
|
public void onScrollStateChanged(AbsListView
view, int scrollState)
{ if (scrollState
== AbsListView.OnScrollListener.SCROLL_STATE_FLING) { if (!Utils.hasHoneycomb())
{ mImageFetcher.setPauseWork( true ); } }
else { mImageFetcher.setPauseWork( false ); } } |
2. 在Activity或Fragment的onResume(),onPause(),onDestroy()方法中調用恰當方法非常有用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
@Override public void onResume()
{ super .onResume(); mImageFetcher.setExitTasksEarly( false ); } @Override public void onPause()
{ super .onPause(); mImageFetcher.setPauseWork( false ); mImageFetcher.setExitTasksEarly( true ); mImageFetcher.flushCache(); } @Override public void onDestroy()
{ super .onDestroy(); mImageFetcher.closeCache(); } |