4

我正在开发一个应用程序,旨在将 android 手机转换为无需用户操作即可运行的远程设备。当时,应用程序由一个 Activity 制作,该 Activity 将 AlarmManager 设置为每 X 分钟执行一次服务(项目内的类)。

所有这些工作正常,但偶尔在连续运行 5-6 天后应用程序崩溃(目前我不知道为什么,因为我现在无法接听电话)。这不是连接问题(我知道),手机仍在运行(插入交流电)。我唯一能想到的是应用程序已关闭。

我不认为这是由于错误,因为 prew 调试不会给我任何错误。

所以我必须假设 android 已经杀死了这个活动(系统需要更多内存?)并且正如图片所解释的那样,没有办法备份它。

流程图

但我有一个疑问:在我的应用程序中,活动并不重要,因为所有工作都由服务完成。服务本身由警报管理器调用,在两次调用之间,服务由 StopSelf() 终止。

在我的情况下,系统可能会终止我的警报管理器服务计划?

我该怎么做才能让警报管理器永远启动服务?

注意:目前还存在 WAKE LOCK 但这仅考虑服务的执行!我希望您了解警报管理器每 x 分钟调用一次服务然后终止...我想执行此操作无限期)

【因为太长,我没有贴出源代码】

4

3 回答 3

4

洛克,

在我自己解决了类似的问题之后,我可能会给你一些建议。我假设您将 Android 设备用作一种远程“嵌入式控制器”,它以最少的用户交互来执行其功能。我相信你 95% 都在那里,只需要进行一些轻微的架构更改。由于您没有提供代码,我将仅用抽象的术语进行解释,而不是给出代码示例。

CommonsWare 是正确的,你需要使用 AlarmManager,但我怀疑你已经知道了。首先是一些背景评论,只是为了确保一切都可以理解。AlarmManager 创建的警报存在于系统级别,即它们可以存在于创建它们的活动和应用程序的生命周期之外。如果您设置了警报但不希望它在您的应用更改状态(例如在它已被销毁之后)时响起,那么您可以使用 alarmManager.cancel(pendingIntent) 取消它 - 只需创建意图和警报管理器相同的参数和Android会匹配报警)。同样,BroadcastReceiver 在系统级别注册(至少如果它们在 manifest.xml 中声明)并且可以存在于创建它们的活动和应用程序的生命周期之外。同样,如果您想确保 BroadcastReceiver 不会响应在您的应用更改状态后发生的事件(例如在它被销毁之后),您需要在它的代码中显式取消注册。如果它是以编程方式注册的,则使用 context.unregisterReceiver(broadcastReceiver); 如果它是在 Manifest 中静态注册的,那就不那么容易了——你必须使用 PackageManager 和 ComponentName 来检索接收器(参见:Android - 如何取消注册清单中创建的接收器?) - 请记住,如果您再次需要接收器,则需要重新启用它。

你说你已经设置了闹钟。确保为警报类型指定 ELAPSED_REALTIME_WAKEUP 或 RTC_WAKUP 以确保即使手机处于“睡眠”模式也能运行。您还说您已经创建了关联的 BroadcastReceiver 来处理警报事件。BroadcastReceiver 应该做最少的工作,因此您应该在单独的线程中或通过启动服务来处理任何处理。您选择启动服务并在完成后使用 stopSelf() 终止它,这样它就不会耗尽系统资源。到目前为止,一切都很好。

这在应用程序运行时很好,但是由于您需要可以无限期可靠运行的东西,您需要确保管理它已暂停、设备“睡眠”、应用程序崩溃的“异常”情况/终止,或设备已重新启动(以及您可能想到的任何其他异常情况)。以下是我确定的您需要解决的问题:

第一:WakeLock 仅在 BroadcastReceiver 的 onReceive() 方法的持续时间内得到保证。终止后,即使您的服务尚未启动甚至完成,设备也可能重新进入“睡眠”状态,因此您需要创建一个 WakeLock,将其传递给服务并在停止服务之前释放它。(注意:对于您的应用程序,您需要 PARTIAL_WAKE_LOCK)。使用 WakeLocks 时要非常小心——确保您只持有 WakeLock 所需的最短时间并确保您释放它,因为它的使用会导致过度消耗电池电量)。 有关使用 WakeLocks 的示例,请参阅http://www.netmite.com/android/mydroid/development/pdk/docs/power_management.html 。

