xenomachina
03/05/2021, 11:45 PMReceiveChannel.isClosedForReceive
to graduate from being "experimental"?Abhishek Dewan
03/06/2021, 7:19 AMRob
03/06/2021, 6:44 PMursus
03/07/2021, 4:23 PMDispatcher.Main
ViewModel has state, and therefore State (MutableStateFlow<State>
) should only be set on main thread -- to avoid synchronization
Is there a way I can assert Scope is on main thread without an android reference? (Looper.myLooper() != Looper.getMainLooper()
)
(To keep my viewmodels KMP ready)
Or, should I just not care and read-write (reduce) to StateFlow within a synchronization block?Marcin Wisniowski
03/07/2021, 10:38 PMChannel
size (without consuming the contents)? I'm currently counting send()
and receive()
calls manually.Arun
03/08/2021, 9:25 AMtakahirom
03/08/2021, 10:46 AMA good practice is to usehttps://developer.android.com/kotlin/coroutines/coroutines-adv#main-safetyto make sure every function is main-safe, which means that you can call the function from the main thread.withContext()
Andrew Ebling
03/08/2021, 11:51 AMReceiveChanel
every 10ms, representing the state of some buttons on an external controller.
When I call a method, I need to wait until I see 10 no button press events, then 10 button press events of a given type in a row, followed by 10 no button press events, then call a supplied completion handler.
So far I’ve got as far as this:
fun readButtonPress(press: ButtonPress, completion: (ButtonPress) -> (Unit)) {
launch {
eventReceiveChannel
.consumeAsFlow()
// wait for:
// at least 10x no press events
// 10x or more press events
// at least 10x no press events
.consume {
completion(buttonPress)
}
}
}
My questions are therefore:
1. am I on the right track? I want to contain coroutine use to this class, as it needs to be called from a callback-based API
2. what is the correct way to track state in this scenario (i.e. number of events received in a row of a given type)?
3. what are the correct flow methods to use to achieve this goal?
I think I’ve just about got my head around coroutines conceptuals and beginning to get my head around Flow, but my brain is blowing a fuse trying to figure this out.Timo Drick
03/08/2021, 4:54 PMval job1 = launch {
try {
withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
SystemClock.sleep(1000)
throw IOException("Wurst")
}
} catch (err: Throwable) {
Log.d("Test", "code executed")
}
}
launch {
delay(500)
job1.cancel()
}
In this code the Log.d line will be executed. Which is bad when e.g.: i want to show a error message after a network call failed but the fragment coroutine scope is already cancelled.
Is there any ellegant solution to avoid executing the catch when corotuine scope is already cancelled?Andrew Ebling
03/09/2021, 9:48 AMkotlinx.coroutines/kotlinx-coroutines-core/common/test/TestBase.common.kt
.
Is it possible to re-use TestBase()
my own tests? I’ve tried import kotlin.test.*
however this package is not found, despite having the following in my Android app `build.gradle`:
implementation ‘org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3’
I’ve also tried adding a testImplementation
line with the same, but the above import still fails.
Is re-using TestBase()
etc. in my code not possible? Or is their a separate testImplementation
entry I should add to my app build.gradle
?Trần Đại Nhân (Max)
03/10/2021, 3:10 AMAndrew Ebling
03/10/2021, 10:06 AMcollect { }
completes? I’d like to do something like this, but the job val is not visible:
val eventJob = launch {
myChannel
.consumeAsFlow()
.filterAllButFinalEvent()
.collect { event ->
doSomething()
eventJob.cancel() // doesn’t compile
}
}
Ivan Pavlov
03/10/2021, 2:45 PMval sf1 = MutableStateFlow(listOf(1))
val sf2 = MutableStateFlow(listOf(-1))
sf1.value = listOf(1, 2)
sf2.value = listOf(-1, -2)
sf1.value = listOf(1, 2, 3)
I combine them like
sf1.combine(sf2) { a, b -> a + b }
.stateIn(scope, SharingStarted.Eagerly, initialValue = listOf())
.onEach { println(it) }
.launchIn(scope)
Then I don't have [1, -1] list in combined flow. I changed initialValue to sf1.value + sf2.value and now it works but I think that I could miss something which simplifies this. Is there a better way?xenomachina
03/10/2021, 6:22 PMzak.taccardi
03/10/2021, 8:59 PMlog.verbose("awaiting for ${request.uri}")
hasTokenStates
.first { hasToken -> hasToken } // suspending
log.verbose("awaiting complete for ${request.uri}")
I want to only log "awaiting for ${request.uri}"
if the first()
suspends for more than `1_000`msGilles Barbier
03/11/2021, 10:20 AMrunBlocking {
launch {
println("a1")
runBlocking { delay(100) }
println("b1")
}
launch {
println("a2")
runBlocking { delay(300) }
println("b2")
}
}
displays a1 a2 b2 b1 - instead of a1 a2 b1 b2Gilles Barbier
03/11/2021, 10:21 AMJavier
03/11/2021, 11:33 AM1.4.3
and 1.4.3-native-mt
are only in the native artifacts, in other words, jvm and android artifacts are the same?Mateusz Krawczuk
03/11/2021, 3:34 PMxenomachina
03/11/2021, 4:38 PMmuliyul
03/11/2021, 5:07 PMrunBlocking {
...
collection.asFlow().flowOn(<http://Dispatchers.IO|Dispatchers.IO>).fold(0) { acc, next ->
acc + calculate(next)
}
}
Does it matter if the calculate
function is suspending or not in terms of performance?xenomachina
03/11/2021, 5:18 PMAbhishek Dewan
03/12/2021, 5:39 AM@ExperimentalTime
internal class AuthenticationRepositoryImpl(
private val authenticationApi: AuthenticationApi,
private val authenticationQueries: AuthenticationQueries
) : AuthenticationRepository {
override suspend fun authenticateUser() {
authenticationQueries.clearAuthenticationData()
authenticationApi.authenticateUser().toAuthentication().apply {
authenticationQueries.setAuthenticationData(accessToken, expiresBy)
}
}
@ExperimentalTime
override suspend fun getAuthenticationData(): Flow<Authentication?> {
val timeNow = Clock.System.now().epochSeconds
return authenticationQueries.getAuthenticationData(timeNow).asFlow().mapToOneOrNull()
}
}
and I am calling the functions from my android app like so:
fun checkAuthentication() {
viewModelScope.launch {
authenticationRepository.getAuthenticationData().onEach {
if (it != null) {
Timber.d("Valid Authentication Found")
isAuthenticationValid.value = true
} else {
Timber.d("Valid Authentication Not Found - Requesting Authentication")
authenticationRepository.authenticateUser()
}
}.collect()
}
}
In the following code if the authentication data is not present in the SqlDelight DB, I get a null in the onEach of the flow and then I trigger a fetch and save to DB. However, the save to DB does not re-trigger a flow update. Is there something I am doing wrong here?uli
03/12/2021, 10:43 AMrunBlocking
, launched on the same single threaded dispatcher do not block each other. They share an event queue.
2. former runBlocking
will only return after later runBLocking
completes
(1) comes as a positive surprise
(2) is totaly unexpected and I’d consider it a bug
I’ll follow up in a thread with the demo codeJason5lee
03/12/2021, 12:33 PMorg.reactivestreams.Publisher
in coroutine?james
03/12/2021, 1:43 PMprivate fun loadData() = with(presenterScope) {
listOf(
async {
metadata = transferOtherBank.getMetadata()
},
async {
sources = transferOtherBank.getSourceList()
},
async {
favorites = transferOtherBank.getFavorites()
},
async {
fee = transferOtherBank.getTransactionFee()
},
async {
banks = transferOtherBank.getBanks()
}
)
}
where presenterScope is Coroutine scope provided by moxy2 library (I can attach implementation if it’s necessary)
presenterScope.launch {
loadData().awaitAll()
setName(favorites, banks)
Timber.d("ASSSAS after await")
}
Once some of async fails (getFavorites for example) setName(…) function is never called.
How can I ensure calling of setName(…) despite any error in any async?
Thanks!kevin.cianfarini
03/12/2021, 2:34 PMNikola Milovic
03/13/2021, 7:32 AMif (edgesUri.getCompleted() == null
null checks and the labelUri.getCompleted()!!.path!!
. Any way to improve this?
suspend fun saveImageToDB(networkImageModel: CBImageNetworkModel): Result<Long> {
return withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
try {
val edgesUri = this.async {
val urlEdges = URL(networkImageModel.edgesImageUrl)
return@async saveBitmapToAppStorage(urlEdges.openStream(), ImageType.EDGES)
}
val finalUri = this.async {
val urlFinal = URL(networkImageModel.finalImageUrl)
return@async saveBitmapToAppStorage(urlFinal.openStream(), ImageType.FINAL)
}
val labelUri = this.async {
val urlLabels = URL(networkImageModel.labelsImageUrl)
return@async saveBitmapToAppStorage(urlLabels.openStream(), ImageType.LABELS)
}
awaitAll(edgesUri, finalUri, labelUri)
if (edgesUri.getCompleted() == null || finalUri.getCompleted() == null || labelUri.getCompleted() == null) {
Result.failure<Long>(Exception("An image couldn't be saved"))
}
val image = CBImageDataModel(
labelsImageUri = labelUri.getCompleted()!!.path!!,
finalImageUri = finalUri.getCompleted()!!.path!!,
edgesImageUri = edgesUri.getCompleted()!!.path!!,
)
Result.success(db.imageDao().insertImage(image))
} catch (e: Exception) {
Timber.e(e)
Result.failure(e)
}
}
leandro
03/13/2021, 10:53 AMsuspendCancellableCoroutine
, do I need to cleanup the resources inside the error callback?
suspend fun isSessionValid(): Boolean = suspendCancellableCoroutine { continuation ->
lateinit var auth: Auth
val callback = object : AuthHandler {
override fun onSuccess(session: AuthUserSession) {
continuation.resume(session.isValid)
auth.release()
}
override fun onFailure(e: Exception) {
continuation.resumeWithException(e)
}
}
auth = builder.setAuthHandler(callback).build()
auth.getSession()
continuation.invokeOnCancellation { auth.release() }
}
Marcin Wisniowski
03/13/2021, 11:18 PMChannel
, whether it's 0 or more, not suspending either way?Marcin Wisniowski
03/13/2021, 11:18 PMChannel
, whether it's 0 or more, not suspending either way?Adam Powell
03/14/2021, 12:16 AM.poll()
will do the trick so long as the channel element type isn't nullable