0.  前言

在Android开发中经常会发生Activity的销毁重建,比如用户长时间接听一个电话后回到APP。在Android开发——Fragment知识整理(二)中我们提到了使用Fragment大量保存Activity销毁重建数据的方法,但是有一个问题是,在异步任务时旋转屏幕,如何处理异步任务呢?如果单纯的在Activity销毁之前关闭上一个异步任务,onPostExecute()中的关闭对话框就不会走了,会出现对话框无法关闭的现象;如果不关闭,可能会更新已经不存在的控件,造成错误,不仅如此最主要的是Activity的销毁会造成对话框dismiss空指针异常,因为与当前对话框绑定的FragmentManager已经是null。

因此我们的目标是在异步加载数据时旋转屏幕,不会对加载任务进行中断重启,并且对话框正常显示。

1.  继承Fragment并在其中声明引用

public class KeepDataFragment extends Fragment {
// 保存一个异步的任务
private MyAsyncTask data;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public void setData(MyAsyncTask data) {
this.data = data;
}
public MyAsyncTask getData() {
return data;
}
}

这里我们创建KeepDataFragment并继承Fragment,并在其中声明需要保存的数据对象,这里是保存了一个异步的任务,然后提供getter和setter。最后一定要在onCreate调用setRetainInstance(true)。


2.  异步任务和进度条

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;
} @Override
protected void onPreExecute() {
mLoadingDialog = new LoadingDialog();
mLoadingDialog.show(activity.getFragmentManager(), "LOADING");
} @Override
protected 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("东南大学", "信息科学与工程学院", "信息安全学科"));
} @Override
protected void onPostExecute(Void unused) {
isCompleted = true;
notifyActivityTaskCompleted();
if (mLoadingDialog != null)
mLoadingDialog.dismiss();
} public List<String> getItems() {
return items;
} public void setActivity(MainActivity activity) {
// 如果上一个Activity销毁,将与上一个Activity绑定的DialogFragment销毁
if (activity == null) {
mLoadingDialog.dismiss();
}
// 设置为当前的Activity
this.activity = activity;
// 开启一个与当前Activity绑定的等待框
if (activity != null && !isCompleted) {
mLoadingDialog = new LoadingDialog();
mLoadingDialog.show(activity.getFragmentManager(), "LOADING");
}
// 如果完成,通知Activity
if (isCompleted) {
notifyActivityTaskCompleted();
}
} private void notifyActivityTaskCompleted() {
if (null != activity) {
activity.onTaskCompleted();
}
}
}

这里使用AsyncTask进行异步任务,不熟悉AsyncTask的可以参考Android开发——AsyncTask的使用以及源码解析,任务开始时显示了一个FragmentDialog对话框,如果不熟悉可以参考Android开发——官方推荐使用DialogFragment替换AlertDialog,这里就不赘述了。任务下载中时,我们模拟了5秒耗时任务并返回了一个字符串List。下载任务结束时让进度框消失,并为Activity提供回调,因为这里持有了Activity的引用。这里我们也提供了setActivity方法,在Activity被销毁时在onSaveInstanceState()中设置setActivity(null)取消之前的对话框,同时也防止了内存泄漏;当Activity重建时在onCreate()中设置setActivity(this)传入新的Activity,从而再次显示一个加载框,这里需要注意的是Activity的销毁重建并不影响加载的数据,所有后台的数据一直继续在加载。

3.  MainActivity中的实现

public class MainActivity extends ListActivity {
private ListAdapter mAdapter;
private List<String> mDatas;
private KeepDataFragment dataFragment;
private MyAsyncTask mMyTask; @Override
public 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();
}
} @Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
} @Override
protected void onSaveInstanceState(Bundle outState) {
mMyTask.setActivity(null);
super.onSaveInstanceState(outState);
} @Override
protected 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中,如果是第一次进入,则将Activity引用传给AsyncTask、开启任务mMyTask并把它交给KeepDataFragment来维护,正常情况下AsyncTask正常进行,完成后回调Activity中的onTaskCompleted()。注意要考虑不正常的情况,即加载过程中屏幕的旋转,Activity销毁时设置setActivity(null)取消之前的对话框,并在Activity重建时KeepDataFragment 实例因为未被销毁直接通过dataFragment.getData() 取出加载任务mTask并设置setActivity(this)从而再次显示一个新的加载框,直到任务完成正常进行Activity的回调显示数据方法。

