展会信息港展会大全

android中listview,gridview加载图片的线程并发解决方案
来源:互联网   发布日期:2015-11-26 17:10:42   浏览:2359次  

导读: 在QQ群里和论坛里,有人问如何处理listview的下载图片时候多线程并发问题,我这里参考了一些网络的资源和项目,总结了一下。希望能对有这些方面疑惑的朋友有所帮助。(l...

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

这里涉及到三个知识点:

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

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

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

通过网络下载图片资源

这个这个很简单,这里给出了一种解决方案:

[java]

static Bitmap downloadBitmap(String url) {

final AndroidHttpClient client = AndroidHttpClient.newInstance("Android");

final HttpGet getRequest = new HttpGet(url);

try {

HttpResponse response = client.execute(getRequest);

final int statusCode = response.getStatusLine().getStatusCode();

if (statusCode != HttpStatus.SC_OK) {

Log.w("ImageDownloader", "Error " + statusCode + " while retrieving bitmap from " + url);

return null;

}

final HttpEntity entity = response.getEntity();

if (entity != null) {

InputStream inputStream = null;

try {

inputStream = entity.getContent();

final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

return bitmap;

} finally {

if (inputStream != null) {

inputStream.close();

}

entity.consumeContent();

}

}

} catch (Exception e) {

// Could provide a more explicit error message for IOException or IllegalStateException

getRequest.abort();

Log.w("ImageDownloader", "Error while retrieving bitmap from " + url, e.toString());

} finally {

if (client != null) {

client.close();

}

}

return null;

}

这个通过http去网络下载图片的功能很简单,我是直接从别的文章里复制过来的,不懂的可以给我留言。

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

在上面中,我们已经实现了从网络下载一张图片,接下来,我们要在异步任务中把图片显示在UI主线程上。在android系统中,android给我们提供了一个异步任务类:AsyncTask ,它提供了一个简单的方法然给我们的子线程和主线程进行交互。

现在我们来建立一个ImageLoader类,这个类有一个loadImage方法来加载网络图片,并显示在android的Imageview控件上。

[java]

public class ImageLoader {

public void loadImage(String url, ImageView imageView) {

BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);

task.execute(url);

}

}

}

这个BitmapDownloadTask类是一个AsyncTask ,他的主要工作就是去网络下载图片并显示在imageview上。代码如下:

[java]

class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {

private String url;

private final WeakReference<ImageView> imageViewReference;

public BitmapDownloaderTask(ImageView imageView) {

imageViewReference = new WeakReference<ImageView>(imageView);

}

@Override

// Actual download method, run in the task thread

protected Bitmap doInBackground(String... params) {

// params comes from the execute() call: params[0] is the url.

return downloadBitmap(params[0]);

}

@Override

// Once the image is downloaded, associates it to the imageView

protected void onPostExecute(Bitmap bitmap) {

if (isCancelled()) {

bitmap = null;

}

if (imageViewReference != null) {

ImageView imageView = imageViewReference.get();

if (imageView != null) {

imageView.setImageBitmap(bitmap);

}

}

}

}

这个BitmapDownloaderTask 里面的doInBackground方法是在子线程运行,而onPostExecute是在主线程运行,doInBackground执行的结果返回给onPostExecute。关于更多的AsyncTask 相关技术和参考android的帮助文档(这个技术点不是本章要讨论的内容)。

到目前为止,我们已经可以实现了通过异步任务去网络下载图片,并显示在imageview上的功能了。

多线程并发处理

在上面中虽然我们实现了子线程下载图片并显示在imageview的功能,但是在listview等容器中,当用户随意滑动的时候,将会产生N个线程去下载图片,这个是我们不想看到的。我们希望的是一个图片只有一个线程去下载就行了。

为了解决这个问题,我们应该做的是让这个imageview记住它是否正在加载(或者说是下载)网络的图片资源。如果正在加载,或者加载完成,那么我就不应该再建立一个任务去加载图片了。

现在我们把修改如下:

[java]

public class ImageLoader {

public void loadImage(String url, ImageView imageView) {

if (cancelPotentialDownload(url, imageView)) {

BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);

DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);

imageView.setImageDrawable(downloadedDrawable);

task.execute(url, cookie);

}

}

}

}

首先我们先通过cancelPotentialDownload方法去判断imageView是否有线程正在为它下载图片资源,如果有现在正在下载,那么判断下载的这个图片资源(url)是否和现在的图片资源一样,不一样则取消之前的线程(之前的下载线程作废)。cancelPotentialDownload方法代码如下:

[java]

private static boolean cancelPotentialDownload(String url, ImageView imageView) {

BitmapDownloaderTask bitmapDownloaderTask = <span style="color:#cc0000;">getBitmapDownloaderTask(imageView);</span>

if (bitmapDownloaderTask != null) {

String bitmapUrl = bitmapDownloaderTask.url;

if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {

<span style="color:#ff6666;"> bitmapDownloaderTask.cancel(true);</span>

} else {

<span style="color:#ff0000;">// 相同的url已经在下载中.

return false;</span>

}

}

return true;

}

当 bitmapDownloaderTask.cancel(true)被执行的时候,则BitmapDownloaderTask 就会被取消,当BitmapDownloaderTask 的执行到onPostExecute的时候,如果这个任务加载到了图片,它也会把这个bitmap设为null了。

getBitmapDownloaderTask代码如下:

[java]

private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {

if (imageView != null) {

Drawable drawable = imageView.getDrawable();

if (drawable instanceof DownloadedDrawable) {

DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;

return downloadedDrawable.getBitmapDownloaderTask();

}

}

return null;

}

DownloadedDrawable是我们自定义的一个类,它的主要功能是记录了下载的任务,并被设置到imageview中,代码如下:

[java]

static class DownloadedDrawable extends ColorDrawable {

private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;

public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {

super(Color.BLACK);

bitmapDownloaderTaskReference =

new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);

}

public BitmapDownloaderTask getBitmapDownloaderTask() {

return bitmapDownloaderTaskReference.get();

}

}

最后, 我们回来修改BitmapDownloaderTask 的onPostExecute 方法:

[java]

if (imageViewReference != null) {

ImageView imageView = imageViewReference.get();

BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);

// Change bitmap only if this process is still associated with it

if (this == bitmapDownloaderTask) {

imageView.setImageBitmap(bitmap);

}

}

赞助本站

人工智能实验室

相关热词: android开发 教程

AiLab云推荐
推荐内容
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港