Skip to content

Instantly share code, notes, and snippets.

@RohitSurwase
Last active February 4, 2023 10:02
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save RohitSurwase/6a31ecdbcdc7e8712a6b517e305d7308 to your computer and use it in GitHub Desktop.
Save RohitSurwase/6a31ecdbcdc7e8712a6b517e305d7308 to your computer and use it in GitHub Desktop.
IntentService (Service) using Kotlin Coroutines instead of Handler+Looper.
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.support.annotation.Nullable
import android.support.annotation.WorkerThread
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.channels.actor
import kotlin.coroutines.CoroutineContext
abstract class CoroutineIntentService(private val mName: String) : Service(), CoroutineScope {
private var coroutineJob: Job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO + coroutineJob
private var mRedelivery: Boolean = false
private lateinit var handlerActor: SendChannel<ActorMessage>
fun setIntentRedelivery(enabled: Boolean) {
mRedelivery = enabled
}
override fun onCreate() {
super.onCreate()
handlerActor = handlerActor(mName)
}
override fun onStartCommand(@Nullable intent: Intent?, flags: Int, startId: Int): Int {
runBlocking {
val msg = ActorMessage()
msg.arg1 = startId
msg.obj = intent
intent?.let { handlerActor.send(msg) }
}
return if (mRedelivery) Service.START_REDELIVER_INTENT else Service.START_NOT_STICKY
}
override fun onDestroy() {
coroutineJob.cancel()
}
@Nullable
override fun onBind(intent: Intent): IBinder? {
return null
}
@WorkerThread
protected abstract fun onHandleIntent(@Nullable intent: Intent?)
private fun CoroutineScope.handlerActor(mName: String) = actor<ActorMessage>(
context = coroutineJob + CoroutineName(mName),
capacity = Channel.UNLIMITED,
start = CoroutineStart.LAZY
) {
for (msg in channel) {
onHandleIntent(msg.obj as Intent)
stopSelf(msg.arg1)
}
}
}
data class ActorMessage(var what: Int = 0, var arg1: Int = 0, var obj: Any? = null)
/**
* CoroutineIntentService usage example
*
* class MyCoroutineIntentServiceImpl : CoroutineIntentService("MyCoroutineIntentServiceImpl") {
* override fun onHandleIntent(intent: Intent?) {
* //Handle your intent
* }
* }
*
*/
@dave08
Copy link

dave08 commented Nov 26, 2019

Hi, does this handle intents in parallel since you're using Dispatchers.IO? I think the normal functionality of IntentService is to queue incoming requests until the current one is finished.

@RohitSurwase
Copy link
Author

Hi @dev08, this IntentService using Coroutines was just an experiment that I did. I had tried it in one of my example and it worked but that example was not multi-threaded. And today, there are more advanced concepts in Kotlin (Flow and Channel) that we can use to improve this experimental service if we want to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment