每10分钟安排的JobScheduler并不总是有效

时间:2019-05-06 19:25:33

标签: android kotlin service android-service android-jobscheduler

我试图安排一个需要每小时(有时为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,以防您需要自己帮助/构建

0 个答案:

没有答案