https://kotlinlang.org logo
#juul-libraries
Title
# juul-libraries
e

Eric Boggs

11/23/2021, 5:18 PM
Hey, another question. I'm following the sensortag example of creating a scanner for my android project. However, instead of collect, I want to use .first(). I noticed that when using .onCompletion, instead of the cause being null, it's actually an AbortFlowException: No more elements needed. It looks like there were a few tickets about this, but I'm unsure if this is expected behavior. I'm on 0.10.3
In this case, it seems like I might be able to just move things from onCompletion into .first and still accomplish the same goal, potentially. I'm still learning about flow but I essentially need to keep off a bunch of writes and observations that happen in sequential order
t

travis

11/23/2021, 5:26 PM
Are you able to share a small sample code snippet of what you have?
e

Eric Boggs

11/23/2021, 7:51 PM
Yes, sorry! Looks something like this
Copy code
scanScope.launch {
    try {
        withTimeout(10000) {
            scanner
                .advertisements
                .catch { cause -> Timber.e(cause) }
                .filter { it.isHub }
                .onCompletion { cause ->
                    if (cause == null) {
                        connect()
                    } else {
                        // We've failed
                    }
                }
                .first { advertisement ->
                    // This is the advertisement we want, so set it
                    true
                }

        }
    } catch (e: Exception) {
        // We've failed
        Timber.e(e)
    }
}
So, if the correct bluetooth device is filtered and collected in .first, the cause will be an AbortFlowException as opposed to null
t

travis

11/24/2021, 6:07 AM
Maybe try something along the lines of?:
Copy code
scanScope.launch {
    val found = withTimeoutOrNull(10_000L) {
        scanner
            .advertisements
            .first { it.isHub }
    }
    if (found == null) {
        // todo: log that scan timed out
        return
    }

    // you may opt to put the follow code in another Coroutine, depends on what the lifecycle is for various BLE operations you're doing:
    val peripheral = peripheralScope.peripheral(found)
    try {
        peripheral.connect()
    } catch (e: Exception) {
        // todo: log that connection failed
    }

    // todo: peripheral is in a connected state here
}
AbortFlowException
is normal for the flow completing due to
first
finding a match.
first
is terminal (ends the flow normally) and returns the item that matched. So the return of the entire flow change, in this case, will be the
Advertisement
, which you can use to create a
Peripheral
and connect to.
e

Eric Boggs

11/24/2021, 3:34 PM
Got it, that makes sense. I think I was just surprised that AbortFlowException was coming back in cause and not being caught. I also kind of don't like that first has to throw an exception because it doesn't seem like an exception. I understand that's a decision made for flows and not this library. Thanks again for the help
6 Views