Home > Software engineering >  Getting a "Suspension function con only be called..." error with flow
Getting a "Suspension function con only be called..." error with flow

Time:01-26

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.

  • Related