看一下如下效果,加载数据的5秒钟内无论如何旋转屏幕都不会出现问题,这样就完成了进行异步任务时Activity的销毁重建不会发生中断并开启新的下载任务,而且对话框也会正常显示。

源码下载地址点这里

Android开发——异步任务中Activity销毁时的问题的更多相关文章

  1. Android开发之漫漫长途 Ⅱ——Activity的显示之Window和View(2)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  2. Android开发之漫漫长途 Ⅱ——Activity的显示之Window和View(1)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  3. Android开发之漫漫长途 Ⅲ——Activity的显示之Window和View(2)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  4. Android中Activity运行时屏幕方向与显示方式详解

    现在我们的手机一般都内置有方向感应器,手机屏幕会根据所处位置自动进行横竖屏切换(前提是未锁定屏幕方向).但有时我们的应用程序仅限在横屏或者竖屏状态下才可以运行,此时我们需要锁定该程序Activity运 ...

  5. Android开发艺术1之Activity的生命周期

    作为<Android开发艺术探索>这本书的第一篇博客,我就多说几句.本系列博客旨在对书中相关内容进行解读,简化,提供一个入门到提高的流程.不敢说书评,也不能说教程,只希望对有些人有帮助就好 ...

  6. Android开发学习之路--Activity之初体验

    环境也搭建好了,android系统也基本了解了,那么接下来就可以开始学习android开发了,相信这么学下去肯定可以把android开发学习好的,再加上时而再温故下linux下的知识,看看androi ...

  7. Android开发之漫漫长途 Ⅳ——Activity的显示之ViewRootImpl初探

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  8. Android开发学习之路--Activity之生命周期

    其实这篇文章应该要在介绍Activity的时候写的,不过那个时候还不怎么熟悉Activity,还是在这里详细介绍下好了.还是参考下官方文档的图吧: 从上面的流程,我们可以看出首先就是打开APP,开始执 ...

  9. Android开发,Eclipse创建aidl接口时,出错

    Android开发中,当我们需要调用远程Service时,我们一般通过远程接口(RMI)来实现的,而Android的RMI需要AIDL(Android Interface Definition Lan ...

随机推荐

  1. 大数据【四】MapReduce(单词计数;二次排序;计数器;join;分布式缓存)

       前言: 根据前面的几篇博客学习,现在可以进行MapReduce学习了.本篇博客首先阐述了MapReduce的概念及使用原理,其次直接从五个实验中实践学习(单词计数,二次排序,计数器,join,分 ...

  2. 4种更快更简单实现Python数据可视化的方法

    数据可视化是数据分析或机器学习项目中十分重要的一环.通常,你需要在项目初期进行探索性的数据分析(EDA),从而对数据有一定的了解,而且创建可视化确实可以使分析的任务更清晰.更容易理解,特别是对于大规模 ...

  3. PowerShell发送邮件(587)

    #定义邮件服务器 $smtpServer = "mail.xx.com" $smtpUser = "sender" $smtpPassword = " ...

  4. 调用webservice 的时候没法输入参数

    在web.config的<system.web></system.web>中间加入如下配置节内容<webServices> <protocols> &l ...

  5. 【C语言】 8421BCD码与二进制的转换

    #define BCD2TODEC(bcd) (bcd) = ((bcd) & 0x0f)+ ((bcd)>>4)*10 #define BIN2BCD(bcd) (bcd) = ...

  6. elasticsearch安装步骤

    今天我们来安装一下elasticsearch,我们采用RPM包安装的方式来,版本为6.5.4.系统为centos7.5版本. 1.首先设置系统环境 1)编辑/etc/sysctl.conf文件添加下面 ...

  7. FZU Monthly-201901 tutorial

    FZU Monthly-201901 tutorial 题目(难度递增) easy easy-medium medium medium-hard hard 思维难度 AHG F B CE D 编码难度 ...

  8. Alpha冲刺报告(4/12)(麻瓜制造者)

    今日完成的情况 江郑: 今天对数据库的需求部分进行了完善 邓弘立: 完成了首页界面UI 刘双玉: 基本完成商品信息发布接口 汪志彬: 尝试UI的设计 符天愉: 将登录接口部署到服务器上,结果Linux ...

  9. 026.7 网络编程 URL对象

    通过一个程序理解Java的url对象. String str_url = "http://127.0.0.1:8080?name=xxx"; URL url = new URL(s ...

  10. swift和oc的protocol的成员变量

    都需要协议实现者提供具体变量: 否则认为不符合协议. @protocol edddd <NSObject> @property(nonatomic, strong) NSObject *e ...