編輯:關於android開發
整天用AsyncTask,但它的內部原理一直沒有特意去研究,今天趁著有時間,碼下它的原理。
具體用法就不再說明,相信大家已經用得很熟練了,我們今天就從它怎麼運行開始說。先新建好我們的AsyncTask:
1 class MyAsyncTask extends AsyncTask<String,Integer,Boolean>{ 2 3 @Override 4 protected Boolean doInBackground(String... voids) { 5 return true; 6 } 7 }
好了,一個最基本的代碼已經好了,接下來我們怎麼運行它呢?當然,大家都知道的。
new MyAsyncTask().execute("參數");
那平時做到這裡,我們可能已經不去往不管了,因為已經執行起來了,但是,你有沒有想過
等等一連串的問題,接下來,我們就一步一步的去看一下,它內部到底做了哪些。
首先,我們看到它new了一個AsyncTask對象,那我們就去它構建函數看看。
1 public AsyncTask() { 2 mWorker = new WorkerRunnable<Params, Result>() { 3 public Result call() throws Exception { 4 mTaskInvoked.set(true); 5 6 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 7 //noinspection unchecked 8 Result result = doInBackground(mParams); 9 Binder.flushPendingCommands(); 10 return postResult(result); 11 } 12 }; 13 14 mFuture = new FutureTask<Result>(mWorker) { 15 @Override 16 protected void done() { 17 try { 18 postResultIfNotInvoked(get()); 19 } catch (InterruptedException e) { 20 android.util.Log.w(LOG_TAG, e); 21 } catch (ExecutionException e) { 22 throw new RuntimeException("An error occurred while executing doInBackground()", 23 e.getCause()); 24 } catch (CancellationException e) { 25 postResultIfNotInvoked(null); 26 } 27 } 28 }; 29 }
看著很多,其它就是new了兩個對象mWorker和mFuture,其實它們分別是Callable和Future的實現,關於它們兩個的實現原理,我會在另外一篇文章裡講解。需要注意的是,在實例化mFuture時,把mWorker當作參數傳入。現在還沒有用到它們,我們接下來往下看execute():
1 @MainThread 2 public final AsyncTask<Params, Progress, Result> execute(Params... params) { 3 return executeOnExecutor(sDefaultExecutor, params); 4 } 5 6 @MainThread 7 public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, 8 Params... params) { 9 if (mStatus != Status.PENDING) { 10 switch (mStatus) { 11 case RUNNING: 12 throw new IllegalStateException("Cannot execute task:" 13 + " the task is already running."); 14 case FINISHED: 15 throw new IllegalStateException("Cannot execute task:" 16 + " the task has already been executed " 17 + "(a task can be executed only once)"); 18 } 19 } 20 21 mStatus = Status.RUNNING; 22 23 onPreExecute(); 24 25 mWorker.mParams = params; 26 exec.execute(mFuture); 27 28 return this; 29 }
我們看到了onPreExecute(),這也解釋了它確實比doInBackground運行的早,可以做一些准備工作。接下來,用到了我們前面提到的兩個對象,這裡是把我們傳遞過去的參數賦給了mWorker的變量,然後看26行 調用了Executor的execute(),並且把mFuture做為參數傳遞過去了,那我們看上一個方法,是把sDefaultExecutor當作參數傳遞過來的,那就看下它的定義:
1 public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); 2 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
它的最後實現類是SerialExecutor。我們接著看這個類定義了什麼。
1 private static class SerialExecutor implements Executor { 2 final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); 3 Runnable mActive; 4 5 public synchronized void execute(final Runnable r) { 6 mTasks.offer(new Runnable() { 7 public void run() { 8 try { 9 r.run(); 10 } finally { 11 scheduleNext(); 12 } 13 } 14 }); 15 if (mActive == null) { 16 scheduleNext(); 17 } 18 } 19 20 protected synchronized void scheduleNext() { 21 if ((mActive = mTasks.poll()) != null) { 22 THREAD_POOL_EXECUTOR.execute(mActive); 23 } 24 } 25 }
這裡面,SerialExecutor的方法execute()方法傳入的參數Runnable,其實就是我們前面的mFuture(FutureTask實現了Future和Runnable),execute()方法內,會把我們傳入的mFuture存入mTasks,這時mActive還等於null,所以會執行scheduleNext(),這樣就會從隊列中取出第一個,放入線程池中,開始執行,當前也就是我們剛剛放入的mFuture,也就是執行了mFuture的run方法,至於run方法內部怎麼運行的,參見我的另一篇文章:Runnable和Future的原理 。總之呢,它就運行了mWorker的call(),為了方便理解,就把mWorker的call()代碼從上面copy到這裡
1 public Result call() throws Exception { 2 mTaskInvoked.set(true); 3 4 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 5 //noinspection unchecked 6 Result result = doInBackground(mParams); 7 Binder.flushPendingCommands(); 8 return postResult(result); 9 }
我們看到,4行修改了線程的優先級,第6行就有我們熟悉的doInBackground(),結果賦給了result,現在是在另外一個線程裡面,所以,在doInBackground方法裡面是不能操作UI的。最後返回的時候是調用了postResult(),並把結果當參數傳入。
1 private Result postResult(Result result) { 2 @SuppressWarnings("unchecked") 3 Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, 4 new AsyncTaskResult<Result>(this, result)); 5 message.sendToTarget(); 6 return result; 7 }
這裡面就是我們熟悉的Handler了,查找Handler最後的實現。
1 private static class InternalHandler extends Handler { 2 public InternalHandler() { 3 super(Looper.getMainLooper()); 4 } 5 6 @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) 7 @Override 8 public void handleMessage(Message msg) { 9 AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj; 10 switch (msg.what) { 11 case MESSAGE_POST_RESULT: 12 // There is only one result 13 result.mTask.finish(result.mData[0]); 14 break; 15 case MESSAGE_POST_PROGRESS: 16 result.mTask.onProgressUpdate(result.mData); 17 break; 18 } 19 } 20 }
根據我們發送消息的CODE 為 MESSAGE_POST_RESULT,我們發現執行的是第13行,仔細看代碼會發現,result.mTask其它就是我們的MyAsyncTask對象,調用它的finish方法
1 private void finish(Result result) { 2 if (isCancelled()) { 3 onCancelled(result); 4 } else { 5 onPostExecute(result); 6 } 7 mStatus = Status.FINISHED; 8 }
這就一目了然了,最後會調用我們的onPostExecute方法,並把結果傳過去。基本的調用流程就是這樣的。
如果你仔細看了,你可能會發現,mFuture實例化的時候,重寫的done()方法並沒有介紹到,那它在整個流程中處於什麼樣的作用呢,我們接下來,單獨說一下。
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); } }
根據名字應該可以猜到,這個方法是在整個異步任務執行完後調用的,在Future中,通過get()就可以獲取到我們前面說的mWorker中call方法的返回值(具體原理可看我另一篇文章),那我們再看postResultIfNotInvoked方法,到底對返回的結果做了哪些處理。
1 private void postResultIfNotInvoked(Result result) { 2 final boolean wasTaskInvoked = mTaskInvoked.get(); 3 if (!wasTaskInvoked) { 4 postResult(result); 5 } 6 }
我們看到了postResult(),這個方法,我們在上面剛剛講過,它是在mWorker的call方法中,返回的時候調用的,你可能會問了,這個方法怎麼調用了兩次,不慌,我們看上面這個方法的名字,就了了解原因了post result if not invoked ,很明顯,這處的調用是防止call()裡面沒有調用而設置的,開關就是第2行的mTaskInvoked,它是一個AtomicBoolean,具體它干嘛的,自行google,我們發現在call方法裡面,有這樣一段代碼
mTaskInvoked.set(true);
所以,在postResultIfNotInvoked方法裡面時,如果執行了call方法,這裡就會返回true,下面就無法執行,這樣就保證了postResult方法只執行了一次。
Android多分辨率適配實踐【1】使用字體圖標(內含兩枚神器),android兩枚目錄 Android多分辨率適配實踐【0】基礎適配篇(撰寫中)Android多分辨率適
Android 高仿華為手機Tab頁滑動導航效果 首先帶大家看一下實現效果,用了兩種實現方式: 1.基於LinearLayout實現,導航欄不可響應手指滑動 2.基於Ho
Xamarin Android 應用程序內圖標上數字提示,xamarinandroid最近在用 Xamarin 做一個 Android 應用,打開應用時,如果有新消息,需
android組合控件Titlebar的定制過程 前言:我相信”天生我才必有用”這句話,每個人都有他的作用,也許他的作用相對其他人來不是很明顯