Android中的AsyncTask解析

概述

     AsyncTask是一個抽象的泛型類,它是一個輕量級的異步任務類,它可以在線程池中執行後臺任務,然後把執行的進度和後臺結果傳遞給主線程並在主線程中更新UI。AsyncTask封裝了Thread和Handler。但是AsyncTask並不適合耗時的後臺任務,對於耗時的後臺任務建議使用線程池。它提供了Params、Progress和Result三個泛型參數。提供了四個核心方法(JAVA中...表示參數的數量不定,它是一種數組性參數):
     ①、onPreExecute():運行在主線程,在異步任務執行之前調用,通常用來進行一些初始化操作。
     ②、doInBackground(Params...):在線程池中執行,執行比較耗時的操作。可以通過publishProgress()方法來更新任務的進度。
     ③、onProgressUpdate(Progress...):在主線程中執行,當後臺任務的執行進度發生改變時此方法會被調用。
     ④、onPostExecute(Result):在主線程中執行,在異步任務執行之後此方法會被調用。參數爲doInBackground(Params...)的返回值。
     繼承自AsyncTask的類至少需要重寫doInBackground()方法


注意事項

     ①、AsyncTask必須在主線程中創建,execute()方法也必須在主線程中調用。
     ②、不要手動調用onPreExecute()、onPostExecute()、doInBackground()、onProgressUpdate()這幾個方法。
     ③、一個AsyncTask對象只能執行一次。
     ④、Android 1.6之前,AsyncTask是串行執行任務的,Android 1.6時AsyncTask開始使用線程池處理並行任務。但從Android 3.0開始爲了避免AsyncTask所帶來的併發錯誤,AsyncTask又採用一個線程來串行執行任務。


執行流程分析

     以一個異步加載圖片的例子,按着執行過程一步步分析。
     我們首先寫了繼承了AsyncTask的MyTask類,三個參數分別爲URL、Float、Bitmap。重寫了構造方法,要求傳入ImageView,以便在onPostExecute()方法中把圖片設置上去。重寫了doInBackground(URL...params)方法,使用了HttpURLConnection來獲取圖片,並通過調用publicProgress()方法來更新進度。onProgressUpdate(Float...values)中使用Log輸出的方式展示圖片加載進度、在onPostExecute(Bitmap)中將圖片設置到ImageView上。

public class MyTask extends AsyncTask<URL, Float, Bitmap> {

    private ImageView iv = null;

    public MyTask(ImageView iv) {
        this.iv = iv;
    }

    @Override
    protected Bitmap doInBackground(URL... params) {
        Log.e("MyTask", "開始");
        URL url = params[0];
        Bitmap bitmap = null;
        try {
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5000);
            conn.setRequestMethod("GET");
            int code = conn.getResponseCode();
            InputStream is = conn.getInputStream();
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            int count = 0;
            int length = conn.getContentLength();
            while ((len = is.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
                count += len;
                publishProgress(count * 1.0f / length);     
            }
            bos.close();
            is.close();
            byte[] byteArray = bos.toByteArray();
            bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return bitmap;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected void onPostExecute(Bitmap bit) {
        super.onPostExecute(bit);
        iv.setImageBitmap(bit);
    }

    @Override
    protected void onProgressUpdate(Float... values) {
        super.onProgressUpdate(values);
        Log.e("MyTask", "進度:" +values[0]);
    }
}

    
      在主線程中創建MyTask實例:

MyTask t = new MyTask(mIv);
t.execute(url);
   
     JAVA規定如果子類構造方法中沒有顯式調用父類的構造方法(也就是沒有super()語句),會默認調用父類的無參構造方法。故調用AsyncTask的構造方法,如下:

public AsyncTask() {
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);

            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //noinspection unchecked
            return postResult(doInBackground(mParams));
        }
    };

    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            try {
                postResultIfNotInvoked(get());
            } catch (InterruptedException e) {
                android.util.Log.w(LOG_TAG, e);
            } catch (ExecutionException e) {
                throw new RuntimeException("An error occured while executing doInBackground()",
                        e.getCause());
            } catch (CancellationException e) {
                postResultIfNotInvoked(null);
            }
        }
    };
}

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    Params[] mParams;
}
     上面的構造方法中,WorkerRunnable是一個維護了Params[]數組的Callable的子類抽象類,重寫了它的call方法後在構造方法中創建了一個WorkerRunnable對象mWorker。FutureTask是一個實現了FutureRunnable的類,提供了start、cancel方法,也提供了查詢任務當前狀態的方法,可以通過FutureTask類封裝Runnable或者Callable對象。在AsyncTask構造方法中,創建了一個FutureTask的實例mFuture封裝了mWorker,並重寫了done()方法。

     接下調用MyTask對象的execute(URL)方法:

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

