0

I made a Service that is actually a simple background counter.

It just pluses 1 to a last number and then it goes to UI.

My previous problem was about the fact that Handler() sometimes worked very slow when smartphone was turned off or if it wasn't charging. Recently I found the same problem in this forum. I added PowerManager.WakeLock to my Service and everything worked fine...

But I decided to test it for a longer time and started the app simultaneously on three smartphones and leave them for about an hour and a half. When I returned I have seen a complete difference between three of them.

The first shows 5100 (1 h 25 mins), the second - 2800 (46 mins) and the third - 5660 (1 h 34 mins). I was pretty sure that wakelock will do the job correctly but now I don't know what happened there.

Here is a code of my Service:

class Timer_Service : Service() {
    companion object {
        val PARAM_OUT_MSG = "0"
    }
    var i = 0

    private lateinit var wakeLock: PowerManager.WakeLock
    private lateinit var mHandler: Handler
    private lateinit var mRunnable: Runnable

    override fun onBind(p0: Intent?): IBinder? {
        TODO("not implemented")
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {

        val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
        wakeLock = powerManager.newWakeLock(
            PowerManager.PARTIAL_WAKE_LOCK,
            "ExampleApp:Wakelock"
        )
        wakeLock.acquire()

        val broadcastIntent = Intent()
        broadcastIntent.action = "com.example.infocell.action.RESPONSE"
        mHandler = Handler()
        mRunnable = Runnable {
            showOrderNumber()
            broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT)
            broadcastIntent.putExtra(PARAM_OUT_MSG, i.toString())
            sendBroadcast(broadcastIntent)
        }
        mHandler.postDelayed(mRunnable, 1000)
        return START_NOT_STICKY
    }

    override fun onDestroy() {
        super.onDestroy()
        mHandler.removeCallbacks(mRunnable)
    }

    private fun showOrderNumber() {
        i += 1
        mHandler.postDelayed(mRunnable, 1000)
    }
}

Manifest also contains <uses-permission android:name="android.permission.WAKE_LOCK" />

Pavel Pereverzev
  • 459
  • 1
  • 6
  • 21
  • You might consider reading [the documentation on background work in Android](https://developer.android.com/training/best-background), particularly [the challenges in background processing](https://developer.android.com/guide/background). You seem to be wanting to do background work very frequently, which simply is no longer practical on Android, after **lots** of user complaints regarding battery usage. – CommonsWare Mar 07 '20 at 19:32

1 Answers1

0

Finally after various tests I got the most precise way to make a simple counter. Instead of relatively reliable Handle() method I would recommend to use Timer(). It worked absolutely equal on all of my four smartphones. Wakelock is also required for that. I would also test JobScheduler() and CountDownTimer() for getting all testing results but I am glad with timer so far.

I will share my code if someone is looking for solution for such tasks.

class Timer_Service : Service() {
    companion object {
        val PARAM_OUT_MSG = "0"
    }
    var i = 0

    private lateinit var wakeLock: PowerManager.WakeLock
    private lateinit var timer: Timer

    override fun onBind(p0: Intent?): IBinder? {
        return null
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
        wakeLock = powerManager.newWakeLock(
            PowerManager.PARTIAL_WAKE_LOCK,
            "ExampleApp:Wakelock"
        )
        wakeLock.acquire()

        val broadcastIntent = Intent()
        broadcastIntent.action = "com.example.infocell.action.RESPONSE"

        timer = Timer()
        val task = object : TimerTask() {
            override fun run() {
                if (Trigger.getTrigger() == 0){
                    showOrderNumber()
                    // bring 'i' value to main activity
                    broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT)
                    broadcastIntent.putExtra(PARAM_OUT_MSG, i.toString())
                    sendBroadcast(broadcastIntent)
                }
            }
        }
        timer.schedule(task,0, 1000)
        return START_STICKY
    }

    override fun onCreate() {
        super.onCreate()
        var notification = createNotification()
        startForeground(1, notification)
    }

    private fun createNotification(): Notification {
        val notificationChannelId = "ENDLESS SERVICE CHANNEL"

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager;
            val channel = NotificationChannel(
                notificationChannelId,
                "My Service",
                NotificationManager.IMPORTANCE_HIGH
            ).let {
                it.description = "Service channel"
                it.enableLights(true)
                it.lightColor = Color.RED
                it.enableVibration(true)
                it.vibrationPattern = longArrayOf(100)
                it
            }
            notificationManager.createNotificationChannel(channel)
        }

        val pendingIntent: PendingIntent = Intent(this, MainActivity::class.java).let { notificationIntent ->
            PendingIntent.getActivity(this, 0, notificationIntent, 0)
        }

        val builder: Notification.Builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) Notification.Builder(
            this,
            notificationChannelId
        ) else Notification.Builder(this)

        return builder
            .setContentTitle("My Service")
            .setContentText("Endless service working...")
            .setContentIntent(pendingIntent)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setTicker("Ticker text")
            .setPriority(Notification.PRIORITY_HIGH) // for under android 26 compatibility
            .build()
    }

    override fun onDestroy() {
        super.onDestroy()
        // Trigger is a separate kotlin class with variables
        if (Trigger.getTrigger() == 1){
            timer.cancel()
            timer.purge()
        }
    }

    private fun showOrderNumber() {
        i += 1
    }

}
Pavel Pereverzev
  • 459
  • 1
  • 6
  • 21