Multithreading For Performance

阅读:http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html

 

文章通过一个图像下载器来说明:

下载代码:

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;
}

上面使用了AndroidHttpClient,然后这个类并不支持在main线程中执行,所以最好使用DefaultHttpClient 。

如果获取成功了,response的entity就会包含了图像的流,然后通过Bitmap的decoded获取图像。

 

接下来使用了AsyncTask进行下载:

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);
            }
        }
    }
}

onPostExecute将在UI线程中进行。
值得一提的是这个WeakReference,它声明了一个弱引用,因为在下载过程中,有可能Activity被销毁导致View消失,这也就是为什么我们在VIEW装入bitmap的时候需要检测是否为NULL了。

 

如果此时你仅仅是下载一个图片,那么看到这里就行了。但是如果考虑到当你使用listview的时候,上面的待会可能会导致这样的情况:

你有三个listview,分别是L1,L2,L3,你希望的结果是这三个listview分别绑定图片A,B,C。

但是如果使用上面的代码有可能的结果就是:L1 L2 L3绑定的图片顺序是: A C B。

出现这种情况的原因在于,onPostExecute只是在每次doInBackground之后再UI线程中运行的代码,有可能B的图片较大而C图片较小,导致绑定了L2的TASK最后将先下载完的C填充到L2上面。

解决的办法,当然就是绑定。

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();
    }
}
public void download(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);
     }
}

也就是说,绑定顺序是这样的:  imageView——》DownloadedDrawable ——》BitmapDownloaderTask ,而BitmapDownloaderTask 绑定了前面的imageView。

注意使用了WeakReference,使AsyncTask能够独立。

在AsyncTask里,需要实现这个方法:

 

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;
}

 

最后,在绑定的时候,使用下面的方法:

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);
    }
}

到此,一切完毕。不得不承认,同一个功能,好坏程序员写得就是不一样啊!

 

 

posted @ 2013-10-26 13:48  yutoulck  阅读(304)  评论(0编辑  收藏  举报