1. 程式人生 > >【安卓筆記】AsyncTask

【安卓筆記】AsyncTask

public abstract class AsyncTask<Params, Progress, Result>
泛型引數:     Params:執行後臺任務所需的引數。比如如果你是網路操作的話,maybe需要傳入一個uri     Progress:後臺任務執行的進度。比如下載進度,通常是一個Integer,這個引數會被onProgressUpdate呼叫,然後去更新UI,比如ProgressBar或者progressDialog。     Result:後臺任務執行所返回的結果。比如你想下載一張圖片,那可以將Result指定為Bitmap 如果你的AsyncTask不需要某個引數,可以直接指定為Void.
方法簡介:  void onPreExecute():在後臺任務執行之前會被呼叫,這個方法是由UI執行緒呼叫的,可以用來操作UI,比如你在這個方法裡可以設定ProgressBar的可見性。 Result doInBackground(Params... params):這個方法是AsyncTask類中唯一的一個抽象方法,開發者必須複寫此方法。從名字上也可看出,這個方法用來執行後臺耗時任務,工作在後臺執行緒之上。onPreExecute執行完之後便會執行這個方法。這個方法中的引數是由主執行緒中呼叫AsyncTask類的execute方法時傳進來的,方法返回的Result會被傳遞給onPostExecute,用來更新UI。另外這個方法中可以根據執行進度呼叫
publishProgress方法將進度傳遞給onProgressUpdate方法,然後onProgressUpdate方法也會更新UI(通常是ProgressBar的進度)。
void onProgressUpdate(Progress... values):當你在doInbackground方法中調了publishProgress時,此方法會被呼叫。這個方法也是工作在UI執行緒上。通常用這個方法更新progressBar的進度以給使用者一個友好提示。 void onPostExecute(Result result):後臺任務執行完畢會呼叫此方法,引數即doInBackground方法返回的那個。這個方法用於更新UI。
除了上面4個方法,還有兩個方法常用: void onCancelled(Result result):這個方法是當用戶取消了操作所執行。 final void publishProgress(Progress... values)由doInBackground方法呼叫,及時釋出後臺任務的執行進度。不可以複寫。 使用AsyncTask需要注意的問題:
  • Task的例項必須在UI 執行緒中建立;
  • execute方法必須在UI 執行緒中呼叫;
  • 不要手動的呼叫onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)這幾個方法;
  • 該task只能被執行一次,多次呼叫execute時將會出現異常;
  • 不要在doInBackground方法中更新UI。
這裡特別需要注意的就是第四點,非同步任務只能呼叫一次,否則會拋異常,看原始碼就一目瞭然了:
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            }
        }
    ... ...
所以你希望多次呼叫的話,只能new多個AsyncTask了,像這樣:
new AsyncTask(){}.execute(..);
使用步驟: 1.繼承AsyncTask類,在doInbackground方法中寫好邏輯。 2.呼叫execute方法。 像這樣:
new AsyncTask<Void, Void, Void>() {
    protected Void doInBackground(Void... params)
    {
        //TODO handle task
        return null;
    }
    @Override
    protected void onPostExecute(Void result) 
    {
        //TODO update UI
    }

}.execute(null);
以下載網路圖片為例: 佈局:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
        <ImageView
            android:id="@+id/iv_show"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1000" />
        <Button
            android:id="@+id/but_down"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="下載" />
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical" >
        <ProgressBar
            android:id="@+id/pb"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="gone"
            android:layout_gravity="center" />
        <TextView
            android:id="@+id/tv_progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:background="#fff"
            android:visibility="gone"
            android:text="下載進度:"
            android:textColor="#000" />
    </LinearLayout>
</FrameLayout>
activity:
package com.example.asynctaskdemo4;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity implements OnClickListener
{
    private static final String PATH = "http://10.46.191.198:8080/demo.bmp";
    private Button but_down = null;
    private ImageView iv_show = null;
    private TextView tv_progress = null;
    private ProgressBar pb = null;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        but_down = (Button) findViewById(R.id.but_down);
        iv_show = (ImageView) findViewById(R.id.iv_show);
        tv_progress = (TextView) findViewById(R.id.tv_progress);
        pb = (ProgressBar) findViewById(R.id.pb);
        
        but_down.setOnClickListener(this);
    }
    @Override
    public void onClick(View v)
    {
        if (R.id.but_down == v.getId())
        {
            new DownloadImageTask().execute(PATH);
        }
    }
    private class DownloadImageTask extends AsyncTask<String, Integer, Bitmap>
    {
        @Override
        protected void onPostExecute(Bitmap result)
        {
            if (result != null)
            {
                tv_progress.setVisibility(View.GONE);
                pb.setVisibility(View.GONE);
                
                iv_show.setImageBitmap(result);
            } else
            {
                tv_progress.setVisibility(View.GONE);
                pb.setVisibility(View.GONE);
                Toast.makeText(MainActivity.this,"下載失敗",0).show();
            }
        }
        @Override
        protected Bitmap doInBackground(String... params)
        {
            String path = params[0];
            HttpClient client = new DefaultHttpClient();
            HttpGet get = new HttpGet(path);
            try
            {
                HttpResponse resp = client.execute(get);
                if(resp.getStatusLine().getStatusCode() == 200)
                {
                    HttpEntity entity = resp.getEntity();
                    if(entity == null)
                    {
                        return null;
                    }
                    long total_length = entity.getContentLength();//獲取檔案總長
                    InputStream is = entity.getContent();
                    ByteArrayOutputStream bous = new ByteArrayOutputStream();
                    int len = 0;
                    byte[] buf = new byte[1024];
                    int current_len = 0;
                    int progress = 0;//當前下載進度
                    while((len = is.read(buf))!= -1)
                    {
                        current_len+=len;
                        bous.write(buf, 0, len);
                        //注意progress的寫法哦
                        progress = (int) ((current_len/(float)total_length)*100);
                        this.publishProgress(progress);
                    }
                    is.close();
                    byte[] data = bous.toByteArray();
                    Options opts = new Options();
                    opts.inSampleSize = 2;//簡單起見直接指定縮放比例
                    Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opts);
                    return bitmap;
                }
            } catch (Exception e)
            {
                e.printStackTrace();
            }
            return null;
        }
        @Override
        protected void onProgressUpdate(Integer... values)
        {
            tv_progress.setText("下載進度:"+values[0]);
        }
        @Override
        protected void onPreExecute()
        {
            tv_progress.setVisibility(View.VISIBLE);
            pb.setVisibility(View.VISIBLE);
        }
    }
}
顯示效果: