AsyncTask完全解析篇

AsyncTask的基本用法

AsyncTask本身是一個抽象類,若想要使用它,需要創(chuàng)建一個子類去繼承它,且必須復(fù)寫它的抽象方法doInBackground()。
在繼承AsyncTask類時需要指定三個泛型參數(shù):

public abstract class AsyncTask<Params, Progress, Result> {
......
}

這三個參數(shù)的用途:

  • Params
    在執(zhí)行AsyncTask的execute(Params)時傳入的參數(shù),可用于在doInBackground()任務(wù)中使用。
  • Progress
    后臺執(zhí)行任務(wù)進度值的類型。
  • Result
    后臺任務(wù)執(zhí)行完畢后,如果需要結(jié)果返回,可指定為Result類型的泛型數(shù)據(jù)作為返回值類型。

舉個具體的例子:

/**
 * Created by Kathy on 17-2-17.
 */

public class MyTask extends AsyncTask<Object, Integer, Boolean> {


    @Override
    protected Boolean doInBackground(Object... params) {
        return null;
    }
}

第一個泛型參數(shù)指定為Object,表示在執(zhí)行AsyncTask任務(wù)時,execute(Object)方法需要傳入Object類型的參數(shù)給后臺;
第二個泛型參數(shù)指定為Integer,表示將使用整型數(shù)據(jù)作為進度顯示單位;
第三個泛型參數(shù)指定為Boolean,標識用布爾型數(shù)據(jù)來反饋執(zhí)行結(jié)果。

AsyncTask的基本方法及用途

  1. onPreExecute()
    這個方法在后臺任務(wù)執(zhí)行前調(diào)用,用于進行界面的初始化,比如顯示一個進度條對話框等。

  2. doInBackground(Params... params)
    必須重寫的抽象方法!這個方法中的所有代碼都會在子線程中運行,我們應(yīng)該在這里去處理所有的耗時任務(wù)。任務(wù)一旦完成就可以通過return語句來將任務(wù)的執(zhí)行結(jié)果進行返回,如果AsyncTask的第三個泛型參數(shù)指定的是Void,就可以不返回任務(wù)執(zhí)行結(jié)果。注意,在這個方法中是不可以進行UI操作的,如果需要更新UI,比如說反饋當前任務(wù)的執(zhí)行進度,可以調(diào)用publishProgress(Progress...)方法來完成。

  3. publishProgress(Progress... values)
    反饋當前任務(wù)的執(zhí)行進度,在doInBackground()中調(diào)用。

  4. onProgressUpdate(Progress... values)
    當在后臺任務(wù)中調(diào)用了publishProgress(Progress...)方法后,這個方法就很快會被調(diào)用,方法中攜帶的參數(shù)就是在后臺任務(wù)中傳遞過來的。在這個方法中可以對UI進行操作,利用參數(shù)中的數(shù)值就可以對界面元素進行相應(yīng)更新。

  5. onPostExecute(Result result)
    當后臺任務(wù)執(zhí)行完畢并通過return語句進行返回時,這個方法就很快會被調(diào)用。返回的數(shù)據(jù)會作為參數(shù)傳遞到此方法中,可以利用返回的數(shù)據(jù)來進行一些UI操作,比如說提醒任務(wù)執(zhí)行的結(jié)果,以及關(guān)閉掉進度條對話框等。

  6. boolean cancel(boolean mayInterruptIfRunning)
    試圖取消任務(wù)的執(zhí)行,但是如果任務(wù)已經(jīng)被完成會取消失敗。執(zhí)行這個方法會導致doInBackground()執(zhí)行完后不會去執(zhí)行onPostExecute()方法,而是執(zhí)行onCancelled()方法。

  7. boolean isCancelled()
    如果在任務(wù)執(zhí)行完成前被取消,該方法返回true。

  8. onCancelled(Result result)
    任務(wù)被取消后回調(diào)的方法。

簡單實例

    class DownloadImageTask extends AsyncTask<Void, Integer, Boolean> {  
      
        @Override  
        protected void onPreExecute() {  
            // 顯示進度條
            progressBar.show();  
        }  
      
        @Override  
        protected Boolean doInBackground(Void... params) { 
                   int downloadprogress = doDownload();  
                   // 通知進度條更新UI
                   publishProgress(progress);
            return true;  
        }  
      
        @Override  
        protected void onProgressUpdate(Integer... values) {  、
            // 更新進度條
            progressBar.setProgress(values[0] );  
        }  
      
        @Override  
        protected void onPostExecute(Boolean result) {  
            // 進度條消失
            progressBar.setVisibility(View.INVISIBLE);
            progressBar.setProgress(0);
        }  
    }  

