洛克,
在我自己解决了类似的问题之后,我可能会给你一些建议。我假设您将 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 以进行调试。如果应用程序终止,
其他人可能有更好的方法来解决这些问题或其他一些指示(我当然会很高兴看到其他输入),但我希望这些想法对您有所帮助并祝您好运!