好的设计:工作线程和活动重启

时间:2012-09-12 22:07:15

标签: android android-activity worker

众所周知,Android会将我们的应用程序组件(活动,服务)置于任何时刻被杀害的威胁之下。如果您想提供一个强大的,无泄漏的解决方案,同时保持代码清洁并解决问题分离,这会使事情变得非常复杂。

问题
活动开始一项耗时的任务(以RunnableAsyncTask或其他任何机制的形式)。应显示进度对话框。该任务将打开多个连接,但它应该有一个更新进度条的方法,它可能需要在完成的中途显示一个选择对话框。它还应该能够关闭对话框并在发生错误或完成时显示Toast。

当一个Activity被杀死然后重新创建一个新实例时,我可以想到两个选项:

  1. 尝试杀死正在运行的任务,并在创建新的替代活动实例时生成另一个任务实例。对于长时间运行的任务,这不是一个选项,因为从用户的角度来看,启动任务并看到进度条指示器从80%回到0%是不可接受的,没有明显的原因。然而,这是Shelves example by Romain Guy中遵循的方法。显然,这是官方开发者指南中推荐的方法。

  2. 让任务保持运行:因此我们可以订阅(并可能启动)onResume更新任务的活动,并在onPause取消订阅(并可能暂停)它。

  3. 遵循第二个选项意味着我们应该防止在活动被暂时销毁的情况下收集任务。例如,我们可以在一个自定义的Application类中声明它,或者甚至将它作为服务提供给应用程序的其余部分(使用Singleton?一个Android服务?)我不喜欢这个,但是,因为任务仅由活动使用,因此将其提供给其他类是没有意义的。另一方面,Android Services也必须处理生命周期。

    选项#2还涉及确保旧活动不会泄露。如果没有活动,我们不应该尝试更新GUI。整个想法应该是线程安全的。最后,在我们确定不再需要任务之后,任务不应该留在内存中。但与此同时,如果初始活动被破坏,任务继续运行,并且新的替代活动启动,则必须立即执行任务以更新GUI并显示任务当前状态(或者结果是否已完成)。

    当没有显示任何活动时,任务可能正在运行这一事实是另一个需要处理的问题,因为我们可能需要在某个时刻同步进行用户交互(选择对话框)。如果活动能够在到达onPause时立即暂停任务,则可以解决此问题,但任务可能需要一些时间才能暂停,甚至无法执行此任务。

    我之前曾经问过类似的问题,但我并没有具体询问如何解决问题,而是提出了为了实现松散耦合而将活动与任务隔离开来的设计尽可能。

    简而言之
      - 这是Android开发中反复出现的问题,之前有人可能会想出一个聪明的设计。是否有设计模式或经过良好测试的库简化了这一点?   - 如果你要实现#2,你会选择哪个类来完成任务(Runnable,Service等)以及它如何与活动进行通信?

    P.S。请不要根据"方向|发布解决方案keyboardHidden"破解XD。除此之外,任何帮助将不胜感激。


    更新:
    我的第一次尝试有点乱。该应用程序是一个蓝牙打印实用程序,涉及检测BT状态(并要求用户启用它,如果它被禁用),检索配对设备(并要求用户选择一个,如果有多个),然后发送数据最后告知用户结果。由于此任务是80%的IO代码,20%与GUI相关的操作,我不得不在任务的开始和结束时对GUI代码进行分组,然后将其从任务中移回活动。我有IntentService执行繁重的工作,然后通过BroadcastReceiver向活动报告。这解决了大多数问题,但这种设计存在一些问题。首先,所有那些常量字符串用作从输入和输出Intents中放入和检索字段的键,它们引入了语义耦合。我在活动< - >服务通信中具有首选的类型安全性。而且我还必须解决如何将复杂的自定义对象传递给Intent中的服务,现在我已经遇到了Parcelables和原始参数。最后,活动和服务都使用相同的API(蓝牙),我更倾向于在单个类中使用与BT相关的代码。我想我会抛弃这个设计,并再次使用基于线程的自定义打印队列,尽管它会更难处理被杀死的活动。

1 个答案:

答案 0 :(得分:8)

为什么不让您的活动启动服务来托管长时间运行的任务?服务是保持长期运营的最佳选择。您可以向操作系统提供提示以使其保持运行。请参阅startForeground()START_STICKY

您可以通过广播从服务中回复活动。让您的活动以编程方式注册broadcast receiver以监听这些意图。如果活动暂停,取消注册您的接收器,那么当您不在前台时,您将不会做出响应。

如果操作系统破坏了您的服务,那么它就会杀死该过程,因此您的任务无论如何都将注定失败。你能做的最好就是重启它。无论如何,如果考虑到上述情况,除非在极端条件下,否则不会发生这种情况。

编辑:总结评论中捕获的一些想法......保持长时间运行的工作程序并自动重新启动它几乎总是错误的做法。移动设备没有电源来实现这一点。作者说他有一个特殊的条件,否定了这个问题......只有当设备几乎总是连接到电源时才会出现这种情况。

android模型是为应用程序提供一组强大的意图(通知)。当需要做某事时,这些意图会唤醒您的设备/应用程序。你的应用应该处理通知,然后让设备立即重新入睡。

如果没有映射到您的活动的库存意图,您可以使用Google Cloud Messaging从服务器唤醒设备。换句话说,让长时间运行的进程在服务器上运行,并在需要时唤醒设备。

如果你能做到这一点,另一种方法是使用AlarmManager定期唤醒并检查一些状态。即使你经常这样做(例如,每5分钟,这也是一个禁忌),它总是比保持设备清醒一样好于建议。另外,使用不精确的唤醒间隔(参见上面链接的文档)。