我试图安排一个需要每小时(有时为45分钟)显示的通知,具体取决于与此问题无关的各种因素。为了进行测试,我每10分钟运行一次,最大执行延迟为1分钟。我的目标是拥有一个计划通知的服务,然后在10分钟后重新启动自身,最大执行长度为1分钟。将来,计划服务需要从Internet检索某些内容,因此JobInfo NetworkType应该设置为Any。但是,当前代码的问题是,它每10分钟确实重新安排一次,但并非总是如此。它有时会在3个小时内(使用电话,处于活动状态的互联网)随机不显示通知,然后再次开始显示通知。我做错了什么,导致它有时会跳过发射几个小时。
注意:我不想使用AlarmManager,因为它不会通过重新启动来保留作业,而我希望使用JobScheduler进行作业。
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Plan the notification plan service
scheduleJob(
context = this,
time = Calendar.getInstance(Util.TIMEZONE).timeInMillis,
executionTime = TimeUnit.MINUTES.toMillis(1),
service = PlanNotificationsService::class.java,
extras = PersistableBundle()
)
}
}
PlanNotificationsService.kt
class PlanNotificationsService : JobService() {
override fun onStopJob(params: JobParameters?): Boolean {
log("Service Abruptly stopped")
return true
}
override fun onStartJob(job: JobParameters?): Boolean {
log("Plan Notification On Start Job")
GlobalScope.async {
val now = Calendar.getInstance(Util.TIMEZONE)
log("Scheduling for " + (now.timeInMillis + TimeUnit.MINUTES.toMillis(1)))
log("Scheduling for " +
"${now.get(Calendar.HOUR_OF_DAY)}:" +
"${now.get(Calendar.MINUTE) + 10}:" +
"${now.get(Calendar.SECOND)}")
// Schedule planning of notifications (itself)
Util.scheduleJob(
context = applicationContext,
time = now.timeInMillis + TimeUnit.MINUTES.toMillis(10),
executionTime = TimeUnit.MINUTES.toMillis(1),
service = PlanNotificationsService::class.java,
extras = PersistableBundle(),
persist = true
)
// Schedule notification
Util.scheduleJob(
context = applicationContext,
time = now.timeInMillis,
executionTime = TimeUnit.MINUTES.toMillis(1),
service = NotificationJobService::class.java,
extras = PersistableBundle().apply {
putString("Title", "Test Title")
putString("Message", "Test message")
}
)
jobFinished(job, false)
}
return true
}
}
NotificationJobService
class NotificationJobService : JobService() {
override fun onStopJob(job: JobParameters?): Boolean = false
override fun onStartJob(job: JobParameters?): Boolean {
log("Notification On Start Job")
job?.extras?.let { extras ->
GlobalScope.async {
scheduleNotification(extras)
jobFinished(job, false)
}
}
return true
}
private fun scheduleNotification(bundle: PersistableBundle) {
log("Notification Creating Notification")
try {
val notificationManager = applicationContext
.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val title = bundle.getString("Title")
val message = bundle.getString("Message")
val icon = R.drawable.ic_launcher_background
// TODO COLOR & DETAILS INTENT
val builder = NotificationCompat.Builder(applicationContext)
.apply {
setSmallIcon(icon)
setContentTitle(title)
setContentText(message)
setStyle(NotificationCompat.BigTextStyle().bigText(message))
setAutoCancel(true)
setDefaults(0)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel(applicationContext)
builder.setChannelId(SCHEDULE_NOTIFICATION_ID)
}
notificationManager.notify((Math.random() * Int.MAX_VALUE).toInt(), builder.build())
} catch (e: Exception) {
log("Fucked Up")
e.printStackTrace()
}
}
private fun createNotificationChannel(context: Context) {
log("Notification Creating Notification Channel")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
if (notificationManager.getNotificationChannel(SCHEDULE_NOTIFICATION_ID) != null) return
val notificationChannel = NotificationChannel(SCHEDULE_NOTIFICATION_ID, "Schedule Notifications", NotificationManager.IMPORTANCE_LOW)
notificationChannel.enableLights(true)
//TODO COLOR
notificationManager.createNotificationChannel(notificationChannel)
}
}
}
Util.kt
class Util {
companion object {
val TAG = "BackNotiTag"
val SCHEDULE_NOTIFICATION_ID = "BackgroundNotification"
val TIMEZONE = TimeZone.getTimeZone("Europe/Amsterdam")
fun log(message: Any) {
//Log.d(TAG, message.toString())
println("$TAG $message")
}
fun scheduleJob(
context: Context,
time: Long,
executionTime: Long,
service: Class<*>,
extras: PersistableBundle = PersistableBundle(),
persist: Boolean? = null
) {
val serviceComponent = ComponentName(context, service)
val maximumLatency = timeUntil(time)
log("Max: $maximumLatency")
val builder = JobInfo.Builder((Math.random() * Int.MAX_VALUE).toInt(),
serviceComponent).apply {
setMinimumLatency(maximumLatency - executionTime)
setOverrideDeadline(maximumLatency)
setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
//setBackoffCriteria(executionTime, JobInfo.BACKOFF_POLICY_LINEAR)
setExtras(extras)
}
if (persist != null) builder.setPersisted(persist)
ContextCompat.getSystemService(context, JobScheduler::class.java)?.schedule(builder.build())
}
private fun timeUntil(time: Long): Long {
val now = Calendar.getInstance(TIMEZONE)
val plannedDate = Calendar.getInstance(TIMEZONE)
plannedDate.timeInMillis = time
val difference = plannedDate.timeInMillis - now.timeInMillis
return if (difference < 0) 0 else difference
}
}
}
Full source,以防您需要自己帮助/构建