【问题标题】:What happens to a PendingIntent if the target app was force-closed?如果目标应用程序被强制关闭,PendingIntent 会发生什么?
【发布时间】:2019-02-22 07:02:17
【问题描述】:

我实际上正在开发一个应该在 5 天后发布通知的应用。

使用 AlarmManager,我将 PendingIntent 发送到我的 Receiver 类。 一切正常,直到我强制关闭我的应用程序。在这种情况下,通知不会出现。

所以我的问题: 这个 PendingIntent 会发生什么,它被触发并且没有达到它的目标? 当我的应用程序最终重新启动时,我可以检查未达到目标的 PendingIntents 吗?

编辑 1:

这些是我的广播接收器的基本部分:

    override fun onReceive(context: Context?, intent: Intent?) {
        if (context != null && intent?.action != null) {
            when (intent.action) {
                INTENT_ACTION_BOOT_COMPLETED -> handleDeviceBoot()
                INTENT_ACTION_REMINDER -> handleReminder(context, intent.getLongExtra(EXTRA_ITEM_ID, -1))
            }
        }
    }

    private suspend fun schedule(context: Context, itemId: Long, fireDate: LocalDateTime) = withContext(Dispatchers.IO) {
        AlarmManagerCompat.setAndAllowWhileIdle(
            getAlarmManager(context),
            AlarmManager.RTC,
            fireDate.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(),
            makePendingIntent(context, itemId)
        )

        with(AppDatabase.get(context).reminderDao()) {
            val oldReminder = getItemReminder(itemId)

            if (oldReminder == null) {
                insert(Reminder(itemId = itemId, fireDate = fireDate))
            } else {
                update(Reminder(id = oldReminder.id, itemId = itemId, fireDate = fireDate))
            }
        }
    }

    private suspend fun cancel(context: Context, itemId: Long) = withContext(Dispatchers.IO) {
        val reminderDao = AppDatabase.get(context).reminderDao()
        val reminder = reminderDao.getItemReminder(itemId)

        reminder?.let {
            getAlarmManager(context).cancel(makePendingIntent(context, itemId))
            reminderDao.delete(it)
        }
    }

    private fun getAlarmManager(context: Context) = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager

    private fun makePendingIntent(context: Context, itemId: Long): PendingIntent {
        val alarmIntent = Intent(context, ReminderManager::class.java).apply {
            action = INTENT_ACTION_REMINDER
            putExtra(EXTRA_ITEM_ID, itemId)
        }
        return PendingIntent.getBroadcast(context, itemId.toInt(), alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT)
    }

【问题讨论】:

    标签: android android-intent alarmmanager android-pendingintent


    【解决方案1】:

    Official Android Documentation中所定义

    PendingIntent 本身只是对系统维护的令牌的引用,该令牌描述了用于检索它的原始数据。这意味着,即使它拥有的应用程序的进程被杀死,PendingIntent 本身仍可用于其他已被赋予它的进程。如果创建应用程序稍后重新检索相同类型的 PendingIntent(相同的操作、相同的 Intent 操作、数据、类别和组件以及相同的标志),它将收到一个表示相同令牌的 PendingIntent,如果它仍然有效,并且可以因此调用 cancel() 将其删除。

    重新检查您的代码以检查是否有其他任何因素会导致此问题。

    【讨论】:

    • 所以如果我的应用程序被杀死并且在应用程序重新启动之前触发了未决意图,那么 PendingIntent 应该在应用程序启动时到达?
    • 请看看我的编辑...并且:我必须在这里获得唤醒锁吗?
    • 我查看了 dumpsys 警报日志,看起来系统在强制停止后重新安排了警报 - 奇怪地重新启动了应用程序。
    • 我实际上测试了警报会发生什么,如果您强制关闭应用程序并且我找到了解决方案,当应用程序被强制停止时警报会被删除。有没有办法检查(在下次启动期间)应用是否被强制停止?
    【解决方案2】:

    当您“强制关闭”应用程序时,应用程序将设置为“停止状态”。在“停止状态”中,除非用户手动重新启动应用程序,否则 Android 不会自动启动您的应用程序。这意味着如果您“强制关闭”您的应用,您的应用将不会收到任何广播 Intents,直到用户手动重新启动它。

    我希望(尽管我自己没有尝试过),如果您安排闹钟在 X 时间响起并且在 X 时间之前“强制关闭”应用程序,当 X 时间发生时,闹钟管理器将尝试发送PendingIntent,但Android 将拒绝实际执行BroadcastReceiver,因为应用程序处于“停止状态”。在这种情况下,我希望触发器丢失。 Android 不会重试或重新安排它。

    基本上,当用户“强制关闭”应用程序时,他是在告诉 Android 他不希望该应用程序再运行,包括该应用程序可能具有或希望将来启动的任何后台进程。

    【讨论】:

    • 请看我的 EDIT 2,这应该是什么?
    • 你在使用WorkManager吗?我能找到ACTION_FORCE_STOP_RESCHEDULE 的唯一参考是WorkManager 的源代码
    • 我用过,但可能来自 WorkManager。所以总而言之,如果App在强制停止后重新打开,我将不得不重新安排它?
    • 没错。强制停止后,您的警报可能会或可能不会设置(取决于它是否已经触发)。
    • 所以如果已经触发了就不设置了,但是如果还在运行,应该在正确的时间重置并触发?
    【解决方案3】:

    答案很简单:主动 PendingIntents 在应用程序强制停止时被取消。

    【讨论】:

      猜你喜欢
      • 2011-12-18
      • 1970-01-01
      • 1970-01-01
      • 2010-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多