Is using runBlocking correct way to interface with...
# coroutines
u
Is using runBlocking correct way to interface with blocking world when using MutableStateFlow?
Copy code
class PermissionManager() {
    private val manualTrigger = MutableSharedFlow<Unit>()
    
    ...

    fun notifyPermissionChanged() {
        runBlocking { <---------------------
            manualTrigger.emit(Unit)
        }
    }
}

class Activity {
	override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        permissionManager.notifyPermissionChanged()
    }
}
tryEmit never does anything
🚫 1
b
You should dispatch it on Dispatchers.IO instead. runBlocking is meant to be used as an entrypoint only
u
not sure what you mean. that down stream processing will happen on Activity thread? yea im aware
b
No I'm saying that you should use async(Dispachers.IO) {} instead of runBlocking. Otherwise you risk blocking UI thread
u
do you mean GlobalScope.async/launch?
b
You should avoid global scope too. There was an article about that by @elizarov a while ago, but I can't find it for you now.
Google "globalScope us bad" or smth. It was on medium
u
I'm aware, but creating a scope just to pipe the event in feels contraproductive, I'd rather just block the thread for the 0.1ms it takes.. since im the type which has private scope instances, not injected
b
If you're ok blocking main thread then go ahead with runBlocking. Nothing wrong with that
u
I see, thanks! btw any obvious reason tryEmit doesnt work?
b
Or you can just make that function suspend
u
which one, the notify?
b
Yes
u
I'd rather not, im just trying to bridge blocking to reactive world, hence the mutableflow; and coming from rxjava it catches me offguard that the emit is a suspend function
b
I personally try to keep my entire app in suspend mode when working with coroutines.
u
yes, all after the proxy is coroutine/flow world
b
I think there are rxjava extensions for coroutines to help converting between the two if that helps you at all
u
not really necessary, its just that flow surfaces the need for backpressure handling of piping in emits into the relay/mutableflow, rx implicitly would block the activity thread if backpressure were to happen, here its obvious via runblocking
j
async(<http://Dispatchers.IO|Dispatchers.IO>) { ... }
you mean
suspend
+
withContext(<http://Dispatchers.IO|Dispatchers.IO>) { ... }
? I can't find an async function
b
async is an extension to coroutineScope
j
but where he is using runBlocking it isn't a suspend fun so he has no scope there
b
Thus me mentioning workarounds around GlobalScope
u
@Adam Powell says no? rather to GlobalScope.launch it?
a
use a buffer or reconsider whether multicast events are the right tool here
f
tryEmit will fail if there are no subscribers to the flow. You can use the
extraBufferCapacity
parameter to ensure the flow will accept your value
👍 1
☝️ 1
u
I don't think I follow. I'm trying to turn android callback into reactive events, but since it's Activity callback for the permissions, I can only imperatively proxy the events via MutableSharedFlow, no? What else is there to be done? I cannot wrap it cold-ly as I would with BroadcastReceiver and CallbackFlow for example
k
As Francesc said, what you are looking for is:
Copy code
val manualTrigger = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
it will let you use
MutableSharedFlow.tryEmit
. You can read more about why you need this here: https://itnext.io/mutablesharedflow-is-kind-of-complicated-61af68011eae
u
oh that's some footgun