1. 程式人生 > >4.下載圖片檔案

4.下載圖片檔案

4.1 問題

應用程式需要從Web或其他遠端伺服器下載一張圖片並顯示。

4.2 解決方案

(API Level 4)
使用AsyncTask在後臺執行緒中下載資料。AsyncTask是封裝類,它可以很方便地讓需要長時間允許操作的執行緒在後臺允許;同樣,它通過一個內部執行緒池管理執行緒的併發。除了管理後臺執行緒外,在操作執行前、中、後都會提供回撥方法,讓你可以做任何需要在主UI執行緒中進行的更新。

4.3 實現機制

在下載圖片的環境中,我們會建立ImageView的一個子類,叫做WebImageView,它會從遠端來源中延遲載入一張圖片並且在該圖片可用時就顯示它。下載過程會在一個AsyncTask操作中執行(參見以下程式碼清單)。
WebImageView

public class WebImageView extends ImageView {

    private Drawable mPlaceholder,mImage; 

    public WebImageView(Context context) {
        super(context,null);
    }

    public WebImageView(Context context, AttributeSet attrs) {
        super(context, attrs,0);
    }

    public WebImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setPlaceholder(Drawable drawable) {
        this.mPlaceholder = drawable;
        if (mImage == null){
            setImageDrawable(mPlaceholder);
        }
    }

    public void setPlaceholder(int resid) {
        mPlaceholder = getResources().getDrawable(resid);
        if (mImage == null){
            setImageDrawable(mPlaceholder);
        }
    }
    
    public void setImageURl(String url) {
        DownloadTask task = new DownloadTask();
        task.execute(url);
    }


    private class DownloadTask extends AsyncTask<String,Void,Bitmap> {
        @Override
        protected Bitmap doInBackground(String... params) {
            String url = params[0];
            try {
                URLConnection connection = (new URL(url)).openConnection();
                InputStream is = connection.getInputStream();
                BufferedInputStream bis = new BufferedInputStream(is);

                ByteArrayBuffer baf = new ByteArrayBuffer(50);
                int current = 0;
                while ((current = bis.read()) != -1) {
                    baf.append((byte) current);
                }
                byte[] imageData = baf.toByteArray();
                return BitmapFactory.decodeByteArray(imageData, 0, imageData.length);
            } catch (Exception exc) {
                return null;
            }
        }

        @Override
        protected void onPostExecute(Bitmap result) {
            mImage = new BitmapDrawable(getContext().getResources(),result);
            if (mImage != null){
                setImageDrawable(mImage);
            }
        }
    }
}

正如你所看到的那樣,WebImageView是Android的mageView小部件的一個簡單擴充套件。在遠端內容下載完成之前,setPlaceholderImage()會為要顯示的圖片設定一個本地Drawable。大多數有趣的工作都是從使用setImageUrl()向檢視傳入一個給定遠端URL開始的,該方法表示自定義的AsyncTask開始工作了。
請注意,AsyncTask是強型別化的,它需要三個值:輸入引數、進度值、結果值。在這種情況下,會傳遞給任務的execute()方法一個字串,而後臺操作應該返回一個Bitmap。對於中間的進度值,我們在本例中並不會使用,因此它被設定為Void。繼承AsyncTask後,唯一需要實現的方法就是doInBackground(),它定義了後臺執行緒中需要大量執行的操作。在前面的示例中,這裡是與提供的遠端URL進行連線以及下載圖片的地方。完成後,我們會試圖用下載的資料建立一個Bitmap。發生任何錯誤時,操作將中止並返回null。

要點:
Android UI類是執行緒不安全的。確保在更新UI時使用執行在主執行緒上的回撥方法。不要在doInBackground()中更新檢視。

以下兩段程式碼清單展示了在一個Activity中使用這個類的示例。因為這個類不是android.widget或android.view包的一部分,所以在XML中使用它時必須先指定它的完全限定包名。

res/layout/main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.jennyni.webimageviewdemo.MainActivity">
    
    <com.jennyni.webimageviewdemo.WebImageView
        android:id="@+id/webImage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    </com.jennyni.webimageviewdemo.WebImageView>

</LinearLayout>

示例Activity

public class WebImageActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        WebImageView  imageView = (WebImageView)findViewById(R.id.webImage);
        imageView.setPlaceholderImage(R.drawable.ic_launcher);
        imageView.setImageUrl("http://lorempixel.com/400/200");

    }
}

本例中,首先會設定一張本地圖片(應用程式圖示)作為WebImageView的佔位圖片,這張圖片會立刻顯示給使用者。然後我們會告訴檢視從Web上獲取Apress的徽標圖片。如前所述,這裡會在後臺下載圖片,下載完成後會替換掉檢視中的佔位圖片。正是因為建立後臺操作的簡單性,Android團隊才會把AsyncTask叫作“無痛苦使用執行緒”。