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); } }
到此,一切完毕。不得不承认,同一个功能,好坏程序员写得就是不一样啊!