Chris Pi
Chris Pi

Reputation: 612

Getting a "Suspension function con only be called..." error with flow

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?

Upvotes: 0

Views: 188

Answers (2)

Chris Pi
Chris Pi

Reputation: 612

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()
            }
        }
    }

Upvotes: 1

Sam
Sam

Reputation: 9952

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.

Upvotes: 0

Related Questions