Android开发——异步任务中Activity销毁时的问题
0.? 前言
在Android開發(fā)中經(jīng)常會(huì)發(fā)生Activity的銷毀重建,比如用戶長時(shí)間接聽一個(gè)電話后回到APP。在Android開發(fā)——Fragment知識(shí)整理(二)中我們提到了使用Fragment大量保存Activity銷毀重建數(shù)據(jù)的方法,但是有一個(gè)問題是,在異步任務(wù)時(shí)旋轉(zhuǎn)屏幕,如何處理異步任務(wù)呢?如果單純的在Activity銷毀之前關(guān)閉上一個(gè)異步任務(wù),onPostExecute()中的關(guān)閉對(duì)話框就不會(huì)走了,會(huì)出現(xiàn)對(duì)話框無法關(guān)閉的現(xiàn)象;如果不關(guān)閉,可能會(huì)更新已經(jīng)不存在的控件,造成錯(cuò)誤,不僅如此最主要的是Activity的銷毀會(huì)造成對(duì)話框dismiss空指針異常,因?yàn)榕c當(dāng)前對(duì)話框綁定的FragmentManager已經(jīng)是null。
因此我們的目標(biāo)是在異步加載數(shù)據(jù)時(shí)旋轉(zhuǎn)屏幕,不會(huì)對(duì)加載任務(wù)進(jìn)行中斷重啟,并且對(duì)話框正常顯示。
1.? 繼承Fragment并在其中聲明引用
public class KeepDataFragment extends Fragment {// 保存一個(gè)異步的任務(wù)private MyAsyncTask data;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setRetainInstance(true);}public void setData(MyAsyncTask data) {this.data = data;}public MyAsyncTask getData() {return data;} }這里我們創(chuàng)建KeepDataFragment并繼承Fragment,并在其中聲明需要保存的數(shù)據(jù)對(duì)象,這里是保存了一個(gè)異步的任務(wù),然后提供getter和setter。最后一定要在onCreate調(diào)用setRetainInstance(true)。
2.? 異步任務(wù)和進(jìn)度條
public class MyAsyncTask extends AsyncTask<Void, Void, Void> {private MainActivity activity;private boolean isCompleted;private LoadingDialog mLoadingDialog;private List<String> items;public MyAsyncTask(MainActivity activity) {this.activity = activity;}@Overrideprotected void onPreExecute() {mLoadingDialog = new LoadingDialog();mLoadingDialog.show(activity.getFragmentManager(), "LOADING");}@Overrideprotected Void doInBackground(Void... params) {items = loadingData();return null;}private List<String> loadingData() {try {Thread.sleep(5000);} catch (InterruptedException e) {}return new ArrayList<String>(Arrays.asList("東南大學(xué)", "信息科學(xué)與工程學(xué)院", "信息安全學(xué)科"));}@Overrideprotected void onPostExecute(Void unused) {isCompleted = true;notifyActivityTaskCompleted();if (mLoadingDialog != null)mLoadingDialog.dismiss();}public List<String> getItems() {return items;}public void setActivity(MainActivity activity) {// 如果上一個(gè)Activity銷毀,將與上一個(gè)Activity綁定的DialogFragment銷毀if (activity == null) {mLoadingDialog.dismiss();}// 設(shè)置為當(dāng)前的Activitythis.activity = activity;// 開啟一個(gè)與當(dāng)前Activity綁定的等待框if (activity != null && !isCompleted) {mLoadingDialog = new LoadingDialog();mLoadingDialog.show(activity.getFragmentManager(), "LOADING");}// 如果完成,通知Activityif (isCompleted) {notifyActivityTaskCompleted();}}private void notifyActivityTaskCompleted() {if (null != activity) {activity.onTaskCompleted();}} }這里使用AsyncTask進(jìn)行異步任務(wù),不熟悉AsyncTask的可以參考Android開發(fā)——AsyncTask的使用以及源碼解析,任務(wù)開始時(shí)顯示了一個(gè)FragmentDialog對(duì)話框,如果不熟悉可以參考Android開發(fā)——官方推薦使用DialogFragment替換AlertDialog,這里就不贅述了。任務(wù)下載中時(shí),我們模擬了5秒耗時(shí)任務(wù)并返回了一個(gè)字符串List。下載任務(wù)結(jié)束時(shí)讓進(jìn)度框消失,并為Activity提供回調(diào),因?yàn)檫@里持有了Activity的引用。這里我們也提供了setActivity方法,在Activity被銷毀時(shí)在onSaveInstanceState()中設(shè)置setActivity(null)取消之前的對(duì)話框,同時(shí)也防止了內(nèi)存泄漏;當(dāng)Activity重建時(shí)在onCreate()中設(shè)置setActivity(this)傳入新的Activity,從而再次顯示一個(gè)加載框,這里需要注意的是Activity的銷毀重建并不影響加載的數(shù)據(jù),所有后臺(tái)的數(shù)據(jù)一直繼續(xù)在加載。
3.? MainActivity中的實(shí)現(xiàn)
public class MainActivity extends ListActivity {private ListAdapter mAdapter;private List<String> mDatas;private KeepDataFragment dataFragment;private MyAsyncTask mMyTask;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);FragmentManager fm = getFragmentManager();dataFragment = (KeepDataFragment) fm.findFragmentByTag("data");if (dataFragment == null) {dataFragment = new KeepDataFragment();fm.beginTransaction().add(dataFragment, "data").commit();}mMyTask = dataFragment.getData();if (mMyTask != null) {//使AsyncTask持有Activity的引用mMyTask.setActivity(this);} else {mMyTask = new MyAsyncTask(this);dataFragment.setData(mMyTask);mMyTask.execute();}}@Overrideprotected void onRestoreInstanceState(Bundle state) {super.onRestoreInstanceState(state);}@Overrideprotected void onSaveInstanceState(Bundle outState) {mMyTask.setActivity(null);super.onSaveInstanceState(outState);}@Overrideprotected void onDestroy() {super.onDestroy();}public void onTaskCompleted() {mDatas = mMyTask.getItems();mAdapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, mDatas);setListAdapter(mAdapter);} }在onCreate中,如果是第一次進(jìn)入,則將Activity引用傳給AsyncTask、開啟任務(wù)mMyTask并把它交給KeepDataFragment來維護(hù),正常情況下AsyncTask正常進(jìn)行,完成后回調(diào)Activity中的onTaskCompleted()。注意要考慮不正常的情況,即加載過程中屏幕的旋轉(zhuǎn),Activity銷毀時(shí)設(shè)置setActivity(null)取消之前的對(duì)話框,并在Activity重建時(shí)KeepDataFragment 實(shí)例因?yàn)槲幢讳N毀直接通過dataFragment.getData() 取出加載任務(wù)mTask并設(shè)置setActivity(this)從而再次顯示一個(gè)新的加載框,直到任務(wù)完成正常進(jìn)行Activity的回調(diào)顯示數(shù)據(jù)方法。
看一下如下效果,加載數(shù)據(jù)的5秒鐘內(nèi)無論如何旋轉(zhuǎn)屏幕都不會(huì)出現(xiàn)問題,這樣就完成了進(jìn)行異步任務(wù)時(shí)Activity的銷毀重建不會(huì)發(fā)生中斷并開啟新的下載任務(wù),而且對(duì)話框也會(huì)正常顯示。
源碼下載地址點(diǎn)這里。
轉(zhuǎn)載于:https://www.cnblogs.com/qitian1/p/6461454.html
總結(jié)
以上是生活随笔為你收集整理的Android开发——异步任务中Activity销毁时的问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安装开源项目 MultiType (基于
- 下一篇: Android开始之 Spinner控件