在 Android 中使用 AsyncTask 下載影象

本教程介紹如何使用 Android 中的 AsyncTask 下載 Image。下面的示例在下載過程中顯示進度條的同時下載影象。

瞭解 Android AsyncTask

非同步任務使你可以實現 MultiThreading 而不會弄髒執行緒。AsyncTask 可以正確,方便地使用 UI 執行緒。它允許執行後臺操作並在 UI 執行緒上傳遞結果。如果你正在執行與 UI 相關的隔離操作,例如下載資料以顯示在列表中,請繼續使用 AsyncTask。

  • 理想情況下,AsyncTasks 應該用於短操作(最多幾秒鐘)。
  • 非同步任務由 3 種泛型型別定義,稱為 Params,Progress 和 Result,以及 4 個步驟,稱為 onPreExecute()doInBackground()onProgressUpdate()onPostExecute()
  • onPreExecute() 中,你可以定義程式碼,這些程式碼需要在後臺處理開始之前執行。
  • doInBackground 有需要在後臺執行的程式碼,這裡在 doInBackground() 中我們可以通過 publishProgress() 方法多次向結果執行緒傳送結果,通知後臺處理已經完成,我們可以簡單地返回結果。
  • onProgressUpdate() 方法從 doInBackground() 方法接收進度更新,通過 publishProgress() 方法釋出,此方法可以使用此進度更新來更新事件執行緒
  • onPostExecute() 方法處理 doInBackground() 方法返回的結果。
  • 使用的泛型型別是
    • 引數,執行時傳送給任務的引數型別
    • 進度,後臺計算期間釋出的進度單元的型別。
    • 結果,後臺計算結果的型別。
  • 如果非同步任務不使用任何型別,則可以將其標記為 Void 型別。
  • 可以通過呼叫 cancel(boolean) 方法取消正在執行的非同步任務。

使用 Android AsyncTask 下載影象

你的 .xml 版面

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

<Button
    android:id="@+id/downloadButton"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Click Here to Download" />

<ImageView
    android:id="@+id/imageView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:contentDescription="Your image will appear here" />

</LinearLayout>

.java 類

package com.javatechig.droid;

import java.io.InputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;

public class ImageDownladerActivity extends Activity {

    private ImageView downloadedImg;
    private ProgressDialog simpleWaitDialog;
    private String downloadUrl = "http://www.9ori.com/store/media/images/8ab579a656.jpg";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.asynch);
        Button imageDownloaderBtn = (Button) findViewById(R.id.downloadButton);

        downloadedImg = (ImageView) findViewById(R.id.imageView);

        imageDownloaderBtn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                new ImageDownloader().execute(downloadUrl);
            }

        });
    }

    private class ImageDownloader extends AsyncTask {

        @Override
        protected Bitmap doInBackground(String... param) {
            // TODO Auto-generated method stub
            return downloadBitmap(param[0]);
        }

        @Override
        protected void onPreExecute() {
            Log.i("Async-Example", "onPreExecute Called");
            simpleWaitDialog = ProgressDialog.show(ImageDownladerActivity.this,
                    "Wait", "Downloading Image");

        }

        @Override
        protected void onPostExecute(Bitmap result) {
            Log.i("Async-Example", "onPostExecute Called");
            downloadedImg.setImageBitmap(result);
            simpleWaitDialog.dismiss();

        }

        private Bitmap downloadBitmap(String url) {
            // initilize the default HTTP client object
            final DefaultHttpClient client = new DefaultHttpClient();

            //forming a HttpGet request 
            final HttpGet getRequest = new HttpGet(url);
            try {

                HttpResponse response = client.execute(getRequest);

                //check 200 OK for success
                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 {
                        // getting contents from the stream 
                        inputStream = entity.getContent();

                        // decoding stream data back into image Bitmap that android understands
                        final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

                        return bitmap;
                    } finally {
                        if (inputStream != null) {
                            inputStream.close();
                        }
                        entity.consumeContent();
                    }
                }
            } catch (Exception e) {
                // You Could provide a more explicit error message for IOException
                getRequest.abort();
                Log.e("ImageDownloader", "Something went wrong while" +
                        " retrieving bitmap from " + url + e.toString());
            } 

            return null;
        }
    }
}

由於目前沒有例子的評論欄位(或者我沒有找到它或我沒有獲得它的許可),這裡有一些評論:

這是使用 AsyncTask 可以完成的一個很好的例子。

然而,該示例目前存在問題

  • 可能的記憶體洩漏
  • 如果在非同步任務完成之前不久有螢幕旋轉,應用程式崩潰。

詳情見: