图片加载利器BitmapFun解析 使用


通过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实现多线程并发加载图片的原理:

一个类,两个方法:

?
1
2
3
4
5
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();
}

 练习代码:

private ImageFetcher mImageFetcher = null;

@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
}
@Override
protected void onResume() {
super.onResume();
// 打开后台任务
mImageFetcher.setExitTasksEarly(false);
//get 重新载
adateper.notifyDataSetChanged();
}
@Override
protected void onPause() {
super.onPause();
// 关闭后台任务
mImageFetcher.setExitTasksEarly(true);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_image_test);

mImageFetcher = new ImageFetcher(this,// 上下文,
100,// 宽
100// 高
);
// 设置参数
mImageFetcher.setImageCache(new ImageCache(this, "new"));// 保存图片的文件夹名称;
// 设置载入前显示的图片
mImageFetcher.setLoadingImage(R.drawable.empty_photo);
listview = (ListView) findViewById(R.id.listivew);
listview.setAdapter(adateper);
}


官网

Processing Bitmaps Off the UI Thread   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值