7

众所周知,Android 将我们的应用程序组件(活动、服务)置于随时被杀死的威胁之下。如果您想提供一个健壮、无泄漏的解决方案,同时保持代码干净并解决关注点分离,这会使事情变得非常复杂。

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

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

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

  2. 保持任务运行:所以我们可以有一个活动订阅(并可能开始)任务以进行更新onResume,并在 取消订阅(并可能暂停)它onPause

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

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

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

我知道以前也有人问过类似的问题,但我并不是专门询问如何解决问题,而是建议推荐什么样的设计以实现松散耦合并尽可能地将活动与任务隔离开来。

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

PS 请不要发布基于“orientation | keyboardHidden” hack XD 的解决方案。除此之外,任何帮助将不胜感激。


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

4

1 回答 1

8

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

您可以通过服务广播与活动进行通信。让您的活动以编程方式注册广播接收器以侦听这些意图。如果活动暂停,请取消注册您的接收器,这样当您不在前台时您将不会响应。

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

编辑:总结评论中的一些想法......保持长时间运行的工作进程并自动重新启动它几乎总是错误的事情。移动设备没有使这可行的电源。作者表示,他有一个特殊情况可以消除这种担忧......只有当设备几乎总是连接到电源时才会如此。

android 模型旨在提供一组强大的意图(通知),您的应用程序可以监听这些意图(通知)。当需要做某事时,这些意图会唤醒您的设备/应用程序。您的应用程序应处理通知,然后让设备立即重新进入睡眠状态。

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

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

于 2012-09-12T22:16:16.347 回答