Day162 — Convert callback to Coroutine
Here record the notes I took when I am reading “Avoid Callback Hell With Kotlin Coroutines”.
One-shot callbacks
use suspendCoroutine
suspend fun DatabaseReference.read(): DataSnapshot = suspendCoroutine { continuation ->
val valueEventListener = object : ValueEventListener {
override fun onCancelled(error: DatabaseError) {
continuation.resumeWithException(error.toException())
}
override fun onDataChange(snapshot: DataSnapshot) {
continuation.resume(snapshot)
}
}
addListenerForSingleValueEvent(valueEventListener)
}
suspend fun getGroups(): List<Group> {
return groupRef.read().mapToGroups()
}
class MyViewModel : ViewModel() {
fun getGroups() {
// call it inside a coroutine scope
viewModelScope.launch {
val result = getGroups()
...
}
}
}
wrap callback with suspendCoroutine
and make the method, then use resume
or resumeWithException
at the place where the callback occurs.
Subscription based callbacks
use callbackFlow
suspend fun DatabaseReference.subscribe(): Flow<Player> = callbackFlow {
val childEventListener = object : ChildEventListener {
override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) {
trySend(
Player(
snapshot.key
)
)
}
override fun onChildChanged(snapshot: DataSnapshot, previousChildName: String?) {}
override fun onChildRemoved(snapshot: DataSnapshot) {}
override fun onChildMoved(snapshot: DataSnapshot, previousChildName: String?) {}
override fun onCancelled(error: DatabaseError) {}
}
addChildEventListener(childEventListener)
awaitClose {
removeEventListener(childEventListener)
}
}
// subscribe to the data
val flow = DatabaseReference().subscribe()
flow.collect { player ->
...
}
wrap callback listener with callbackFlow
, use trySend
to send the data, and use awaitClose
to close the subscription when needed.
Function vs method
Method is a function associated to an object.
Function is a more general term, and all methods are also functions.
class A {
fun someMethod() {}
}