即使在用户从最近的应用程序中删除应用程序后,如何安排和发送通知?

How To schedule and a send Notification even after user removes app from recent apps?

我的应用很简单

  1. 从用户那里抽时间(我知道怎么做)

  2. 安排一个通知(我知道怎么发通知)

  3. 现在我只想知道即使用户将它从最近的应用程序中删除,我如何发送这个通知。

尝试过的解决方案- AlarmManager、BroudcastReceiver、服务、意图服务,

但只有当应用程序存在于 RAM 中(移动设备的最近应用程序列表)时,所有这些都有效。

我该怎么做,请告诉我主题。我不需要代码。 我会研究的。

您可以使用 WorkManger 安排任务。

WorkManager API 可以轻松安排可延迟的异步任务,即使应用程序退出或设备重启,这些任务也会 运行。

检查 Google 文档 here

对于通知,您可以在工作管理器中发送通知 class。了解更多 here.

这些提示应该足够了。如果您需要更多说明,请告诉我。

正如您提到的 AlarmManager 和其他软件对您不起作用,我测试了您尝试使用 JobScheduler 实现的目标。

它对我有用,甚至在从最近的应用程序列表中删除该应用程序后它仍然有效。 我在 Android 10 模拟器上试过了。

您要求提供主题/参考资料作为答案进行研究。 所以首先我会提到我使用的参考资料。

这是 JobScheduler 的代码实验室,它真的很有帮助:https://codelabs.developers.google.com/codelabs/android-training-job-scheduler/

这里有一个关于创建多个计划作业的很好的参考:https://android-developers.googleblog.com/2017/10/working-with-multiple-jobservices.html

这是我试过的。

NotificationJobService.kt

private const val NOTIF_CHANNEL_ID = "primary_notification_channel"
private const val NOTIF_CHANNEL_NAME = "Job Service notification"

class NotificationJobService : JobService() {

    override fun onStartJob(params: JobParameters?): Boolean {

        // Get Notification Manager
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        // Create Notification Channel if device OS >= Android O
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel(NOTIF_CHANNEL_ID, NOTIF_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT).let {
                notificationManager.createNotificationChannel(it)
            }
        }

        // Create PendingIntent with empty Intent
        // So this pending intent does nothing
        val pendingIntent = PendingIntent.getActivity(this, 0, Intent(), PendingIntent.FLAG_ONE_SHOT)

        // Configure NotificationBuilder
        val builder = NotificationCompat.Builder(this, NOTIF_CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle("Title")
            .setContentText("Message")
            .setAutoCancel(true)
            .setContentIntent(pendingIntent)

        // Make the Notification
        notificationManager.notify(0, builder.build())

        // False to let system know that the job is completed by the end of onStartJob(),
        // and the system calls jobFinished() to end the job.
        return false

    }

    override fun onStopJob(params: JobParameters?): Boolean {
        // True because if the job fails, you want the job to be rescheduled instead of dropped.
        return true
    }

}

MainActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Job ID must be unique if you have multiple jobs scheduled
        var jobID = 0

        // Get fake user set time (a future time 1 min from current time)
        val ( userSetHourOfDay, userSetMinute ) = getMockUserSetTime()

        val timeToWaitBeforeExecuteJob = calculateTimeDifferenceMs(userSetHourOfDay, userSetMinute)

        (getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler).run {
            schedule(
                JobInfo.Builder(
                    jobID,
                    ComponentName(baseContext, NotificationJobService::class.java)
                )
                // job execution will be delayed by this amount of time
                .setMinimumLatency(timeToWaitBeforeExecuteJob)
                // job will be run by this deadline
                .setOverrideDeadline(timeToWaitBeforeExecuteJob)
                .build()
            )
        }
    }

    // Returns a pair ( hourOfDay, minute ) that represents a future time,
    // 1 minute after the current time
    private fun getMockUserSetTime() : Pair<Int, Int> {
        val calendar = Calendar.getInstance().apply {
            // add just 1 min from current time
            add(Calendar.MINUTE, 1)
        }
        return Pair(calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE))
    }

    // Calculate time difference relative to current time in ms
    private fun calculateTimeDifferenceMs(hourOfDay: Int, minute: Int) : Long {
        val now = Calendar.getInstance()
        val then = (now.clone() as Calendar).apply {
            set(Calendar.HOUR_OF_DAY, hourOfDay)
            set(Calendar.MINUTE, minute)
        }
        return then.timeInMillis - now.timeInMillis
    }

}

我在安排作业时使用了 setMinimumLatency(timeToWaitBeforeExecuteJob)setOverrideDeadline(timeToWaitBeforeExecuteJob) 约束,因此作业将在我们希望的确切时间执行 运行。

我 运行 该应用程序一次,返回并从最近的应用程序列表中删除该应用程序。 然后我暂停了设备,1分钟后,我听到了提示音。 当我恢复设备并检查时,预期的通知就在那里。

You should consider Remon Shehatta's as well. Because it seems WorkManager sits on top of JobScheduler and AlarmManager, and picks the right one based on device's API level. So it may be better to use WorkManager if you are targeting API levels older than 21. But as you mentioned that AlarmManager did not work for you, you should experiment and choose the right one.

请向我们更新您的发现。