超过几秒的AsyncTask处理方式?

3

API参考文档说明:

AsyncTasks理想情况下应该用于短操作(最多几秒钟)。

如果doInBackground执行需要30秒,问题在于线程池可能会耗尽线程吗?如果是这个原因,如果我确保我的应用程序永远不会有多个这样长时间运行的doInBackground并发执行,那么它是否会停止成为一个问题?


2
请问您想要实现什么功能? - Joel
我有一个屏幕,上面有很多链接到远程音频样本的链接。当用户单击链接时,应播放相应的音频样本,播放持续约30秒。当样本结束或用户单击另一个链接时,播放停止,或由于其他原因而停止。我的想法是将所有MediaPlayer材料都放在AsyncTask.doInBackground中,并在需要提前停止播放时取消任务。 - arbee
你应该使用Walter建议的服务。每个音频下载都应该使用异步任务,但是没有必要生成线程来播放音频。你可以在应用程序的主线程中完成所有音频管理。 - Joel
2个回答

6
@Walter Mundt给出的答案是正确的。但是,我想补充一些信息并指向一个可以用于长时间运行AsyncTask的库。
AsyncTasks已经被设计用于在后台执行任务。如果您的AsyncTask持续时间过长,则会面临两个不同的问题:
1. Activity与活动生命周期的联系较弱,如果您的Activity停止,则无法获得AsyncTask的结果。确实,您可以这样做,但这将是粗略的方法。 2. AsyncTask文档不是很好。一个天真、直观的实现和使用asynctask可能很快导致内存泄漏。 RoboSpice是我想介绍的库,正如@Walter Mundt所提议的那样,它使用后台服务来执行此类请求。它已经被设计用于网络请求(本质上可能是长时间运行的),但它可以很容易地适应执行与网络无关的长时间运行的任务。我很高兴为它添加一个补丁。
以下是为什么AsyncTasks不适合长时间运行任务的原因。以下论述改编自 RoboSpice motivations:这个应用程序解释了为什么在Android平台上使用RoboSpice填补了一项需求。

AsyncTask和Activity生命周期

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

当它完成后,AsyncTask不会更新新Activity的UI。它实际上更新了以前没有显示的Activity的实例。如果您使用findViewById来检索Activity中的视图,则可能导致java.lang.IllegalArgumentException: View not attached to window manager类型的异常。

内存泄漏问题

创建AsyncTask作为Activity的内部类非常方便。由于AsyncTask将在任务完成或进行中时需要操作Activity的视图,因此使用Activity的内部类似乎很方便:内部类可以直接访问外部类的任何字段。
然而,这意味着内部类将持有其外部类实例(即Activity)的一个不可见引用。
长期来看,这会产生内存泄漏:如果AsyncTask持续时间较长,则保持Activity“活动”,而Android希望摆脱它,因为它无法再显示。Activity无法被垃圾回收,这是Android保留设备资源的核心机制。
您的任务进度将会丢失。
您可以使用一些解决方案来创建一个长时间运行的异步任务,并根据活动的生命周期管理其生命周期。您可以在您的活动的onStop方法中取消AsyncTask,或者让您的异步任务完成,并且不会失去其进度并重新链接到您的下一个活动实例
这是可能的,并且我们在RobopSpice动机中展示了如何实现,但它变得复杂,并且代码并不是真正通用的。此外,如果用户离开活动并返回,则仍将失去任务的进度。虽然使用Loaders会出现相同的问题,但这将是一个更简单的与上述重新链接解决方案等价的方案。
使用Android服务
最好的选择是使用服务来执行您的长时间运行的后台任务。这正是RoboSpice提出的解决方案。它再次被设计用于网络,但可以扩展到非网络相关的内容。该库具有大量特性
您甚至可以在不到30秒的时间内通过信息图表获得一个了解。

使用AsyncTasks进行长时间运行的操作是一个非常糟糕的想法。然而,它们适用于短暂的操作,例如在1或2秒后更新视图。

我鼓励您下载RoboSpice Motivations应用程序,它真正深入地解释了这一点,并提供了不同方式执行后台操作的示例和演示。


如果您正在寻找RoboSpice的替代方案,用于非网络相关任务(例如没有缓存),您也可以看看Tape


5
我相信AyncTasks通常仍与生成它们的前台活动堆栈绑定在一起,因此例如如果一个Activity生成了一个AsyncTask,用户离开应用程序,然后操作系统缺少内存,它将关闭Activity的进程(包括仍在运行的AsyncTask),并期望您在用户恢复/返回到您的应用程序时恢复状态并重新开始。

对于较长时间运行的任务,特别是只有一个或几个任务,您可能需要使用Service,因为这些可以保持持久性,即使您的应用程序UI关闭以节省内存。

免责声明:我已经有一段时间没有进行Android编码了,因此这个答案可能已经过时或基于错误的理解。如果有更多最近经验的人可以评论确认,我将删除此警告;如果高声望的人知道这是正确的,他们也可以编辑掉这段话。


1
我对使用“服务”这个建议给予赞同。 - Swayam
谢谢!对于我特定的使用情况,实际上我希望当产生任务的活动停止时,我的AsyncTask也能停止。在这种情况下,AsyncTask.cancel会被调用吗(给我一个机会在onCancel中显式释放资源)? - arbee
当用户点击您的其中一个链接时,将启动第二个活动,以便再次启动AsyncTask? 您必须调用AsyncTask.cancel才能停止它。在启动AsyncTask的第二个活动中,您可以使用AsyncTask.onCancelled来释放资源。 - Finding Nemo 2 is happening.
https://dev59.com/23E85IYBdhLWcg3w8IbK 表明当Activity被销毁时,任务可能会继续运行。我不确定这是否适用于因内存原因而被丢弃的情况;您可以使用开发者选项来测试一个简单的示例,以便不保留后台选项。 - Walter Mundt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接