即使 Activity 已销毁,AsyncTask 也不会停止 [英] AsyncTask won't stop even when the Activity has destroyed

查看:37
本文介绍了即使 Activity 已销毁,AsyncTask 也不会停止的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 AsyncTask 对象,它在创建 Activity 时开始执行并在后台执行操作(最多下载 100 个图像).一切正常,但有一种我无法理解的特殊行为.

I have an AsyncTask object which starts executing when the Activity is created and does stuff in the background (downloads up to 100 images). Everything works fine but there is this peculiar behavior which i'm not able to understand.

例如:当android屏幕的方向改变时,Activity被销毁并重新创建.所以我覆盖了 onRetainNonConfigurationInstance() 方法,并将所有下载的数据保存在 AsyncTask 中.我这样做的目的是不让 AsyncTask 在每次 Activity 在方向更改期间被破坏创建时运行,但正如我在日志中看到的那样,之前的 AsyncTask 仍在执行.(虽然数据保存正确)

For eg: when the android screen's orientation changes then the Activity is destroyed and created again. So I override the onRetainNonConfigurationInstance() method and save all the downloaded data executed in the AsyncTask. My purpose of doing this is to not have AsyncTask run each time Activity is destroyed-created during orientation changes, but as i can see in my logs the previous AsyncTask is still executing. (The data is saved correctly though)

我什至尝试取消活动的 onDestroy() 方法中的 AsyncTask,但日志仍然显示 AsyncTask 正在运行.

I even tried to cancel the AsyncTask in the onDestroy() method of the activity but the logs still show AsyncTask as running.

这真是奇怪的行为,如果有人能告诉我停止/取消 AsyncTask 的正确程序,我真的很感激.

This is really strange behavior and would really be thankful if someone can tell me the correct procedure to stop/cancel the AsyncTask.

推荐答案

@Romain Guy 给出的答案是正确的.不过,我想补充一些信息,并给出一个或 2 个库的指针,这些库可用于长时间运行的 AsyncTask,甚至更多用于面向网络的异步任务.

The answer given by @Romain Guy is correct. Nevertheless, I would like to add a complement of information and give a pointer to a library or 2 that can be used for long running AsyncTask and even more for network oriented asynctasks.

AsyncTasks 被设计为在后台执行任务.是的,您可以使用 cancel 方法停止它.当您从 Internet 下载东西时,我强烈建议您当线程处于 IO 阻塞状态时要小心.您应该按如下方式组织下载:

AsyncTasks have been designed for doing stuff in background. And yes, you can stop it using the cancel method. As you download stuff from the Internet, I strongly suggest you take care of your thread when it is the IO blocking state. You should organize your download as follow :

public void download() {
    //get the InputStream from HttpUrlConnection or any other
    //network related stuff
    while( inputStream.read(buffer) != -1 && !Thread.interrupted() ) {
      //copy data to your destination, a file for instance
    }
    //close the stream and other resources
}

使用 Thread.interrupted 标志将帮助您的线程正确退出阻塞 io 状态.您的线程将对 cancel 方法的调用做出更快的响应.

Using the Thread.interrupted flag will help your thread to quit properly a blocking io state. Your thread will be more responsive to an invocation of the cancel method.

但是如果您的 AsyncTask 持续时间过长,那么您将面临 2 个不同的问题:

But if your AsyncTask lasts for too long, then you will face 2 different issues :

  1. Activity 与 Activity 生命周期的关联性很差,如果您的 Activity 终止,您将无法获得 AsyncTask 的结果.确实,是的,你可以,但它会很粗糙.
  2. AsyncTask 没有很好的文档记录.异步任务的简单但直观的实现和使用可能很快导致内存泄漏.

RoboSpice,我想介绍的库,使用后台服务来执行这种的请求.它是为网络请求而设计的.它提供了额外的功能,例如自动缓存请求的结果.

RoboSpice, the library I would like to introduce, uses a background service to execute this kind of requests. It has been designed for network requests. It provides additional features such as automatic caching of requests' results.

这就是 AsyncTasks 对长时间运行的任务不利的原因.以下推理改编自 RoboSpice 动机的摘录 :解释为什么使用 RoboSpice 满足 Android 平台需求的应用.

Here is the reason why AsyncTasks are bad for long running tasks. The following reasonning is an adaptation from exerpts of RoboSpice motivations : the app that explains why using RoboSpice is filling a need on the Android platform.

