android中listview,gridview加载图片的线程并发解决方案

如何处理listview的下载图片时候多线程并发问题,我这里参考了一些网络的资源和项目,总结了一下。希望能对有这些方面疑惑的朋友有所帮助。(listview和gridview,viewpager同一个道理,大家举一反三)。

               这里涉及到三个知识点:

1、通过网络下载图片资源。

                2、异步任务显示在UI线程上。

                3、解决当用户随意滑动的时候解决多线程并发的问题(这个问题是本教程要解决的重点)

 

通过网络下载图片资源

这个这个很简单,这里给出了一种解决方案:
01 static Bitmap downloadBitmap(String url) {
02     final AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
03     final HttpGet getRequest = new HttpGet(url);
04  
05     try {
06         HttpResponse response = client.execute(getRequest);
07         final int statusCode = response.getStatusLine().getStatusCode();
08         if(statusCode != HttpStatus.SC_OK) {
09             Log.w("ImageDownloader""Error "+ statusCode + " while retrieving bitmap from " + url);
10             return null;
11         }
12          
13         final HttpEntity entity = response.getEntity();
14         if (entity != null) {
15             InputStream inputStream = null;
16             try {
17                 inputStream = entity.getContent();
18                 final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
19                 return bitmap;
20             finally {
21                 if (inputStream != null) {
22                     inputStream.close(); 
23                 }
24                 entity.consumeContent();
25             }
26         }
27     catch (Exception e) {
28         // Could provide a more explicit error message for IOException or IllegalStateException
29         getRequest.abort();
30         Log.w("ImageDownloader""Error while retrieving bitmap from "+ url, e.toString());
31     finally {
32         if (client != null) {
33             client.close();
34         }
35     }
36     return null;
37 }
这个通过http去网络下载图片的功能很简单,我是直接从别的文章里复制过来的,不懂的可以给我留言。


在异步任务把图片显示在主线程上

在上面中,我们已经实现了从网络下载一张图片,接下来,我们要在异步任务中把图片显示在UI主线程上。在android系统中,android给我们提供了一个异步任务类:AsyncTask ,它提供了一个简单的方法然给我们的子线程和主线程进行交互。
现在我们来建立一个ImageLoader类,这个类有一个loadImage方法来加载网络图片,并显示在android的Imageview控件上。
1 public class ImageLoader {
2  
3     public void loadImage(String url, ImageView imageView) {
4             BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
5             task.execute(url);
6         }
7     }
8  
9 }
这个BitmapDownloadTask类是一个AsyncTask ,他的主要工作就是去网络下载图片并显示在imageview上。代码如下:
01 class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
02     private String url;
03     private final WeakReference<ImageView> imageViewReference;
04  
05     public BitmapDownloaderTask(ImageView imageView) {
06         imageViewReference = new WeakReference<ImageView>(imageView);
07     }
08  
09     @Override
10     // Actual download method, run in the task thread
11     protected Bitmap doInBackground(String... params) {
12          // params comes from the execute() call: params[0] is the url.
13          return downloadBitmap(params[0]);
14     }
15  
16     @Override
17     // Once the image is downloaded, associates it to the imageView
18     protected void onPostExecute(Bitmap bitmap) {
19         if (isCancelled()) {
20             bitmap = null;
21         }
22  
23         if (imageViewReference != null) {
24             ImageView imageView = imageViewReference.get();
25             if (imageView != null) {
26                 imageView.setImageBitmap(bitmap);
27             }
28         }
29     }
30 }
这个BitmapDownloaderTask 里面的doInBackground方法是在子线程运行,而onPostExecute是在主线程运行,doInBackground执行的结果返回给onPostExecute。关于更多的AsyncTask 相关技术和参考android的帮助文档(这个技术点不是本章要讨论的内容)。
到目前为止,我们已经可以实现了通过异步任务去网络下载图片,并显示在imageview上的功能了。

多线程并发处理

在上面中虽然我们实现了子线程下载图片并显示在imageview的功能,但是在listview等容器中,当用户随意滑动的时候,将会产生N个线程去下载图片,这个是我们不想看到的。我们希望的是一个图片只有一个线程去下载就行了。
为了解决这个问题,我们应该做的是让这个imageview记住它是否正在加载(或者说是下载)网络的图片资源。如果正在加载,或者加载完成,那么我就不应该再建立一个任务去加载图片了。
现在我们把修改如下:
01 public class ImageLoader {
02  
03     public void loadImage(String url, ImageView imageView) {
04             if (cancelPotentialDownload(url, imageView)) {
05          BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
06          DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
07          imageView.setImageDrawable(downloadedDrawable);
08          task.execute(url, imageView);
09      }
10         }
11     }
12  
13 }

首先我们先通过cancelPotentialDownload方法去判断imageView是否有线程正在为它下载图片资源,如果有现在正在下载,那么判断下载的这个图片资源(url)是否和现在的图片资源一样,不一样则取消之前的线程(之前的下载线程作废)。cancelPotentialDownload方法代码如下:
01 private static boolean cancelPotentialDownload(String url, ImageView imageView) {
02     BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
03  
04     if (bitmapDownloaderTask != null) {
05         String bitmapUrl = bitmapDownloaderTask.url;
06         if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
07             bitmapDownloaderTask.cancel(true);
08         else {
09             // 相同的url已经在下载中.
10             return false;
11         }
12     }
13     return true;
14 }
当 bitmapDownloaderTask.cancel(true)被执行的时候,则BitmapDownloaderTask 就会被取消,当BitmapDownloaderTask 的执行到onPostExecute的时候,如果这个任务加载到了图片,它也会把这个bitmap设为null了。
getBitmapDownloaderTask代码如下:
01 private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
02     if (imageView != null) {
03         Drawable drawable = imageView.getDrawable();
04         if (drawable instanceof DownloadedDrawable) {
05             DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;
06             return downloadedDrawable.getBitmapDownloaderTask();
07         }
08     }
09     return null;
10 }
DownloadedDrawable是我们自定义的一个类,它的主要功能是记录了下载的任务,并被设置到imageview中,代码如下:
01 static class DownloadedDrawable extends ColorDrawable {
02     private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;
03  
04     public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
05         super(Color.BLACK);
06         bitmapDownloaderTaskReference =
07             new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
08     }
09  
10     public BitmapDownloaderTask getBitmapDownloaderTask() {
11         return bitmapDownloaderTaskReference.get();
12     }
13 }

最后, 我们回来修改BitmapDownloaderTask 的onPostExecute 方法:
1 if (imageViewReference != null) {
2     ImageView imageView = imageViewReference.get();
3     BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
4     // Change bitmap only if this process is still associated with it
5     if (this == bitmapDownloaderTask) {
6         imageView.setImageBitmap(bitmap);
7     }
8 }

后记

在上面中,虽然我们解决了多线程的并发问题,但是,相信你一定也能看出有很多不足的地方,比如说,没有缓存,没有考虑内存问题等等,不过这个已经不是本章要讨论的内容了(不过不要失望,在后边的章节中,我将会一一为你讲解),通过这个简单的入门,相信你一定知道怎么去解决listview,gridview的线程并发问题了吧。


还有问题?

联系我吧,QQ群:130112760(老罗的Android之旅),个人网站:http://www.devChina.com ,或者csdn中给我留言。


欢迎转载,转载注明出处:http://blog.csdn.net/michael_yy/article/details/8012308
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值