@MainThread
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)");
        }
    }

    mStatus = Status.RUNNING;

    onPreExecute();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}
     由上面的源碼可以看出,execute()方法中調用了executeOnExecutor()方法,並傳入sDefaultExecutor。sDefaultExecutor是一個串行的線程池,一個進程中的所有串行任務都在這個線程池中排隊執行。在executeOnExecutor()方法中,先判斷了當前任務的狀態。如果非PENDING,而是RUNNING或者FINISHED,說明這個任務要麼正在執行,要麼已經結束,拋出異常。否則將這個任務的狀態設置爲RUNNING,執行onPreExecute()方法。將傳進的params參數設置給mWorker,然後調用sDefaultExecutor的execute()方法 。

     接下來進入串行線程池sDefaultExecutor的execute方法看看:

private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

     這個串行線程池SerialExecutor實現了Executor接口。重寫了Executor類中的唯一方法execute(Runnable)。在execute()方法中,先將傳進來的Runnable對象mFuture再封裝成一個Runnable,然後添加進mTask隊列。然後判斷當前活動的任務是否爲null,是的話就調用scheduleNext()方法安排下一個任務。在scheduleNext()方法中,如果任務隊列不爲空則出隊一個任務,使用線程池THREAD_POOL_EXECUTOR來執行任務。
     JVM會調用mActive的run()方法。在它的run()方法中,先調用了mFutrue的run()方法(mFuture被封裝爲一個Runnable加入mTasks隊列),再在finally塊中調用scheduleNext(),進行下一個任務。
     在調用mFuture的run()方法時,會調用到mWorker的call()方法。如下:

public void run() {
   ......
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
               ......
            }
    } finally {
     ......
    }
}

     故此時回到mWorker的call()方法:

mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);

        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        //noinspection unchecked
        return postResult(doInBackground(mParams));
    }
};

     call()方法中調用了doInBackground(mParams)方法,執行我們重寫的doInBackground方法。然後把該方法的返回結果作爲參數傳入postResult(Result)方法中。來到postResult()方法:

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}
     在postResult(Result)方法中,通過handler獲取到消息池中的一個消息。注意這裏的getHandler()方法獲取一個靜態的Handler對象sHandler,sHandler是用於將執行環境切換到主線程中的Handler,這也就是爲什麼AsyncTask必須在主線程中加載的原因。它是實現了Handler的靜態內部類InternalHandler的實例。如下:

private static Handler getHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            sHandler = new InternalHandler();
        }
        return sHandler;
    }
}

private static class InternalHandler extends Handler {
    public InternalHandler() {
        super(Looper.getMainLooper());
    }

    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}
     InternalHandler重寫了handleMessage(Message)方法。回到postResult(Result)方法中。獲取到sHandler之後,把傳入的Result封裝成AsyncTaskResult對象,作爲消息的obj傳進obtainMessage(int, Object)方法中。內部靜態類AsyncTaskResult類中維護了一個AsyncTask成員變量和Data數組(即Result)。接下來通過sHandler進行消息的分發。在handleMessage中,匹配到MESSAGE_POST_RESULT,於是調用AsyncTaskResult中的AsyncTask對象的finish()方法,把結果傳進去:

private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}
     在finish方法中,根據情況調用onCancelled()方法或onPostExecute()方法,最後把任務的狀態置爲Status_FINISHED。至此一個任務就執行完畢。

     最後再看看任務進度的更新。回到MyTask類中,在doInBackground()方法中,我調用了publishProgress(float)方法,這個方法用於發送一個MESSAGE_POST_PROGRESS消息給主線程。匹配到sHandler的handleMessage()中的MESSAGE_POST_PROGRESS,執行自己重寫的onProgressUpdate()方法。

@WorkerThread
protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}


     在Android 3.0以上的阪本中任務默認是串行執行的,要使任務並行執行,只要採用AsyncTask的executeOnExecutor方法。如下:

new MyTask(...).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");


發佈了36 篇原創文章 · 獲贊 3 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章