Search code examples
androidkotlintimerschedule

Android timer when phone is idle


I am trying to create an Android app which plays a sound every few seconds. I want this to work even when the phone is idle. At the moment everything works fine even when the phone screen is off. But after about a minute, the timer stops working. As soon as the screen is turned back on, the missed sounds are played in quick succession. I struggle to find the right terms and concepts to properly find a solution with Google.

When I first encountered this issue, I made sure that my service was running in the background. As it seems, the service is also enabled in the background because everything works fine as long as the screen is not turned off.

Code for running the service in the background:

override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
    timer.scheduleAtFixedRate(TimeTask(), 0, 100);
    return START_STICKY
}

private inner class TimeTask() : TimerTask() {
     override fun run() {
        sendBroadcast(Intent(TIMER_UPDATED))
     }
}

Since this didn't work, I tried to make the service a foreground service. But this didn't work either. (I tried to do it as shown here) Code for running the service in foreground:

private fun runInBackground() {
        val channelId =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                createNotificationChannel("service", "something")
            } else {
                ""
            }

        val notification: Notification = NotificationCompat.Builder(this, channelId)
            .setContentTitle("title")
            .setContentText("text")
            .setSmallIcon(R.drawable.alert_dark_frame)
            .build()

        startForeground(1, notification)
}

@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(channelId: String, channelName: String): String{
        val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_NONE)
        channel.lightColor = Color.BLUE
        channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        (getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager)
            .createNotificationChannel(channel)
        return channelId
}

I read something about scheduling tasks in Android. And found the AlarmManager, but I don't think this would really work the way I want it to because I would have to schedule an alarm for every 100ms. The official doc also states that this shouldn't be used in that way and that "handlers" are more suited, but I struggle to understand how I could replace my current timer with such a handler. I have tried to implement something, but failed.

val updateHandler = Handler()

val runnable = Runnable {
    // some action
}

updateHandler.looper(runnable, 100)

Solution

  • Finally solved my problem. Something that I didn't understand before was a "wakelock". It looks something like this:

    val wakeLock: PowerManager.WakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
            newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ShuttleRun::DefaultWakeLock").apply {
                acquire(30*60*1000L)
            }
        }
    

    It basically just keeps the device awake, which doesn't mean that the screen is turned on, but rather that the service can run in the background without being disturbed.