我想添加一个信息补充,并给出一个指向库或2的指针,可以用于长时间运行的AsyncTask,甚至可以用于面向网络的asynctasks。
AsyncTasks专为在后台执行操作而设计。是的,您可以使用该cancel方法停止它。当您从Internet下载内容时,我强烈建议您在IO阻塞状态下处理您的线程。您应按照以下方式组织下载: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方法。
AsyncTask设计缺陷
但如果您的AsyncTask持续时间过长,那么您将面临两个不同的问题:活动与活动生命周期密切相关,如果活动中断,您将无法获得AsyncTask的结果。的确,是的,你可以,但这将是粗糙的方式。
AsyncTask没有很好的文档记录。一个天真但直观的实现和使用asynctask可能很快导致内存泄漏。
我想介绍的库RoboSpice使用后台服务来执行这种请求。它专为网络请求而设计。它提供了其他功能,例如自动缓存请求的结果。
这就是为什么AsyncTasks对于长时间运行的任务不利的原因。以下推理是对RoboSpice动机的改编:适用于解释为什么使用RoboSpice满足Android平台需求的应用程序。
AsyncTask和Activity生命周期
AsyncTasks不遵循Activity实例的生命周期。如果在Activity中启动AsyncTask并旋转设备,则将销毁Activity并创建新实例。但AsyncTask不会死。它会继续生存直到它完成。
完成后,AsyncTask将不会更新新Activity的UI。实际上,它更新了之前不再显示的活动实例。这可能导致java.lang.IllegalArgumentException类型的异常:如果您使用findViewById来检索Activity内的视图,则View不会附加到窗口管理器。
内存泄漏问题
将AsyncTasks创建为活动的内部类非常方便。由于AsyncTask需要在任务完成或正在进行时操纵Activity的视图,使用Activity的内部类似乎很方便:内部类可以直接访问外部类的任何字段。
然而,这意味着内部类将在其外部类实例上保存一个不可见的引用:Activity。
从长远来看,这会产生内存泄漏:如果AsyncTask持续很长时间,它会使活动保持“活着”,而Android则希望摆脱它,因为它无法再显示。该活动不能被垃圾收集,这是Android在设备上保留资源的核心机制。
您的任务进度将丢失
您可以使用一些变通方法来创建长时间运行的异步任务,并根据活动的生命周期管理其生命周期。您可以在活动的onStop方法中取消AsyncTask,也可以让异步任务完成,而不是松开其进度并将其重新链接到下一个活动实例。
这是可能的,我们展示了RobopSpice的动机,但它变得复杂,而且代码并不是真正的通用。此外,如果用户离开活动并返回,您仍将失去任务的进度。Loaders也出现了同样的问题,尽管它与上面提到的重新链接解决方法的AsyncTask相比更简单。
使用Android服务
最好的选择是使用服务来执行长时间运行的后台任务。这正是RoboSpice提出的解决方案。同样,它专为网络设计,但可以扩展到非网络相关的东西。该库具有大量功能。
你可以通过信息图表在不到30秒的时间内了解它。
对于长时间运行的操作使用AsyncTasks确实是一个非常糟糕的主意。然而,它们适用于短期生活,例如在1或2秒后更新视图。
我鼓励您下载RoboSpice Motivations应用程序,它真正深入地解释了这一点,并提供了有关网络相关内容的不同方法的示例和演示。
如果您正在为非网络相关任务(例如没有缓存)寻找RoboSpice的替代方案,您还可以查看Tape。