android中listview,gridview加載圖片的線程并發解決方案
如何處理listview的下載圖片時候多線程并發問題,我這里參考了一些網絡的資源和項目,總結了一下。希望能對有這些方面疑惑的朋友有所幫助。(listview和gridview,viewpager同一個道理,大家舉一反三)。
這里涉及到三個知識點:
1、通過網絡下載圖片資源。
2、異步任務顯示在UI線程上。
3、解決當用戶隨意滑動的時候解決多線程并發的問題(這個問題是本教程要解決的重點)
通過網絡下載圖片資源
這個這個很簡單,這里給出了一種解決方案:
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控件上。
public class ImageLoader { public void loadImage(String url, ImageView imageView) { BitmapDownloaderTask task = new BitmapDownloaderTask(imageView); task.execute(url); } } }這個BitmapDownloadTask類是一個AsyncTask ,他的主要工作就是去網絡下載圖片并顯示在imageview上。代碼如下:
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記住它是否正在加載(或者說是下載)網絡的圖片資源。如果正在加載,或者加載完成,那么我就不應該再建立一個任務去加載圖片了。
現在我們把修改如下:
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, imageView); } } } }
首先我們先通過cancelPotentialDownload方法去判斷imageView是否有線程正在為它下載圖片資源,如果有現在正在下載,那么判斷下載的這個圖片資源(url)是否和現在的圖片資源一樣,不一樣則取消之前的線程(之前的下載線程作廢)。cancelPotentialDownload方法代碼如下:
private static boolean cancelPotentialDownload(String url, ImageView imageView) { BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); if (bitmapDownloaderTask != null) { String bitmapUrl = bitmapDownloaderTask.url; if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) { bitmapDownloaderTask.cancel(true); } else { // 相同的url已經在下載中. return false; } } return true; }當 bitmapDownloaderTask.cancel(true)被執行的時候,則BitmapDownloaderTask 就會被取消,當BitmapDownloaderTask 的執行到onPostExecute的時候,如果這個任務加載到了圖片,它也會把這個bitmap設為null了。
getBitmapDownloaderTask代碼如下:
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中,代碼如下:
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 方法:
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); } }
后記
在上面中,雖然我們解決了多線程的并發問題,但是,相信你一定也能看出有很多不足的地方,比如說,沒有緩存,沒有考慮內存問題等等,不過這個已經不是本章要討論的內容了(不過不要失望,在后邊的章節中,我將會一一為你講解),通過這個簡單的入門,相信你一定知道怎么去解決listview,gridview的線程并發問題了吧。
還有問題?
聯系我吧,QQ群:130112760(老羅的Android之旅),個人網站:http://www.devChina.com ,或者csdn中給我留言。
歡迎轉載,轉載注明出處:http://blog.csdn.net/michael_yy/article/details/8012308
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!