編輯:關於Android編程
在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);
}
}
1,手機獲取ROOT權限(我的是小米note,直接下載MIUI開發版安裝即可)2,在應用市場下載(終端模擬器),安裝。3,賦予(終端模擬器)ROOT權限。提示:測試其是否
第一次接觸樹形ListView是一年前,當時公司做的項目是一個企業的員工管理軟件,在展示員工時用到的,我花了大半天時間,才把樹形ListView搞明白,完成任務後就沒有然
在Android NDK開發中,有兩個重要的文件:Android.mk和Application.mk,各盡其責,指導編譯器如何編譯程序,並決定編譯結果是什麼。本文將詳細說
使用include標簽復用布局- 1.include標簽的作用 假如說我下圖的這個布局在很多界面都用到了,我該怎麼辦?每個頁面都寫一遍的話,代碼太冗余,並且維護難度加大。