AsyncTasks 不遵循 Activity 实例的生命周期.如果您在 Activity 中启动 AsyncTask 并旋转设备,则 Activity 将被销毁并创建一个新实例.但是 AsyncTask 不会死.它将继续存在,直到完成.

AsyncTasks don't follow Activity instances' life cycle. If you start an AsyncTask inside an Activity and you rotate the device, the Activity will be destroyed and a new instance will be created. But the AsyncTask will not die. It will go on living until it completes.

当它完成时,AsyncTask 不会更新新 Activity 的 UI.事实上,它更新了活动的前一个实例不再显示.这可能会导致 java.lang.IllegalArgumentException: View not attach to window manager 类型的异常,如果你例如,使用 findViewById 来检索 Activity 内的视图.

And when it completes, the AsyncTask won't update the UI of the new Activity. Indeed it updates the former instance of the activity that is not displayed anymore. This can lead to an Exception of the type java.lang.IllegalArgumentException: View not attached to window manager if you use, for instance, findViewById to retrieve a view inside the Activity.

创建 AsyncTasks 作为活动的内部类非常方便.由于 AsyncTask 需要操作视图当任务完成或正在进行时,使用 Activity 的内部类似乎很方便:内部类可以直接访问外部类的任何字段.

It is very convenient to create AsyncTasks as inner classes of your Activities. As the AsyncTask will need to manipulate the views of the Activity when the task is complete or in progress, using an inner class of the Activity seems convenient : inner classes can access directly any field of the outer class.

尽管如此,这意味着内部类将在其外部类实例上持有一个不可见的引用:Activity.

从长远来看,这会产生内存泄漏:如果 AsyncTask 持续很长时间,它会保持活动活着"而 Android 想摆脱它,因为它不能再显示.活动不能被垃圾收集,这是一个中心Android 在设备上保留资源的机制.

On the long run, this produces a memory leak : if the AsyncTask lasts for long, it keeps the activity "alive" whereas Android would like to get rid of it as it can no longer be displayed. The activity can't be garbage collected and that's a central mechanism for Android to preserve resources on the device.

您可以使用一些变通方法来创建长时间运行的异步任务,并根据活动的生命周期管理其生命周期.您可以 在您的活动的 onStop 方法中取消 AsyncTask 或者您可以让您的异步任务完成,并且不会丢失其进度和 将其重新链接到您活动的下一个实例.

You can use some workarounds to create a long running asynctask and manage its life cycle accordingly to the life cycle of the activity. You can either cancel the AsyncTask in the onStop method of your activity or you can let your async task finish, and not loose its progress and relink it to the next instance of your activity.

这是可能的,我们在 RobopSpice 中展示了它的动机,但它变得复杂并且代码不是真正通用的.此外,如果用户离开活动并回来,您仍然会失去任务的进度.加载器也会出现同样的问题,尽管它与上面提到的带有重新链接解决方法的 AsyncTask 等效.

This is possible and we show how in RobopSpice motivations, but it becomes complicated and the code is not really generic. Moreover, you will still loose the progress of your task if the user leaves the activity and comes back. This same issue appears with Loaders, although it would be a simpler equivalent to the AsyncTask with relinking workaround mentionned above.

最好的选择是使用服务来执行长时间运行的后台任务.而这正是 RoboSpice 提出的解决方案.同样,它是为网络设计的,但可以扩展到非网络相关的东西.这个库有一个大量功能.

The best option is to use a service to execute your long running background tasks. And that is exactly the solution proposed by RoboSpice. Again, it is designed for networking but could be extended to non-network related stuff. This library has a large number of features.

借助 信息图表.

将 AsyncTasks 用于长时间运行的操作确实是一个非常非常糟糕的主意.不过,它们适用于短暂的生命周期,例如在 1 或 2 秒后更新视图.

It is really a very very bad idea to use AsyncTasks for long running operations. Nevertheless, they are fine for short living ones such as updating a View after 1 or 2 seconds.

我鼓励您下载 RoboSpice Motivations应用程序,它确实深入地解释了这一点,并提供了执行某些网络相关内容的不同方法的示例和演示.

I encourage you to download the RoboSpice Motivations app, it really explains this in-depth and provides samples and demonstrations of the different ways to do some network related stuff.

如果您正在为非网络相关任务(例如没有缓存)寻找 RoboSpice 的替代方案,您还可以查看 磁带.

If you are looking for an alternative to RoboSpice for non network related tasks (for instance without caching), you could also have a look at Tape.

这篇关于即使 Activity 已销毁,AsyncTask 也不会停止的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