第二:如果您在代码中重置警报(而不是定义自动重复警报),请在 BroadcastReceiver 的 OnReceive() 方法中执行此操作,或者作为您启动的服务中的第一件事 - 这将确保警报重复,无论应用程序或设备的状态如何。

第三:确保您使用的任何上下文都是非空值。您可以使用 getApplicationContext() 动态获取服务中的上下文。否则,这可以通过明确地将上下文从应用程序传递到警报来实现,并确保它一直通过广播接收器以及相关的线程和服务传递。如果您在应用程序中静态存储了 Context 以便您可以在任何地方检索它,那么如果应用程序终止,这将返回一个空值。如果您使用 Context(例如检索资源、访问数据库等),并且它为 null,则会导致空指针异常,并且 Service 或 BroadcastReceiver 将崩溃。我相信这是您的应用程序终止时广播接收器无法正常工作的最可能原因。

第四:您可能希望在您的 Service 或 BroadcastReceiver 完全限定 (.R.drawable.icon) 或从传递的 Context 中生成对 ResourceIDs (例如 R.drawable.icon) 的引用。我还没有发现这是必要的,但我怀疑这可能是谨慎的。

第五:实现一个单独的 BroadcastReceiver 来处理设备重启场景(ON_BOOT_COMPLETE 事件)。如果合适,您可以让此接收器重新启动应用程序,或者它可以启动服务以检查您的应用程序是否应该处于活动状态,设置任何必需的参数并设置相关警报,然后使用 stopSelf() 终止它,或者只是再次设置警报并让接收器处理这一切。请记住确保服务在其持续时间内具有 WakeLock,并在完成时释放 WakeLock。如果您不只是重新启动应用程序或服务(声明为应用程序的一部分),那么如果需要,您还应该将正确的上下文作为类属性静态存储在 BroadcastReceiver 中,以便可以访问资源。

您可能希望考虑的其他几件事:由于您的设置是远程的,我会认真考虑将任何持久性数据存储在 SQLite 数据库表中。这将确保数据在应用程序终止和设备重启之间是可恢复的,而无需重新生成。如果您的应用程序与服务器服务通信,请考虑将推送通知用于服务器发起的通信,而不是让应用程序定期轮询。推送通知还可以用于“唤醒和启动服务和应用程序”,因此可以用作远程机制的一部分来查询设备和应用程序的状态。这种方法也更加省电和及时。在代码中的关键点将信息发布到 LogCat 以进行调试。如果应用程序终止,

其他人可能有更好的方法来解决这些问题或其他一些指示(我当然会很高兴看到其他输入),但我希望这些想法对您有所帮助并祝您好运!

于 2012-01-11T00:22:46.203 回答
0

与服务一起使用AlarmManager的目的是启动一个将短暂运行的服务,然后该服务消失(例如,一个IntentService)。如果你想尝试一个永久的服务,你不需要AlarmManager,你的服务在一段时间后被安卓关闭。

如果您将应用程序重写为不需要永久服务,而是AlarmManager按预期使用,那么您应该具有更好的生存能力。

于 2011-12-22T21:23:33.280 回答
0

我相信 Lork 想要实现的目标与我也在努力的目标相似。他希望警报管理器触发将处理警报的广播接收器,即使它所属的应用程序已终止(例如由 Android 操作系统)。

例如:应用程序设置了一个类型为 ELAPSED_REALTIME_WAKEUP 或 RTC_WAKUP 的警报,并通过一个引用应用程序上下文和广播接收器类的 Intent 来处理它触发时的广播接收器。接收器在应用程序清单中声明为 a。

在正常情况下,当应用程序正在运行或暂停时,当警报响起时,会触发广播接收器,唤醒设备并根据需要恢复应用程序,并处理警报。但是,如果应用程序已经被杀死(例如被操作系统杀死),那么警报仍然会响起(因为它仍然被注册)但是广播接收器不会被触发并且 LogCat 显示一个空指针异常,(我假设是因为对应用程序的引用不再在内存中)。即使 Context 已通过,也会发生这种情况。

我(我假设 Lork)在这里错过了一个简单的策略吗?还是不可能?广播接收器是否可以独立存在并在必要时触发应用程序?

我一直在玩弄的一个不完美的策略是将所有应用程序数据访问移动到一个内容提供程序中,并有一个单独的低配置应用程序,它只实现广播接收器——触发一个服务来完成工作并访问它的应用程序数据需要通过内容提供者。这仍然会被操作系统终止,但可能性较小。

于 2012-01-03T16:09:42.957 回答