Android 後臺任務(三)AsyncTask
翻譯自:http://blog.stylingandroid.com/archives/833
轉載請註明:http://blog.csdn.net/liaoqianchuan00/article/details/23949649
前面我們使用了線程來讓耗時操作脫離UI線程執行,也介紹了一些在工作線程中如何再去更新主線程的方法。但是當我們爲了頻繁的在UI線程和工作線程之間切換加了很多Runnables時,我們的代碼變得越來越難看。這篇文章,我們來看看AsyncTask怎麼來提供一個更清晰的更新UI的機制。
AsyncTask爲我們提供了開一個後臺線程的框架。我們實現一個AsyncTask的子類,重寫他的不同life-cycle點的一些方法,一些方法運行在UI線程,一些運行在後臺工作線程。
你必須重寫的一個方法是doInBackground方法,這個方法運行在後臺工作線程,這個地方你需要添加你需要做得一些耗時操作的代碼。其他3個可選的UI線程的方法:
1. onPreExecute:在doInBackground之前執行,你可以在這裏寫上一些出事後的代碼。
2. onProgressUpdate:在doInBackground執行的同時執行的一些方法,你的耗時操作在進行的時候,你可以在這裏更新UI來顯示進度。
3. onPostExecute:在doInBackground之後執行,你可以在這裏寫上耗時操作完成之後更新UI的代碼。
有個評判的標準就是,當你只需要重寫doInBackground方法的時候,你其實可以直接使用一個簡單的Thread來實現。
AsyncTask是一個泛型類, 在定義一個AsyncTask的時候,你需要定義三個類型對象,分別是傳入Task的對象類型(doInBackground傳入的類型),代表進度的對象類型,工作線程返回的類型(doInBackground返回的類型。比如我們有一個任務是需要從網絡上下載東西,同時基於一個整形數值來更新進度條,最後返回一個String值:
class MyAsyncTask extends AsyncTask<URL, Integer,String>
{
private final Activityactivity;
private ProgressBar progress;
private int count = 0;
public MyAsyncTask( Activityactivity )
{
this.activity= activity;
}
@Override
protected void onPreExecute()
{
progress= (ProgressBar)
activity.findViewById(R.id.progress );
}
@Override
protected StringdoInBackground( URL... params )
{
Stringret = null;
count= params.length;
for(int i = 0; i < count; i++ )
{
publishProgress(i );
//Do something which
//populates "ret"
}
returnret;
}
@Override
protected voidonProgressUpdate( Integer... values )
{
progress.setMax(count );
progress.setProgress(values[0] );
}
@Override
protected void onPostExecute(String result )
{
Toast.makeText(activity, result,
Toast.LENGTH_SHORT).show();
}
}
這樣做我們就不需要考慮手動的在UI線程和工作線程之間切換來更新UI了,它都爲我們自動的處理了。
要使用它,我們需要創建一個MyAsyncTask的實力,並且調用execute的時候傳入一個或者多個URL參數:
public class MyActivity extends Activity
{
@Override
public void onCreate( BundlesavedInstanceState )
{
super.onCreate(savedInstanceState );
setContentView(R.layout.main );
//Get some urls
newMyAsyncTask( this ).execute( url1, url2, url3 );
}
使用AsyncTask的一個問題就是,除非開發人員很清楚每個方法在哪個線程上執行,會很容易發生問題。比如,我發現一些人在onPreExecute方法中放入了網絡請求的代碼,這看起來沒有什麼問題,因爲網絡請求已經在AsyncTask中實現了啊,但是其實這個網絡請求仍然在UI線程中執行的。
上面例子中還有一個問題就是AsyncTask保存了一個Activity的實例,由於AsyncTask線程池的機制,他的工作線程的生命週期是不確定的,應用程序無法控制的,所以這個時候如果AsyncTask作爲Activity的內部類的話很容易出現內存泄露。而我們直接使用線程的方式要好點,只有在run函數不結束時,纔出現這種內存泄露問題。以後將會單獨用一篇文章來分析。
Activityfinish掉了,但是AsyncTask還在執行,會有問題嗎?(需要驗證)
在Honeycomb上引入了Loaders,這是另外一個機制來處理耗時操作,他更清晰的區分了那些是在UI線程上執行的,那些是在工作線程上執行的。同時他不需要在任務結束後保持對Context的引用。下一章我們將介紹Loaders。