通过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();
}
|