android高級---->AsyncTask的源碼分析


  在Android中實現異步任務機制有兩種方式,Handler和AsyncTask,它在子線程更新UI的例子可以參見我的博客(android基礎---->子線程更新UI)。今天我們通過一個小的案例去深入到AsyncTask的源碼,去更好的理解AsyncTask的原理。

 

目錄導航

  1.  AsyncTask的簡要說明
  2.  AsyncTask的的簡單的實際案例
  3.  AsyncTask的的源碼分析 
  4.  AsyncTask的原理總結
  5.  友情鏈接

 

AsyncTask的簡要說明

一、 AsyncTask的由來背景:

  為了簡化Handler的操作,Android1.5提供了工具類android.os.AsyncTask,它使創建異步任務變得更加簡單,不再需要編寫任務線程和Handler實例即可完成相同的任務。所以它封裝了Handler,變得更容易編寫。

 

二、 AsyncTask是一個抽象的函數,有三個在繼承的時候可以指定的參數:,這個后續會講解原因

public abstract class AsyncTask<Params, Progress, Result>
  • Params:啟動任務執行的輸入參數的類型,也就是task.execute()方法中傳入的參數類型,doInBackground方法的參數類型也是這個
  • Progress:方便記錄后台任務執行的進度,也就是onProgressUpdate和publishProgress方法參數的類型
  • Result:onPostExecute方法參數的類型,也是doInBackground方法返回的類型

 

三、 在AsyncTask類的使用過程中,以下的幾個方法比較常用的:

  • execute(Params... params):執行一個異步任務,需要我們在代碼中調用此方法,觸發異步任務的執行。
  • onPreExecute():在execute(Params... params)被調用后立即執行,一般用來在執行后台任務前對UI做一些准備工作。
  • doInBackground(Params... params),在onPreExecute()完成后立即執行,用於執行較為費時的操作,此方法將接收輸入參數和返回計算結果。doInBackground方法接收的參數就是execute()中傳入的參數:另外在執行過程中可以調用publishProgress(Progress... values)來更新進度信息。
  • onProgressUpdate(Progress... values),如果調用publishProgress(Progress... values)時,此方法被執行,直接將進度信息更新到UI組件上。否則不執行
  • onPostExecute(Result result),當后台操作結束時,此方法將會被調用,計算結果(就是doInBackground方法返回的結果)將做為參數傳遞到此方法中,直接將結果顯示到UI組件上

 

四、 在AsyncTask類的使用技巧,也可以說是使用規范:

  • 異步任務的實例必須在UI線程中創建,而且execute(Params... params)方法必須在UI線程中調用。
  • 一個任務實例只能執行一次,如果執行第二次將會拋出異常。這個將在后續源碼解析時說明
  • 不要手動調用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)這幾個方法
  • 不能在doInBackground(Params... params)中更改UI組件的信息。

 

五、 一般使用AsyncTask的場景:

  • schedule messages and runnables to be executed as some point in the future
  • enqueue an action to be performed on a different thread than your own.

 

六、 任務的取消

  • 任何時候調用cancel(boolean)方法,都可以取消任務。這個方法會產生的影響是:之后調用的iscancelled()會返回true。
  • 這個方法調用之后,onCancelled(Object)方法會在doInBackground(Object[]) 方法返回后執行,而不是onProgressUpdate(Progress... values)方法
  • 鑒於上述據說,為了確保任務是盡快取消,你應該定期在doInBackground()方法中始終檢查iscancelled()方法的返回值。如果可以的話,放在一個循環當中。

 

AsyncTask的的實際案例

了解了上述AsyncTask的基礎知識之后,現在我們通過實例去了解AsyncTask的使用和執行流程:

一、 代碼比較簡單,在MainActivity的onCreate方法中調用繼承MyAsynTask類的execute()方法:以下是全部代碼

package com.example.linux.asyntasktest;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

/**
* writer: huhx
*/
public class MainActivity extends AppCompatActivity { private final static String TAG = "MainActivity"; private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.textView); } // asynTask的使用 public void asynTask(View view) { MyAsynTask myAsynTask = new MyAsynTask(); myAsynTask.execute("huhx"); } // MyAsynTask內部類 private class MyAsynTask extends AsyncTask<String, Integer, String> { private final static String TAG = "MyHuhxAsynTask"; public MyAsynTask() { Log.i(TAG, "胡紅翔 constructor"); } @Override protected String doInBackground(String... params) { Log.i(TAG, "params: " + params[0]); publishProgress(13); return "world"; } @Override protected void onPostExecute(String s) { textView.setText(s); Log.i(TAG, "on post execute string: " + s); } @Override protected void onPreExecute() { super.onPreExecute(); Log.i(TAG, "on pre execute."); } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); Log.i(TAG, "on progress update : " + values[0]); } } }

 

二、 我們大致看一下執行的流程,執行的日志結果如下:

03-19 19:22:03.557 6442-6442/com.example.linux.asyntasktest I/MyHuhxAsynTask: 胡紅翔 constructor
03-19 19:22:03.557 6442-6442/com.example.linux.asyntasktest I/MyHuhxAsynTask: on pre execute.
03-19 19:22:03.567 6442-7172/com.example.linux.asyntasktest I/MyHuhxAsynTask: params: huhx
03-19 19:22:03.587 6442-6442/com.example.linux.asyntasktest I/MyHuhxAsynTask: on progress update : 13
03-19 19:22:03.587 6442-6442/com.example.linux.asyntasktest I/MyHuhxAsynTask: on post execute string: world

 

三、 從以上打印的日志,我們初步得到AsyncTask的執行流程:

  • 首先執行MyAsynTask的構造方法,然后執行onPostExecute方法。
  • 執行doInBackground方法,並且該方法的參數就是execute方法傳遞過來的參數
  • 由於doInBackground中定義了publishProgress方法,onProgressUpdate方法接接着執行,並且該方法的的參數就是publishProgress方法傳遞過來的參數
  • 最后onPostExecute方法得到執行,並且該方法的參數就是doInBackground方法return的值

 

AsyncTask的的源碼分析

 好了,上述我們基本得到了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方法執行:於是,我們跟進去:

public void run() {
    if (state != NEW ||
        !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

 

四、 上述有一句重要的代碼:發現里面重要的代碼:result = c.call(),這個call方法,就是執行了在AsyncTask構造方法中mWorker的call方法:這里我們說一下原因:

  1.  mFuture = new FutureTask<Result>(mWorker),將mWorker當作參數傳遞給FutureTask的構造函數

  2. 而FutureTask的構造函數如下:所以c.call()就是執行了mWorker中的call()方法

public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

 

五、 上述我們的執行流程來到了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;

 

AsyncTask的原理總結

  • 當我們調用execute(Params... params)方法后,execute方法會調用onPreExecute()方法。
  • 然后由ThreadPoolExecutor實例sExecutor執行一個FutureTask任務,這個過程中doInBackground(Params... params)將被調用
  • 如果被開發者覆寫的doInBackground(Params... params)方法中調用了publishProgress(Progress... values)方法,則通過Handler發送一條MESSAGE_POST_PROGRESS消息,更新進度。
  • Handler處理消息時onProgressUpdate(Progress... values)方法將被調用;如果遇到異常,則發送一條MESSAGE_POST_CANCEL的消息,取消任務
  • sHandler處理消息時onCancelled()方法將被調用;如果執行成功,則發送一條MESSAGE_POST_RESULT的消息,顯示結果,sHandler處理消息時onPostExecute(Result result)方法被調用。

 

友情鏈接 -- huhx

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM