Hi :android-wave: , For `ViewModel` (AAC) , we ha...
# android
a
Hi 👋 , For
ViewModel
(AAC) , we have
viewModelScope
- A
CloseableCoroutineScope
tied to the ViewModel lifecycle. Do we have any such
CloseableCoroutineScope
for
BroadcastReceiver()
?
c
you shouln’t launch coroutines/threads on a BroadcastReceiver.
Because a receiver’s
onReceive(Context, Intent)
method runs on the main thread, it should execute and return quickly. If you need to perform long running work, be careful about spawning threads or starting background services because the system can kill the entire process after
onReceive()
returns. For more information, see Effect on process state To perform long running work, we recommend:
https://developer.android.com/develop/background-work/background-tasks/broadcasts#security-and-best-practices
a
The work in the coroutine is relatively simple and I expect it to complete very quickly. It fetches data from preferences data store (Hence the need for coroutine) and then set a alarm based on if the flag is true or not.
c
just use
runBlocking
then and stay on the main thread.
What I was trying to say is, there is nothing like a coroutine scope for the broadcast receiver as it would not work as expected.
a
Technically, that is exactly what happens. The coroutine is using the main thread.
Copy code
val coroutineScope = MainScope() + job
Just a bit not sure to use
runBlocking
.
c
you dont need the scope. from the runBlocking docs:
```* Runs a new coroutine and blocks the current thread interruptibly until its completion.
*
* It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in
*
main
functions and in tests.```
and you need to block the current thread and not let
onReceive
complete before your suspend function returns.
a
Does that mean this
onReceive
can complete before the coroutine completes 🤔 ?
Copy code
@AndroidEntryPoint
public class TimeChangedReceiver : BroadcastReceiver() {
    private val job = Job()
    private val coroutineScope = MainScope() + job

    @Inject
    public lateinit var alarmKit: AlarmKit

    override fun onReceive(
        context: Context?,
        intent: Intent?,
    ) {
        if (intent?.action == "android.intent.action.TIME_SET") {
            coroutineScope.launch {
                doWork()
                cleanUp()
            }
        }
    }

    private suspend fun doWork() {
        alarmKit.enableReminder()
    }

    private fun cleanUp() {
        job.cancel()
    }
}
c
lots of questionable things you are doing here 😅
and yes, after the
launch
call your function will return.
a
Can you please share more info 😅 . I made these changes based on this message from a different thread. https://kotlinlang.slack.com/archives/C88E12QH4/p1715586269365899?thread_ts=1715581758.821969&cid=C88E12QH4
c
yeah, you are not canceling the scope but a job that you never assign and as said after the
launch
call your function will return and is free for getting killed by the system.
a
Okay. So
runBlocking
is the only proper solution for this? Also, anything else wrong with the code?
c
a simple
Copy code
override fun onReceive(
        context: Context?,
        intent: Intent?,
    ) {
        if (intent?.action == "android.intent.action.TIME_SET") {
            runBlocking {
                doWork()
            }
        }
    }
should be all you need.
or as the documentation suggest, start a service and do your suspend work in a
androidx.lifecycle.LifecycleService
which handles the scope as cancelation properly.
a
Okay 👍 . I will continue with the
runBlocking
for now. I will switch to service if required later. thank you color
👍 1