Я пытаюсь запланировать уведомление, которое должно отображаться каждый час, иногда 45 минут, в зависимости от различных факторов, не связанных с этим вопросом.В целях тестирования я запускаю его каждые 10 минут с максимальной задержкой выполнения, равной 1 минуте.Моя цель - иметь службу, которая планирует уведомления, а затем перезапускается через 10 минут с максимальной продолжительностью выполнения 1 минута.В будущем службе планирования необходимо извлечь что-то из Интернета, поэтому для JobInfo NetworkType должно быть установлено значение Любой.Однако проблема с моим текущим кодом заключается в том, что он делает перепланирование его каждые 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
}
}
}
Полный исходный код на случай, если вы захотите помочь / построить его самостоятельно