JobScheduler по расписанию каждые 10 минут не всегда работает - PullRequest
0 голосов
/ 06 мая 2019

Я пытаюсь запланировать уведомление, которое должно отображаться каждый час, иногда 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
        }
    }
}

Полный исходный код на случай, если вы захотите помочь / построить его самостоятельно

...