在UI線程里執(zhí)行上述任務(wù),只需要調(diào)用如下execute()方法即可:

new DownloadImageTask().execute(); 

源碼角度分析AsynTask

啟動AsynTask任務(wù),需要構(gòu)建它的實例,首先來看AsyncTask的構(gòu)造函數(shù):

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                return postResult(result);
            }
        };
e
        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 occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

AsyncTask的構(gòu)造函數(shù)只是初始化了兩個變量mWorker和mFuture,并在初始化mFuture時傳入了mWorker作為參數(shù)。

如果需要啟動某個任務(wù),需要調(diào)用AsyncTask的execute()方法:

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

    //向下調(diào)用
    @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)");
            }
        }

        // 修改狀態(tài)為運行狀態(tài)
        mStatus = Status.RUNNING;

        // 最先執(zhí)行onPreExecute()方法
        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);
        return this;
    }

這里判斷了任務(wù)的狀態(tài),任務(wù)有三種狀態(tài):PENDING / RUNNING / FINISHED

    /**
     * Indicates the current status of the task. Each status will be set only once
     * during the lifetime of a task.
     */
    public enum Status {
        /**
         * Indicates that the task has not been executed yet.
         */
        PENDING,
        /**
         * Indicates that the task is running.
         */
        RUNNING,
        /**
         * Indicates that {@link AsyncTask#onPostExecute} has finished.
         */
        FINISHED,
    }

