編輯:關於Android編程
相信大家對AsyncTask機制都不陌生?基本的使用的方法和注意事項都清楚,編寫一個類,繼承AsyncTask類,必須重寫doInBackground()方法,運行在子線程,耗時的操作在此執行;onPostExecute()方法在doInBackground()執行完後被回調,運行在主線程,可進行UI更新;若需要顯示更新的進度,可重寫onProgressUpdate()方法。
在一開始學習異步任務AsyncTask時,我們就在被各種教程傳輸這些理論知識,確實對於它已經了然於心,可是有考慮過底層是如何詳細實現AsyncTask機制的嗎?為何onPreExecute()方法最先被調用?doInBackground()如何封裝到子線程中的?onPostExecute方法回調又是如何實現的?這些方法之間是如何被聯系起來的?
雖然Android已為我們封裝好AsyncTask類,只有從源代碼的角度去剖析才能真正掌握。(以下講解會一步步深入,剝繭抽絲,弄清楚詳細實現方式,想要徹底剖析一定要有耐心,另配有邏輯圖供大家整理AsyncTask機制的邏輯流程!)
AsyncTaskasyncTask = new AsyncTask () { protected void onPreExecute() {}; @Override protected Object doInBackground(String... params) { return null; } protected void onPostExecute(Object result) {}; protected void onProgressUpdate(Integer[] values) {}; }; asyncTask.execute("params")
以上為AsyncTask的基本使用:new 一個AsyncTask類,重寫需要邏輯實現的方法。調用execute方法,任務就開始執行了。所以,看源代碼就從這個入口execute方法開始分析:
public final AsyncTaskexecute(Params... params) { //(1)檢測狀態 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)"); } } //(2)修改狀態 mStatus = Status.RUNNING; //(3)調用onPreExecute方法 onPreExecute(); //(4)把參數賦值給mWorker對象 mWorker.mParams = params; //(5)線程池對象執行mFuture sExecutor.execute(mFuture); return this; }
在此方法體中,開始執行異步任務,逐步驟分析:
若狀態為RUNNING:顧名思義為正在執行,拋出異常“無法執行,該任務正在執行”。所以說一旦new 了一個AsyncTask,連續調用兩次execute方法,肯定會報錯。
若狀態為FINISHED:顧名思義為已經執行過,拋出異常“無法執行,該任務已經執行過,一個任務只能執行一次”。相當於AsyncTask類中的doInBackground方法已經執行完,回調過onPostExecute方法了,再次調用execute方法,比如想再執行一次下載任務,就會報錯!
由以上我們可以得出一個結論:execute方法只能調用一次,若想多次執行,多次的new一個AsyncTask類,再去調用對應的execute方法。
在進過步驟一後(代表狀態判斷未報錯),即可修改狀態為RUNNING(即該任務正在執行)。
看到onPreExecute方法是不是覺得倍感親切,雖然我們在new一個AsyncTask類重寫其方法時,很少會去重寫該方法。現在我們知道此方法為何是在主線程中執行的了:
因為最開始new一個AsyncTask後,執行execute方法這些步驟本就是在主線程中執行的,而execute方法中又調用了onPreExecute方法,所以自然其運行在主線程,並且也是以上四個方法中最先執行的方法!
【AsyncTask機制邏輯流程圖 v1】:
mWorker.mParams = params;
以上代碼中的params就是 一開始調用AsyncTask的execute方法時傳進來的參數,此參數被保存到mWorker對象中的mParams變量中,可是這個mWorker又是什麼?首先來看它的聲明:
privaet final WorkerRunnablemWorker;
mWorker是一個WorkerRunnable類,繼續深入,查看其具體實現:
private static class InternalHandler extends Handler{ @Override public void handleMessage(MMessage msg){ ...//我也很重要,後面再講 } //☆☆☆看我☆☆☆ private static abstact class WorkerRunnableimplements Callable { Params[] mParams; } ... }
以上可得知,WorkerRunnable是一個InternalHandler 類(重要的一個類)的內部類,有一個mParams數組,來保存了一開始調用AsyncTask的execute方法時傳進來的參數!
【AsyncTask機制邏輯流程圖 v2】:
繼續分析,此類還實現了一個Callable接口,查看具體實現:
public interface Callable{ V call() throws Exception; }
這個Callable接口中只有一個call()方法,方法作用?何時調用?先留下一個疑問,我們後文分解。既然”mWorker對象的聲明“這一個線索已經挖到底了,就尋找下一個突破口,查看它的初始化:
public AsyncTask(){ //☆☆☆看我☆☆☆ mWorker = new WorkerRunnable() { public Result call() throws Exception { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); return doInBackground(mParams); } }; mFuture = new FutureTask (mWorker){ ...//我也很重要,後面再講 } }
mWorker的初始化部分在AsyncTask的構造方法中,代表最開始new 一個 AsyncTask時,mWorker對象就已經被初始化了。以上分析,其實mWorker就是一個Callable類型對象,它在初始化時new完後,覆蓋了一個call方法,而call方法的返回值正是 調用doInBackground()方法的返回值,並且在調用的時候傳遞了參數mParams。
【AsyncTask機制邏輯流程圖 v3】:
現在問題二就來了,這個doInBackground()方法是如何運行在子線程中?也就是說這個call方法何時被一個子線程調用?
莫方!先意識到這個問題,保留疑問,繼續往下走。以上就是步驟四的邏輯,我們繼續回到execute方法中,依次執行步驟五,來解決疑問。
sExecutor.execute(mFuture);
老樣子!這一行代碼看不懂,首先查找sExecutor對象的聲明:
private static final ThreadPool Executor sExecutor = new ThreadPoolsExecutor(CORE_POOL_SIZE,MAXIMUN_POOL_SIZE,KEEP_ALIVE,TimeUnit.SECONDS,sWorkQueue,sThreadFactory);
真面目揭曉!所以說Asynctask能夠執行多個任務,是因為類中有一個線程池。回到步驟五的那行代碼中,sExecutor對象執行execute方法就是往線程池中取出來一個線程,去處理括號中的內容(即mFuture),mFuture又是何物?來查看聲明:
FutureTaskmFuture;
得知mFuture是一個FutureTask類型,繼續深入,查看具體實現:
public class FutureTaskimplements RunnableFuture { public FutureTask(Callable callable) { if (callable == null) throw new NullPointerException(); sync = new Sync(callable); } ... }
發現FutureTask類型又是實現了RunnableFuture,剝繭抽絲,繼續深入查看具體實現:
public interface RunnableFutureextends Runnable,Future { void run(); }
可以恍然大悟了!原來這個RunnableFuture接口是實現了Runnable,所以裡面就有一個run方法,這跟我們所學習的理論知識“運行子線程時一般要在run方法中執行”是非常吻合的!理解之後,這一條“mFuture的聲明”線索也到底了,注意需要明確一點:執行mFuture對象就是在執行run方法!現在問題就是這個run方法在哪裡被調用了?或者說mFuture在哪裡覆蓋重寫了run方法?尋找新線索,mFuture聲明完後,來查找它初始化的地方:
public AsyncTask(){ mWorker = new WorkerRunnable() { public Result call() throws Exception { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); return doInBackground(mParams); } }; //☆☆☆看我☆☆☆ mFuture = new FutureTask (mWorker){ //此處重寫done方法 @Override protected void done() { ... //獲取doInbackground方法返回的結果 result = get(); ... //創建一個消息 message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult (AsyncTask.this, result)); //把這條消息發送給創建這個消息的Handler:target.sendMessage(this) message.sendToTarget(); } }; }
找到mFuture 的初始化地方,乍一看到步驟四中分析的mWorker對象也是在 AsyncTask的構造方法中(即AsyncTask一new出來的時候這兩個對象就都實例化了!)。並且mFuture在new 一個 FutureTask時,傳了一個參數mWorker。(這裡簡單回顧mWorker:是一個Callable對象,有一個call方法,方法中調用了doInBackground),代表將參數mWorker存儲到FutureTask類的構造方法中了,繼續深入,查看FutureTask實現:
public class FutureTaskimplements RunnableFuture { private final Sync sync; public FutureTask(Callable callable) { if (callable == null) throw new NullPointerException(); sync = new Sync(callable); } }
查看FutureTask的構造方法,也可以證實之前對mWorker對象的回顧,就是一個Callable類型。sync = new Sync(callable);又把mWorker封裝給Sync對象,Sync為何物?查看其具體實現:
Sync(Callablecallable) { //這裡的callable就是mWorker this.callable = callable; }
只看Sync的構造方法即可,可得知只是做了簡單的保存。這條線索“FutureTask類的聲明和實現”也挖到底了,
【AsyncTask機制邏輯流程圖 v4】:
回到mFuture對象初始化時(在Asynctask的構造方法中),它在new 了一個FutureTask類中,覆蓋重寫了父類的done方法,先暫緩done方法具體實現的分析。
我們之前說過“執行mFuture對象就是在執行run方法”!那麼現在查找run方法在何處被調用:
//FutureTask類中的方法 //mFuture的run方法被調用了 public void run() { sync.innerRun(); }
run方法中又調用了Sync對象的innerRun方法,深入,查看其具體實現:
void innerRun() { ... //調用mWorker的call() result = callable.call(); ... set(result); ... }
在innerRun方法中,調用了callable(也就是mWorker)的call方法,因為在sync被new出來的時候,在構造方法中就已經把mWorker賦值給了callable,所以實際上是調用mWorker的call方法!所以一大懸案—–問題二“為何doInBackground方法運行在子線程”已被偵破:
mFuture的run方法肯定執行在子線程中,run方法又調用了innerRun方法,其也在子線程中,innerRun方法又調用了mWorker的call方法!其call方法中又調用了 doInBackground方法,所以它必然執行在子線程!(這一系列的推理一定要掌握)
【AsyncTask機制邏輯流程圖 v5】:
理解之後,新的問題又來了,doInBackground方法如何將其返回值傳給onPostExecute方法中,它是如何被回調的?怎麼又在主線程中運行的?莫方!回到上面代碼繼續往下:
result = callable.call();
調用了mWorker的call方法後有一個返回值result
set(result);
又調用了set方法(方法在Sync對象的innerRun方法裡面),將其保存起來。查看set方法具體實現:
//FutureTask類中的方法 protected void set(V v) { sync.innerSet(v); }
set方法(在FutureTask類中)中又調用了Sync對象的innerSet方法,繼續深入查看:
void innerSet(V v){ ... ... if (compareAndSetState(s, RAN)) { result = v; releaseShared(0); //關鍵的done方法 done(); return; } }
innerSet方法中調用了done方法,怎麼有點眼熟?!查看其實現:
//FutureTask類中的方法 protected void done(){}
done方法(在FutureTask類中)沒有方法體,那調用此方法有何意義?但存在即合理!這個方法肯定是在new 一個FutureTask類時,將其覆蓋重寫了!這條線索“執行mFuture對象就是在執行run方法”已完畢,帶著我們的猜測回到mFuture的初始化(AsyncTask構造方法)代碼中,上面有代碼,再次粘貼主要部分:
mFuture = new FutureTask(mWorker){ //此處重寫done方法 @Override protected void done() { ... //獲取doInbackground方法返回的結果 result = get(); ... //創建一個消息 message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult (AsyncTask.this, result)); //把這條消息發送給創建這個消息的Handler:target.sendMessage(this) message.sendToTarget(); } };
首先是get方法,之前在FutureTask中,Sync變量保存了doInBackground的返回值!所以這裡取出結果,取出來後創建了一個消息,傳入了兩個參數:MESSAGE_POST_RESULT(是post的一個結果)、new了一個AsyncTaskresult類,並將doInBackground的返回值傳入到new 的AsyncTaskresult的參數中。
【AsyncTask機制邏輯流程圖 v6】:
其實創建消息時接受的兩個參數,一個是post結果,一個是Message裡面的Object,用Object保存了一個AsyncTaskresult對象,這個對象就保存有返回值。最後調用了message 的 sendToTarget方法,這個方法感覺很陌生,繼續深入,查找其實現:
public void sendToTarget(){ target.sendMessage(this); }
target調用了sendMessage方法,其實這個target就是一個hanlder,來發送消息,由於sendToTarget方法在Message內部,所以傳入參數this,就是自身。一發消息就會發到Handler中,查找Handler何處聲明:
private static final InternalHandler sHandler = new InternalHandler();
Handler在這裡new 出來了,是一個InternalHandler 類,裡面一定有一個hanlderMessage方法來處理接收的消息:
private static class InternalHandler extends Handler{ public void handleMessage(Message msg) { AsyncTaskResult result = (AsyncTaskResult) msg.obj; switch (msg.what) { case MESSAGE_POST_RESULT: //調用finish方法 result.mTask.finish(result.mData[0]); break; ... ... } } }
果然如此,聲明了一個內部類繼承於Handler,重寫了父類的handleMessage方法,所以之前mFuture對象重寫的done**方法**中發送的消息會發送至此!
之前創建消息的時候將doInBackground的返回值作為參數傳入到AsyncTaskresult類中(也就是Message的Object類),現在將result取出來,執行了:
result.mTask.finish(result.mData[0]);
這個mTask就是之前創建消息時傳入的第一個參數,也就是AsyncTask本身,mData[0]就是doInBackground的返回值。再看AsyncTask對象的finish的方法體:
private void finish(Result result) { if (isCancelled()) result = null; //調用onPostExecute方法,並傳入結果 onPostExecute(result); mStatus = Status.FINISHED; }
首先看方法體中的參數result就是doInBackground的返回值,注意注意!此處就回調了onPostExecute方法,並傳入結果!所以又一大懸案“onPostExecute如何在doInBackground執行完後實行回調並得到結果?”已偵破!
可是最後一大懸案“onPostExecute方法為何運行在主線程?”
是因為通過Handler發送消息實現的!通過InternalHandler發消息,一發就跑到主線程了呀!
結合源代碼分析之後,我們會發現主要是InternalHandler和ThreadPoolExecutor這兩個類起著重要作用!
所以,Android 原生的 AsyncTask機制 就是對線程池(也就是ThreadPoolExecutor)的一個封裝,使用其自定義的 Executor來調度線程的執行方式(並發還是串行),最後使用 Handler(也就是InternalHandler) 來完成子線程和主線程的數據共享。
其實寫此博客之前,我搜到網上已經有很多大牛分析過了,並且更加精髓。我在考慮自己再寫會不會有點炒冷飯的感覺,可是別人的東西畢竟是別人的,多總結歸納總是有好處的,共勉~
最後!我討厭csdn博客保存功能,因為之前沒保存,重寫了兩次,心碎!但是當我重寫第二遍的時候,有些細節部分更明白了,所謂溫故而知新啊,也許第一遍看源代碼分析不太懂,但是多看幾遍就理解了!而且此篇博客關鍵在於一步步分析,十分詳細!有何問題歡迎討論。
希望對你們有幫助:)
1. 前言前幾篇學習了jni開發的基本流程、動態注冊native函數以及相關編譯文件的編寫,咱們也算是知道了jni開發,但是還不夠,今天咱們來學習下,java和jni的數
菜單功能是點擊按鈕彈出分類菜單 看看效果圖 先說一下實現原理,彈出菜單采用的是Fragment實現,很方便且高效,上面的三個按鈕是RadioButton。 新建一個項目
Android 圖片選擇可以達到的效果:1.第一個圖片的位置放照相機,點擊打開照相機2.其余的是顯示全部存儲的圖片,點擊一次是查看大圖,長按則是每張圖片出現一
前言 為了保證每周一篇的進度,又由於Vitamio新版本沒有發布, 決定推遲本地播放的一些功能(截圖、視頻時間、尺寸等),跳過直接寫在線播放部分的章節。從V