當(dāng)AsyncTask被引入到Android中時,它被貼上“無憂線程”的標(biāo)簽。其目的是讓與UI線程交互的子線程變得更容易。AsyncTask其本質(zhì)是一個由5個核心線程組成的,最大隊列數(shù)為128的線程池。我們在使用的過程中,通常會重寫doInBackground(Params…) 方法,比較耗時的操作都可以放在這里。這個方法在子線程,是不能直接操作UI的。此方法在后臺線程執(zhí)行,完成任務(wù)的主要工作,通常需要較長的時間。在執(zhí)行過程中可以調(diào)用onPostExecute(Result)方法,相當(dāng)于Handler 處理UI的方式,在這里面可以使用在doInBackground 得到的結(jié)果處理操作UI。 此方法在主線程執(zhí)行,任務(wù)執(zhí)行的結(jié)果作為此方法的參數(shù)返回。
AsyncTask的用法很簡單,那么我們看下面這段代碼,這樣寫正確嗎?
private TextView mTextview;
new AsyncTask<...> {
@Override
protected void onPostExecute(Objecto) {
mTextview.setText("text");
}
}.execute();
乍一看好像沒什么問題,但這段代碼會導(dǎo)致內(nèi)存泄露,線程有可能會超出當(dāng)前Activity的生命周期之后仍然在run,因為這個時候線程已經(jīng)不受控制了。Activity生命周期已經(jīng)結(jié)束,需要被系統(tǒng)回收掉,但是AsyncTask還在持有TextView的引用,這樣就導(dǎo)致了內(nèi)存泄露。
那我們把上面的代碼改一改
private TextView mTextview;
new AsyncTask<...> {
@Override
protected void onPostExecute(Objecto) {
//mTextview.setText("text");
}
}.execute();
算了,懶得改 ,我直接注釋掉,不做UI操作了,這樣總不會有問題了吧。真的嗎?
仔細看,這里是個內(nèi)部類,由于Java內(nèi)部類的特點,AsyncTask內(nèi)部類會持有外部類的隱式引用。即使從代碼上看我在AsyncTask里沒有持有外部的任何引用,但是寫在Activity里,對context仍然會有個強引用,這樣如果線程超過Activity生命周期,Activity還是無法回收造成內(nèi)存泄露。
那問題怎么解決呢,有兩種辦法:第一,在Activity生命周期結(jié)束前,去cancel AsyncTask,因為Activity都要銷毀了,這個時候再跑線程,繪UI顯然已經(jīng)沒什么意義了。第二,如果一定要寫成內(nèi)部類的形式,對context采用WeakRefrence,在使用之前判斷是否為空。
如有刊誤,歡迎指正。