<code>AsyncTask</code>,異步任務,顧名思義。
簡單說一下使用場景就是:
- 有耗時邏輯、數(shù)據(jù)操作
- 耗時在(最多)幾秒以內
- 操作后需要更新UI層面的變化
<code>AsyncTask</code>是<code>Handler</code>和<code>Thread</code>的一種封裝,但并不是一種通用的線程框架,官方推薦了引用<code>AsyncTask</code>的場景(在引言中所提及的),既然它并不通用,那么了解它到時何時應該用就變得尤為重要。
Talk it by use it
首先,要明確的是要實現(xiàn)一個<code>AsyncTask</code>需要做什么:
-
構造器:
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
你會發(fā)現(xiàn)當我們要構造一個<code>AsyncTask</code>時,需要傳入三個類型,三個類型分別代表的是<code>Params</code>, <code>Progress</code>, <code>Result</code>,下文再提用處。
-
可以<code>Override</code>的方法有:
onPreExecute() //執(zhí)行前的準備doInBackground(Params... params) //耗時操作的邏輯處理 onProgressUpdate(Progress... progress) //進度的更新 onPostExecute(Result result) //結果的處理
現(xiàn)在意義就很明了了,<code>Params</code>指代的是耗時操作的參數(shù),<code>Progress</code>用于更新進度,<code>Result</code>用于更新結果。顯然<code>onProgressUpdate</code>和<code>onPostExecute</code>中是用于更新UI的變化的。
- 用法需要注意的地方
- You can specify the type of the parameters, the progress values, and the final value of the task, using generics
- The method <code>doInBackground()</code> executes automatically on a worker thread
- <code>onPreExecute()</code>, <code>onPostExecute()</code>, and <code>onProgressUpdate()</code> are all invoked on the UI thread
- The value returned by <code>doInBackground()</code> is sent to <code>onPostExecute()</code>
You can call <code>publishProgress()</code> at anytime in <code>doInBackground()</code> to execute <code>onProgressUpdate()</code> on the UI thread- You can cancel the task at any time, from any thread
<div align = "right">摘自官網(wǎng)文檔 鏈接</div>
翻譯
- 構造器的三個參數(shù)類型為泛型,可自定義
- <code>doInBackground()</code>自動在工作線程(即非UI線程) 中執(zhí)行
- <code>onPreExecute()</code>,<code>onPostExecute()</code>,<code>onProgressUpdate()</code>全都在UI線程中執(zhí)行
- <code>doInBackground()</code>的返回值作為<code>onPostExecute()</code>的參數(shù),<code>publishProgress()</code>可以在<code>doInBackground()</code>中調用,AsyncTask內部會回調<code>onProgressUpdate()</code>,參數(shù)為<code>pulishProgress()</code>的參數(shù)
- 任何時候都可以從任意線程取消異步任務
怕翻譯的不好所以貼了一份原文
用法也就是這樣了,想了想我覺得也不用再做過多的贅述。
Now Talk about HOW
那么到底<code>AsyncTask</code>表現(xiàn)如何?
?最明顯的一點就是,它讓耗時操作更新UI變得十分簡單,開發(fā)者幾乎不需要做太多的東西就可以完成這一目的,與此相比自己實現(xiàn)<code>Thread</code>還有<code>Hanlder</code>變得略為繁瑣。
當然上述的是使用<code>AsyncTask</code>的優(yōu)點,還是要說一說缺點的。
?從起因開始說,一個<code>AsyncTask</code>,它的類的加載以及實例化需要在UI線程中實現(xiàn),那么在這個實現(xiàn)過程中,任務的內部就會持有一個對上下文對象的引用(<code>Activity</code>),用于更新UI的操作,那么這個上下文對象的持有就需要開發(fā)者考慮引用是否會丟失或者改變。例如,屏幕旋轉引起的Activity recreate、任務還未結束時退出當前Activity等等。這些情況會有可能導致程序直接crash,或者引起內存泄露,那么我們就必須兼顧到<code>AsyncTask</code>結束任務的處理。
?官方文檔中包含了這樣的一段demo:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
我們可以看到在<code>doInBackground</code>中,每一次的循環(huán)最后都加入了一個<code>isCancelled()</code>的判斷,為什么呢?雖然<code>AsyncTask</code>提供了<code>cancel(boolean)</code>方法,其中的參數(shù)可以控制任務是否應被中斷,我手動寫了個簡單的循環(huán)在<code>doInBackground()</code>中
@Override
protected void onProgressUpdate(Void... values) {
Log.e("progress", progress + "");
}
@Override
protected Void doInBackground(Void... params) {
Log.e("Task", "in");
while (isRunning && progress < 10) {
progress++;
try {
Thread.sleep(500);
publishProgress();
if (isCancelled())
Log.e("Task", "cancel");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
然后我在執(zhí)行到一半的時候就調用了<code>cancel(true)</code>,請注意是<code>true</code>!按道理講,這樣的調用過后,任務應當被中斷,然后去回調<code>onCancel()</code>,然而真相是<code>doInBackground()</code>還是會繼續(xù)執(zhí)行,但是<code>publishProgress()</code>被赤裸裸的忽略掉了。也就是說,即便你在外部調用了<code>cancel()</code>方法,你也不能確保<code>doInBackground()</code>中到底會不會發(fā)生異常,因此官方的文檔才會有上面的那段demo,在<code>doInBackground()</code>中判斷任務是否已經被取消并做出相關操作,例如結束掉網(wǎng)絡請求、終止文件的讀取等等我就不再多舉例。
?然而最重點的應該是,在<code>onCancel()</code>中究竟應該如何操作——請注意,如果調用了<code>cancel()</code>方法,不論<code>doInBackground()</code>是否已經正常的執(zhí)行完畢(<code>onCancel(Result)</code>的參數(shù),并不能保證是一個不為空的值,并且Google推薦當開發(fā)者自己實現(xiàn)<code>onCancel(Result)</code>時,不要調用<code>super.onCancel(result)</code>),<code>onCancel()</code>是一定會被執(zhí)行的,那么我們就需要在<code>onCancel()</code>當中考慮不再對不再需要駐留內存的對象保持引用,最常見的就是上下文對象了。
?另外,我們還可以做的是,將對對象的引用更換為弱引用,也算是多加了一層保險了。