Search code examples
androidkotlinandroid-activityservice

Create a service that runs while activity is open


I want to create a Service that makes a network operation but i want it to run as long as an activity is open. So i want to bind it in the activity's lifecycle. If the user navigates to another activity and back i want it to restart. If the screen goes off and the user reopens it i want it to start again if its not possible to keep it

class PushService: Service() {

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

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // ToDo Create the request that i want
    }
}

So i have to start and stop the service in the onResume and onStop of the Activity?

 override fun onResume() {
        super.onResume()

        Intent(this, PushService::class.java).also { intent ->
            startService(intent)
        }
    }

    override fun onStop() {
        super.onStop()

        stopService(Intent(this, PushService::class.java))
    }

Im not sure how to do that. Does anybody know the correct way?

Perhaps it would be a good idea to just create the proccess that i want inside the ViewModel instead of start a Service for it?


Solution

  • You are mostly doing it correctly, except you should either be using onResume/onPause or onStart/onStop, not mixing the two pairs. onStart and onStop are only called when your activity is going out of view entirely. So in your example, if a dialog from another app appeared in front of yours, onStop would not get called, but onResume would get called so your already started service will get multiple onStartCommand calls.

    However, the whole point of Services is to run operations that continue when your app is not visible. If you're not doing that, it would be simpler to write your own class (maybe that implements LifecycleObserver or borrows lifecycleScope from the Activity) to handle the background work. Then you wouldn't have to deal with registering it in the manifest and handling intents.

    Example of a LifecycleObserver:

    // lifecycle is a property of AppCompatActivity. You can instantiate this class
    // from your activity.onCreate()
    class MyNeworkTaskManager(lifecycle: Lifecycle): LifecycleObserver, CoroutineScope by lifecycle.coroutineScope {
    
        init {
            lifecycle.addObserver(this)
        }
    
        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
        private fun onResume() {
            startMyRequest()
        }
    
        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        private fun onPause() {
            pauseOrCancelMyRequest()
        }
    
        // Alternatively, if you want to expose suspend functions so your activity can request 
        // and respond to data in a coroutine without callbacks:
        suspend fun getMyData(args: String): MyData {
            val results = someNetworkRequestSuspendFunction(args)
            return MyData(results)
        }
    
        // Or if you want to use coroutines for your network request, but still want
        // your activity to use callbacks so it doesn't have to use coroutines to call
        // these functions:
        fun getMyDataAsync(args: String, callback: (MyData) -> Unit) = launch {
            val results = someNetworkRequestSuspendFunction(args)
            callback(MyData(results))
        }
    
    }
    

    I don't do much with networking myself. But whatever library you're using, you can usually convert callbacks to coroutines using suspendCancellableCoroutine. There are tutorials for that you can look up.