AsyncTask源碼解析

優缺點:

優點
它封裝了Handler,使創建異步任務變得更加簡單,相較于Handler+Thread的模式,不再需要編寫任務線程和Handler實例即可完成相同的任務。
缺點

  • 不適合長時間的后臺操作(適合幾秒鐘的操作)
  • AsyncTask的生命周期沒有跟Activity的生命周期同步
    AsyncTask不會隨著activity的銷毀而銷毀,它會一直執行, 直到doInBackground()方法執行完畢。如果在Activity銷毀之前,沒有取消 AsyncTask,有可能讓我們的AsyncTask崩潰(crash)。因為它在onPostExecute()中想要處理的view已經不存在了。所以,我們總是必須確保在銷毀活動之前取消任務。
    另外,即使我們正確地調用了cancle() 也未必能真正地取消任務。因為如果在doInBackgroud里有一個不可中斷的操作,比如BitmapFactory.decodeStream(),那么這個操作會繼續下去。
  • 容易內存泄漏
    如果AsyncTask被聲明為Activity的非靜態的內部類,那么AsyncTask會保留一個對創建了AsyncTask的Activity的引用。如果Activity已經被銷毀,AsyncTask的后臺線程還在執行,它將繼續在內存里保留這個引用,導致Activity無法被回收,引起內存泄露。
  • 結果丟失
    屏幕旋轉或Activity在后臺被系統殺掉等情況會導致Activity的重新創建,之前運行的AsyncTask會持有一個之前Activity的引用,這個引用已經無效,這時調用onPostExecute()再去更新界面將不再生效。

要注意的地方:

  • 并行還是串行
    在Android 1.6之前的版本,AsyncTask是串行的,在1.6至2.3的版本,改成了并行的。在2.3之后的版本又做了修改,可以支持并行和串行,當想要串行執行時,直接執行execute()方法,如果需要并行執行,則要執行executeOnExecutor(Executor)。
  • 在activity的destroy()中執行cancel()后,AsyncTask將執行完doInBackground(),但不再執行onPostExecute()和onProgressUpdate(),而是執行onCancelled().

使用:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private ProgressDialog mDialog;
    private TextView mTextView;
    MyAsyncTask myAsyncTask;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.id_tv);
        mDialog = new ProgressDialog(this);
        mDialog.setMax(100);
        mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mDialog.setCancelable(true);
        myAsyncTask = new MyAsyncTask();
        myAsyncTask.execute();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (myAsyncTask != null)
            myAsyncTask.cancel(true);
    }

    private class MyAsyncTask extends AsyncTask<Void, Integer, Void> {

        @Override
        protected void onPreExecute() {
            mDialog.show();
            Log.e(TAG, Thread.currentThread().getName() + " onPreExecute ");
        }

        @Override
        protected Void doInBackground(Void... params) {

            // 模擬數據的加載,耗時的任務
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(80);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                publishProgress(i);
            }

            Log.e(TAG, Thread.currentThread().getName() + " doInBackground ");
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            mDialog.setProgress(values[0]);
            Log.e(TAG, Thread.currentThread().getName() + " onProgressUpdate ");
        }

        @Override
        protected void onPostExecute(Void result) {
            // 進行數據加載完成后的UI操作
            mDialog.dismiss();
            mTextView.setText("LOAD DATA SUCCESS ");
            Log.e(TAG, Thread.currentThread().getName() + " onPostExecute ");
        }
    }
}

效果:


源碼分析:

好了,上述我們基本得到了AsyncTask的執行流程,現在我們通過源碼并且結合上述案例,對AsyncTask做更為細致的分析:

一、 首先我們是在主線程上創建了一個繼承AsyncTask的MyAsynTask類: MyAsynTask myAsynTask = new MyAsynTask();由于是繼承關系,上述方法執行了MyAsynTask的父類AsyncTask的構造方法:

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);
        }
    };

    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類初始化了兩個類:mWorker 與mFuture:
  • WorkerRunnable是實現了Callable接口的抽象方法;FutureTask實現了RunnableFuture,而RunnableFuture接口繼承了Runnable和Future
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> 

public interface RunnableFuture<V> extends Runnable, Future<V>
public class FutureTask<V> implements RunnableFuture<V>

二、 調用myAsynTask對象的execute()方法,并且傳遞參數huhx;我們跟進去,發現實際的執行是:executeOnExecutor(sDefaultExecutor, params);

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;
}
  • 該方法首先是判斷mStatus狀態,如果是正在運行(RUNNING)或者已經結束運行(FINISHED),就會拋出異常。
  • 接著設置狀態為運行,執行onPreExecute()方法,并把參數的值賦給mWorker.mParams;
  • 于是Executor去執行execute的方法,學過java多線程的都知道。這個方法是開啟一個線程去執行mFuture中的run()方法
  • 注意mFuture和mWorker都是在AsyncTask的構造方法中初始化過的

三、根據上述講解,將執行mFuture的run,也就是將執行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);
}
  • 設置最高優先級,接著執行了doInBackground()方法,注意它的參數mParams正是mWorker的屬性mParams,而我們在之前有過這樣的代碼:mWorker.mParams = params;因此doInBackground方法的參數就是execute方法傳遞的參數
  • 好了,執行到了我們重寫的doInBackground()方法了,現在要回到MyAsynTask類中的來了,在doInBackground中執行了publishProgress方法。跟進去,我們看一下代碼
protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}

四、 上述我們講到了Handler的部分了,很自然的我們是不是要看一下Handler的handleMessage方法呢?跟進去,我們看一下

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;
    }
}

由于上述傳遞的消息是MESSAGE_POST_PROGRESS,所以result.mTask.onProgressUpdate(result.mData);得到執行,那么result.mTas是什么了,對了,就是我們的AsyncTask。由于AsyncTaskResult的第二個參數是values是publishProgress的參數,那么onProgressUpdate中的參數就是publishProgress方法的參數,如下:

  private static class AsyncTaskResult<Data> {
    final AsyncTask mTask;
    final Data[] mData;

    AsyncTaskResult(AsyncTask task, Data... data) {
        mTask = task;
        mData = data;
    }
}

五、 好了,我們要回到第三步的,最后一個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;
}

看到沒有,發送了MESSAGE_POST_RESULT信息,于是在第六步中的handleMessage方法的代碼中result.mTask.finish(result.mData[0])得到執行,在這個方法中,執行了AsyncTask的finish方法

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

在這代碼中,如果沒有取消,那么就執行onPostExecute方法,記得result是什么嗎?Result result = doInBackground(mParams);正是doInBackground方法返回的結果
最后將狀態設置為Status.FINISHED,還記得我們在AsyncTask的簡要說明的第四點說過嗎?一個任務實例只能執行一次,如果執行第二次將會拋出異常,因為執行完一次之后,狀態變成FINISHED,在executeOnExecutor方法中會有如下判斷:會報異常的!

case FINISHED:
        throw new IllegalStateException("Cannot execute task:"
                + " the task has already been executed "
                + "(a task can be executed only once)");
}

六、對于在任務的取消中那些說明,我們額外去對它的源碼做一些簡單的分析:
調用onCancelled(true)時,系統會發送MESSAGE_POST_RESULT信息,也就是提前進入了上述第五步:執行如下代碼

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

由于設置了onCancelled(true),所以onCancelled(result)方法得到執行。之后再設置狀態為Status.FINISHED;

建議閱讀

Java多線程:Executor,Executors,Future,Callable,Runnable等

參考

為什么Android AsyncTask的使用要遵循五大原則

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容