androidkotlinpush-notificationkotlin-coroutinesjobservice

JobService to fire Notification by using Switch widget on Android


I have an app with a Switch that has to fire my JobService. I called two methods in setOnCheckedChangeListener of the Switch, one for register and one for unregister the job. It seems to work, like debug shows, but the the background work inside the onStartJob. What am I missing?

MemoNotificationService.kt

class MemoNotificationService : JobService() {

    companion object {
        const val TAG = "MemoPush"
        const val serviceId = 4242
    }


    override fun onStartJob(params: JobParameters?): Boolean {
        doBackgroundWork(params)
        return true
    }

    private fun doBackgroundWork(params: JobParameters?) {
        CoroutineScope(Dispatchers.IO).launch {
            Log.d(TAG, "Push sending...")
            sendNotification("body push 1", "title push 1", "", 1)
            Log.d(TAG, "Push sent")
            jobFinished(params, false)
        }
    }

    override fun onStopJob(params: JobParameters?): Boolean {
        Log.d(TAG, "<Job interrupt>")
        return true
    }


    fun sendNotification(
        messageBody: String,
        messageTitle: String,
        largeIconPath: String,
        pushId: Int
    ) {

        val channelId = getString(R.string.consumer_notification_channel_id)
        val channelName = getString(R.string.consumer_notification_channel_name)
        val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
        val notificationBuilder = NotificationCompat.Builder(this, channelId)
            .setSmallIcon(R.drawable.logo_anim1)
            .setColor(ContextCompat.getColor(this, R.color.colorAccent))
            .setColorized(true)
            .setContentTitle(messageTitle)
            .setContentText(messageBody)
            .setAutoCancel(true)
            .setSound(defaultSoundUri)
            .setLights(Color.YELLOW, 1000, 1000)
        

        val notificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        // Since android Oreo notification channel is needed.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                channelId,
                channelName,
                NotificationManager.IMPORTANCE_DEFAULT
            )
            channel.enableLights(true)
            channel.lightColor = Color.YELLOW
            notificationManager.createNotificationChannel(channel)
        }

        notificationManager.notify(pushId, notificationBuilder.build())
    }


}

SettingsFragment.kt

private fun registerNotification() {
        //TODO("Not yet implemented")
        val componentService = ComponentName(requireContext(), MemoNotificationService::class.java)
        val info = JobInfo.Builder(MemoNotificationService.serviceId, componentService)
            .setPersisted(true) /*persist on boot*/ /*not sure if needed*/
            .setPeriodic(30 * 1000) /*once a day = 24 * 60 * 60 * 1000 */
            .build()
        val scheduler = requireContext().getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
        val resultCode = scheduler.schedule(info)
        if(resultCode == JobScheduler.RESULT_SUCCESS)
            Log.d(MemoNotificationService.TAG, "<Job scheduled>")
        else
            Log.d(MemoNotificationService.TAG, "<Job scheduling failed>")
    }

private fun unregisterNotification() {
        //TODO("Not yet implemented")
        val scheduler = requireContext().getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
        Log.d(MemoNotificationService.TAG, "<Job scheduling cancelling> :: ${scheduler.allPendingJobs}")
        scheduler.cancel(MemoNotificationService.serviceId)
        Log.d(MemoNotificationService.TAG, "<Job scheduling cancelled> :: ${scheduler.allPendingJobs}")
    }

AndroidManifest.xml

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
...
<service

    android:name=".MemoNotificationService"
    android:permission="android.permission.BIND_JOB_SERVICE" />

Logcat

D/MemoPush: <Job scheduled>
D/MemoPush: <Job scheduling cancelling> :: [(job:4242/com.example.testjobservice/.MemoNotificationService)]
D/MemoPush: <Job scheduling cancelled> :: []

Actually I need to send push notifications with an offline service (no Internet in the entire application), once a day for example, with the same notification, or a list of... Is it the right way to achieve this?


Solution

  • Documentation says that minimum scheduler time is 15 minutes, so it would probably be the problem. Changing the code to:

    SettingsFragment.kt

    private fun registerNotification() {
            val componentService = ComponentName(requireContext(), MemoNotificationService::class.java)
            val info = JobInfo.Builder(MemoNotificationService.serviceId, componentService) 
                .setPersisted(true)
    
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
                //info.setPeriodic(24 * 60 * 60 * 1000,12 * 60 * 60 * 1000)
                info.setPeriodic(30 * 60 * 1000,15 * 60 * 1000) //for testing purpose
            }
            else {
                //info.setPeriodic(24 * 60 * 60 * 1000)
                info.setPeriodic(15 * 60 * 1000) //for testing purpose
            }
    
            val scheduler = requireContext().getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
            val resultCode = scheduler.schedule(info.build())
            if(resultCode == JobScheduler.RESULT_SUCCESS)
                Log.d(MemoNotificationService.TAG, "<Job scheduled>")
            else
                Log.d(MemoNotificationService.TAG, "<Job scheduling failed>")
    }
    

    it works like a charm, I guess.

    Anyway, for further solutions, it's better to use WorkManager that is built upon AlarmManager (API < 21) and JobScheduler (API >= 21). Official codelab: https://developer.android.com/codelabs/android-workmanager?index=..%2F..index#0