可以發(fā)現(xiàn)execute()執(zhí)行方法中,最先被調(diào)用的是onPreExecute()方法。接下來發(fā)現(xiàn)調(diào)用了exec.execute(mFuture)這個語句并將mFuture這個FutureTask對象傳遞進來,那么exec是Executor類型的,且向上追溯,是執(zhí)行execute()方法時由sDefaultExecutor傳遞進來的,且看sDefaultExecutor定義的地方:

    /**
     * An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

可以看到sDefaultExecutor的賦值是一個常量,且為SerialExecutor類的實例,則exec.execute(mFuture)的執(zhí)行方法可以追溯到SerialExecutor的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類中也有一個execute()方法,這個方法里的所有邏輯就是在子線程中執(zhí)行的。且執(zhí)行execute()方法時傳入的Runnable對象為FutureTask對象,所以會調(diào)用到FutureTask類的run()方法。最終的最終,會執(zhí)行到mWorker對象的call()方法:

            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                return postResult(result);
            }

可以看到以上方法中調(diào)用到了doInBackground(mParams)去做耗時處理,所以doInBackground()實在onPreExcute()方法之后執(zhí)行,且在子線程中執(zhí)行。
接下來,將返回值類型為Result的doInBackground()的執(zhí)行結(jié)果傳遞到postResult(result)中:

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

postResult()方法的作用是什么呢?它使用Handler發(fā)送一條消息MESSAGE_POST_RESULT,并攜帶上AsyncTaskResult對象,這個對象中攜帶任務(wù)執(zhí)行的結(jié)果。那么接收這條消息的回調(diào)方法在哪兒呢?這個取決于發(fā)送消息的Handler:

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

InternalHandler的回調(diào)方法如下,且發(fā)現(xiàn)InternalHandler的Looper是主線程的,所以InternalHandler的回調(diào)方法是執(zhí)行在主線程中!

    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可以看到:
1.如果收到MESSAGE_POST_RESULT消息,會執(zhí)行finish()方法;
2.如果收到MESSAGE_POST_PROGRESS消息,會執(zhí)行onProgressUpdate()方法。
我們來看finish()方法:

    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

邏輯很清晰了,執(zhí)行完doInbackground()方法后,返回的結(jié)果通過IntenalHandler的實例發(fā)送消息傳遞給IntenalHandler的回調(diào)方法,最后,在主線程中執(zhí)行finish()方法;如果任務(wù)執(zhí)行被取消,則執(zhí)行onCancelled()方法;如果沒有被取消,則繼續(xù)向下執(zhí)行onPostExecute()方法,且將狀態(tài)設(shè)置為Status.FINISHED。

可以看到,在IntenalHandler的回調(diào)方法中接收另外一個消息MESSAGE_POST_PROGRESS,這個消息是在哪兒發(fā)送的呢?

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

以上可知,在doInBackground()方法中通過調(diào)用publishProgress()實現(xiàn)子線程 和UI線程通信,publishProgress()通過handler發(fā)送消息,在主線程中接收消息的地方會執(zhí)行到onProgressUpdate()方法去更新UI。

綜上可知,Asynctask背后使用的仍然是Handler異步消息處理機制,只是在源碼層做了封裝,我們在使用時,不必考慮而已。

使用AsyncTask的注意事項

  • AsynTask的實例需要在UI線程中創(chuàng)建
  • execute(Params... params)方法必須在UI線程中調(diào)用
  • AsynTask的doInBackground(Prams)方法執(zhí)行異步任務(wù)運行在子線程中,其他方法運行在主線程中,可以操作UI組件。
  • 運行后,可以隨時調(diào)用AsynTask對象的cancel(boolean)方法取消任務(wù),如果成功,調(diào)用isCancelled()將返回true,并且不再執(zhí)行onPostExcute()方法,而是執(zhí)行onCancelled() 方法。

你必須知道的AsyncTask的缺陷

1.生命周期
AsyncTask不隨著Activity的生命周期的銷毀而銷毀,這使得AsyncTask執(zhí)行完成后,Activity可能已經(jīng)不在了。AsyncTask會一直執(zhí)行doInBackground()方法直到執(zhí)行結(jié)束。一旦上述方法結(jié)束,會依據(jù)情況進行不同的操作。
但是,AsyncTask的cancel(mayInterruptIfRunning)可以通過傳入一個布爾值來打斷執(zhí)行的任務(wù),mayInterruptIfRunning設(shè)置為true,表示打斷任務(wù),正在執(zhí)行的任務(wù)會繼續(xù)執(zhí)行直到完成。但是可以在doInBackground()方法中有一個循環(huán)操作,通過調(diào)用isCancelled()來判斷任務(wù)是否被打斷,如果isCancelled() == true,則應(yīng)避免再執(zhí)行后續(xù)的一些操作。
總之,使用AsyncTask需要確保AsyncTask正確地取消。

2.內(nèi)存泄露風險
在Activity中使用非靜態(tài)匿名內(nèi)部AsyncTask類,由于Java內(nèi)部類的特點,AsyncTask內(nèi)部類會持有外部類的隱式引用。由于AsyncTask的生命周期可能比Activity的長,當Activity進行銷毀AsyncTask還在執(zhí)行時,由于AsyncTask持有Activity的引用,導致Activity對象無法回收,進而產(chǎn)生內(nèi)存泄露。

3.結(jié)果丟失
另一個問題就是在屏幕旋轉(zhuǎn)等造成Activity重新創(chuàng)建時AsyncTask數(shù)據(jù)丟失的問題。當Activity銷毀并重新創(chuàng)建后,還在運行的AsyncTask會持有一個Activity的非法引用即之前的Activity實例。

4.串行還是并行?

    new AsyncTask1.execute();  
    new AsyncTask2.execute();  

上面兩個任務(wù)是同時執(zhí)行呢,還是AsyncTask1執(zhí)行結(jié)束之后,AsyncTask2才能執(zhí)行呢?實際上是結(jié)果依據(jù)API不同而不同。

關(guān)于AsyncTask時串行還是并行有很多疑問,這很正常,因為它經(jīng)過多次的修改。
在1.6(Donut)之前:
在第一版的AsyncTask,任務(wù)是串行調(diào)度。一個任務(wù)執(zhí)行完成另一個才能執(zhí)行。由于串行執(zhí)行任務(wù),使用多個AsyncTask可能會帶來有些問題。所以這并不是一個很好的處理異步(尤其是需要將結(jié)果作用于UI試圖)操作的方法。

從1.6到2.3(Gingerbread)
后來Android團隊決定讓AsyncTask并行來解決1.6之前引起的問題,這個問題是解決了,新的問題又出現(xiàn)了。很多開發(fā)者實際上依賴于順序執(zhí)行的行為。于是很多并發(fā)的問題蜂擁而至。

3.0(Honeycomb)到現(xiàn)在
好吧,開發(fā)者可能并不喜歡讓AsyncTask并行,于是Android團隊又把AsyncTask改成了串行。當然這一次的修改并沒有完全禁止AsyncTask并行。你可以通過設(shè)置executeOnExecutor(Executor)來實現(xiàn)多個AsyncTask并行。關(guān)于API文檔的描述如下
If we want to make sure we have control over the execution, whether it will run serially or parallel, we can check at runtime with this code to make sure it runs parallel

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容