Android非同步處理機制AsyncTask的理解
在Android中,由於主執行緒的諸多限制,像網路請求等一些耗時的操作我們必須在子執行緒中執行。我們往往會通過new Thread來開啟一個子執行緒,待子執行緒操作完成以後通過Handler切換到主執行緒中執行。這麼以來我們無法管理我們所建立的子執行緒,並且無限制的建立子執行緒,它們相互之間競爭,很有可能由於佔用過多資源而導致宕機或者OOM。所以在Java中為我們提供了執行緒池來管理我們所建立的執行緒。
而AsyncTask就是這麼個環境下產生的。它本質上是一個靜態的執行緒池(封裝了THREAD_POOL_EXECUTOR非同步執行緒池和SERIAL_EXECUTOR同步執行緒池和Handler),AsyncTask派生出的子類可以實現不同的非同步任務,這些任務都是提交到靜態的執行緒池中執行。執行緒池中的工作執行緒執行doInBackground(mParams)方法執行非同步的任務。當任務狀態改變後,工作執行緒向UI執行緒傳送訊息,AsyncTask內部的InternalHandler響應這些訊息,並呼叫相關的回撥函式。
下面是三種泛型:
- Params表示用於AsyncTask執行任務的引數的型別
- Progress表示在後臺執行緒處理的過程中,可以階段性地釋出結果的資料型別
- Result表示任務全部完成後所返回的資料型別
.AsyncTask的核心方法:
** onPreExecute()**
這個方法會在後臺任務開始執行之間呼叫,在主執行緒執行。用於進行一些介面上的初始化操作,比如顯示一個進度條對話方塊等。
doInBackground(Params...)
這個方法中的所有程式碼都會在子執行緒中執行,我們應該在這裡去處理所有的耗時任務。
任務一旦完成就可以通過return語句來將任務的執行結果進行返回,如果AsyncTask的第三個泛型引數指定的是Void,就可以不返回任務執行結果。注意,在這個方法中是不可以進行UI操作的,如果需要更新UI元素,比如說反饋當前任務的執行進度,可以呼叫publishProgress(Progress...)方法來完成。
onProgressUpdate(Progress...)
當在後臺任務中呼叫了publishProgress(Progress...)方法後,這個方法就很快會被呼叫,方法中攜帶的引數就是在後臺任務中傳遞過來的。在這個方法中可以對UI進行操作,在主執行緒中進行,利用引數中的數值就可以對介面元素進行相應的更新。
onPostExecute(Result)
當doInBackground(Params...)執行完畢並通過return語句進行返回時,這個方法就很快會被呼叫。返回的資料會作為引數傳遞到此方法中,可以利用返回的資料來進行一些UI操作,在主執行緒中進行,比如說提醒任務執行的結果,以及關閉掉進度條對話方塊等。
上面幾個方法的呼叫順序:
onPreExecute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExecute()
如果不需要執行更新進度則為onPreExecute() --> doInBackground() --> onPostExecute(),
除了上面四個方法,AsyncTask還提供了onCancelled()方法,它同樣在主執行緒中執行,當非同步任務取消時,onCancelled()會被呼叫,這個時候onPostExecute()則不會被呼叫,但是要注意的是,AsyncTask中的cancel()方法並不是真正去取消任務,只是設定這個任務為取消狀態,我們需要在doInBackground()判斷終止任務。就好比想要終止一個執行緒,呼叫interrupt()方法,只是進行標記為中斷,需要線上程內部進行標記判斷然後中斷執行緒。
使用AsyncTask的注意事項:
①非同步任務的例項必須在UI執行緒中建立,即AsyncTask物件必須在UI執行緒中建立。
②execute(Params... params)方法必須在UI執行緒中呼叫。
③不要手動呼叫onPreExecute(),doInBackground(Params...params),onProgressUpdate(Progress... values),onPostExecute(Result result)這幾個方法。
④不能在doInBackground(Params... params)中更改UI元件的資訊。
⑤一個任務例項只能執行一次,如果執行第二次將會丟擲異常。
AsyncTask使用不當的後果
1.)生命週期
AsyncTask不與任何元件繫結生命週期,所以在Activity/或者Fragment中建立執行AsyncTask時,最好在Activity/Fragment的onDestory()呼叫 cancel(boolean);
2.)記憶體洩漏
如果AsyncTask被宣告為Activity的非靜態的內部類,那麼AsyncTask會保留一個對建立了AsyncTask的Activity的引用。如果Activity已經被銷燬,AsyncTask的後臺執行緒還在執行,它將繼續在記憶體裡保留這個引用,導致Activity無法被回收,引起記憶體洩露。
3.) 結果丟失
螢幕旋轉或Activity在後臺被系統殺掉等情況會導致Activity的重新建立,之前執行的AsyncTask(非靜態的內部類)會持有一個之前Activity的引用,這個引用已經無效,這時呼叫onPostExecute()再去更新介面將不再生效。
AsyncTask裡面的兩個執行緒池:
1AsyncTask裡面有THREAD_POOL_EXECUTOR和SERIAL_EXECUTOR兩種方式來非同步執行任務;THREAD_POOL_EXECUTOR是非同步的,而SERIAL_EXECUTOR任務是順序執行的。
2. THREAD_POOL_EXECUTOR如果新增的任務過多,沒有及時處理的話,會導致程式崩潰,它的佇列size是128;它的排程規則是核心池大小,佇列大小,以及最大執行緒數和異常處理Handler來決定的。
3. SERIAL_EXECUTOR本質是在THREAD_POOL_EXECUTOR的基礎上新增一個mTasks的集合來保證任務的順序執行。