I am trying to get some knowledge in reactive programming and working with flows, so I took a method out of my contact class and tried to rewrite it a bit and use a flow to emit the data
val getMCxContactsFlow: Flow<MCxContact> = flow {
val contact: MCxContact? = null
val uri = RawContacts.CONTENT_URI
.buildUpon()
.appendQueryParameter(RawContacts.ACCOUNT_NAME, accountName)
.appendQueryParameter(RawContacts.ACCOUNT_TYPE, accountType)
.build()
val cursor = contentResolver.query(
uri,
arrayOf(Contacts._ID),
null,
null,
null
)
getCursorInformation(cursor) { result ->
result.apply {
try {
val id = getStringOrNull(getColumnIndex(Contacts._ID))
?: throw Exception("Cant find contact ID")
Log.d(TAG, id)
val query = contentResolver.query(
Data.CONTENT_URI,
null,
"${Data.RAW_CONTACT_ID} =?",
arrayOf(id),
null
)
val contacts = getContactFromQuery(query)
emit(contacts) // The Emit with the Error
} catch (e: Exception) {
e.printStackTrace()
}
}
}
emit(contact!!) //This emit with the fake contact from first line works
}
private fun getCursorInformation(
cursor: Cursor?,
iterator: (cursor: Cursor) -> Unit
) {
if (cursor == null) throw Exception("getCursorInformation: cursor is null")
else {
cursor.apply {
if (count > 0) {
while (moveToNext()) iterator(this)
} else {
throw Exception("Cursor data is empty")
}
close()
}
}
}
But Android gives me an Error on emit: Suspension functions can be called only within coroutine body
The emit on the end does work without any error. So I assume there is a scope Problem. But what exactly is wrong here?
CodePudding user response:
Thanks to user @amanin
The solution is to make the callback iterable()
function also suspendable
private suspend fun getCursorInformation(
cursor: Cursor?,
iterator: suspend (cursor: Cursor) -> Unit
) {
if (cursor == null) throw Exception("getCursorInformation: cursor is null")
else {
cursor.apply {
if (count > 0) {
while (moveToNext()) iterator(this)
} else {
throw Exception("Cursor data is empty")
}
close()
}
}
}
CodePudding user response:
To emit flow values from inside a callback like this, you should use a callbackFlow
. Just replace flow { ... }
with callbackFlow { ... }
and replace emit
with send
(or sendBlocking
, if necessary).
You might also need to add something like awaitClose
to make sure the flow remains open while the callback is still active. Check the docs for more instructions on how to set up a callback flow.