melatonina
06/07/2021, 11:17 PMStateFlow
is a good replacement for LiveData
on Android?
There is often a noticeable delay in updates, which leads to the UI to represent incorrect information when it first shows up.
That never happened to me with LiveData
.Tim Malseed
06/08/2021, 3:27 AMCoroutineScope.launch {
stateFlowA.collect {
foo()
}
stateFlowB.collect {
bar()
}
}
And wondering why bar() isn't called. (It's because stateFlowA never completes, as it's a StateFlow).
I think I'm just tempted to declare both suspend functions inside the one CoroutineScope.launch { }
block out of convenience.
Anyway, the simple solution is to just not make this mistake. But I wonder if I'm doing this because there's a problem with my mental model. I think maybe this comes about because launch{}
is the only operator I ever use to kick-off a suspend fun (I rarely consider async
, and I'm not sure if there are others.
I don't have a specific question, just wanted to discuss this and see if anyone else has the same problem. I'm hoping the discussion will help correct my mental model.Sudhir Singh Khanger
06/08/2021, 4:37 AMRoom will use different Dispatchers for transactions and queries. These are derived from the executors you provide when building your Database or by default will use the Architecture Components IO executor. This is the same executor that would be used by LiveData to do background work.Does that mean if I use
withContext(<http://Dispatchers.IO|Dispatchers.IO>) { //db calls }
then likelihood is that Room merges this with whatever Architecture Components IO executor
it uses internally? Or would <http://Dispatchers.IO|Dispatchers.IO>
overwrite Room's default dispatcher. As far as I understand Default
means main
in Android.Tim Malseed
06/08/2021, 4:40 AMStateFlows
the second depends on the first. But, the second is not emitting when I expect it to..james
06/08/2021, 6:31 AMHenning B
06/08/2021, 1:14 PMchristophsturm
06/08/2021, 1:18 PMsuspend fun main() {
val executorService = Executors.newWorkStealingPool(1000)
withContext(executorService.asCoroutineDispatcher()) {
repeat(1000) {
async {
delay(1000)
}
}
}
println(executorService)
}
at the end the executor service has one task left:
java.util.concurrent.ForkJoinPool@5079ec1[Running, parallelism = 1000, size = 70, active = 1, running = 1, steals = 2000, tasks = 0, submissions = 0]
why is that. if i do runBlocking instead of withContext the executor service is empty as expected.Pablo
06/08/2021, 1:23 PMflow
that inside does an async{}
call?
For instance
flow<List<Foo>>{
val list = getFromApi()
val one = async(<http://Dispatchers.IO|Dispatchers.IO>)
val two = async(<http://Dispatchers.IO|Dispatchers.IO>)
val correctList = one.await() + two.await()
emit(correctList)
}
Or it's better to use async without flow here?
That's pseudo because to do async
I need a coroutineScope
is there any way to get it from the flow
?stojan
06/08/2021, 3:08 PMRecyclerView.ViewHolder
(I need to load an image, and offload that to a background thread)
Not sure if this channel, or #android is better....ursus
06/09/2021, 4:44 AMdkim
06/09/2021, 1:43 PM1.6.0
Kotlin: 1.5.10
(JVM)
All dependencies: https://github.com/dkim19375/Tag/blob/master/build.gradle
Server log: https://paste.helpch.at/eruzihulol.sql
Client log: https://paste.helpch.at/bedemiyihi.sql
Note that for both logs I'm using TornadoFX, and in my test class, I'm not using TornadoFX, and this is the log:
program
test 1, is active: true
test 2, is active: true
Is this an issue towards what I'm doing with ktor or is it something with coroutines? (I'm not sure so I'm asking both channels)
EDIT: Figured it outPaul Woitaschek
06/10/2021, 8:15 AMrtsketo
06/10/2021, 9:54 AMval jobs = arrayListOf<Job>()
aListOfStuff.forEach {
jobs.add(launch { something(it) })
}
jobs.joinAll()
println("All jobs completed!")
Recently I discovered I can do something similar with supervisorScope
, like so:
supervisorScope {
aListOfStuff.forEach {
launch { something(it) }
}
}
println("All jobs completed!")
Does it completely mimics the behavior I intented the first part to have though?
Are there be any mihaps I should be aware of?Slackbot
06/10/2021, 11:49 AMMark Allanson
06/10/2021, 3:38 PMkevin.cianfarini
06/10/2021, 7:43 PMexpected: Thread[test,5,main]
but was : Thread[main @coroutine#1,5,main]
Kirill Vasilenko
06/11/2021, 8:46 AMStateFlow
is a really convenient thing for implementing reactive view models, but there is a big problem - when you combine them (with combine(...)
or map(...)
operators) they turn into Flow
that is not so convenient.
val firstField: StateFlow<String>
val secondField: StateFlow<Int>
val canDoSomethingScary: Flow<Boolean> = combine(firstField, secondField) { first, second ->
first.isNotBlank() && second > 0 && second < 10
}
// method has to be suspended :(
suspend fun doSomethingScary1() {
// had to have coroutineScope here :(
if (!canDoSomethingScary.stateIn(coroutineScope).value) return
// do something scary
}
There is a proposal on GitHub about having an opportunity to combine StateFlows
and get StateFlow
. There are also examples why getting Flow
is not convenient and working code snippets with the solution, using which one will be able to write much clearer code and keep their view models at the multiplatform level right now. Like this
val firstField: StateFlow<String>
val secondField: StateFlow<Int>
val canDoSomethingScary: StateFlow<Boolean> = combineStates(firstField, secondField) { first, second ->
first.isNotBlank() && second > 0 && second < 10
}
// method doesn't have to be suspended :)
fun doSomethingScary() {
if (!canDoSomethingScary.value) return
// do something scary
}
Please vote and discuss the proposal there to induce the Kotlin team to include it (or a better solution) in the kotlinx.coroutines
in the following releases.
https://github.com/Kotlin/kotlinx.coroutines/issues/2631Mark Allanson
06/11/2021, 3:45 PMDeferred
inside a supervisorJob
continues to report as active until all the co-routines are complete? I was expecting the child jobs move to a completed state once they themselves finished, instead of only transitioning to completed when all have done so.Karthik Periasami
06/11/2021, 6:29 PMinterface RestApi {
fun get()
fun post()
fun put()
fun delete()
}
interface NewRestApi {
suspend fun get()
suspend fun post()
suspend fun put()
suspend fun delete()
}
I am adding a feature in my rest client library to support suspend functions. What is the recommended prefix for suspend method names? I need something to differentiate between the non-suspend functions, to avoid misunderstanding. Any ideas are welcome, I suck at naming.Scott Kruse
06/11/2021, 7:01 PMstateflow
never completes, so by this reasoning, we should never start collecting on a stateflow from within suspend
function correct?taponidhi
06/13/2021, 4:32 AMursus
06/13/2021, 9:47 AMclass PermissionManager() {
private val manualTrigger = MutableSharedFlow<Unit>()
...
fun notifyPermissionChanged() {
runBlocking { <---------------------
manualTrigger.emit(Unit)
}
}
}
class Activity {
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
permissionManager.notifyPermissionChanged()
}
}
tryEmit never does anythingVikas Singh
06/14/2021, 10:56 AMTiago Nunes
06/14/2021, 2:12 PMList<Flow<T>>
to a Flow<List<T>>
? Sounds simple enough but it's bugging my brain 😅ursus
06/14/2021, 7:49 PMtrySend
vs trySendBlocking
in callbackFlow
. I presume it's about backpressure right? If so, to mimic rxjava Observable, I want trySendBlocking
right?William Reed
06/14/2021, 8:15 PMdiego-gomez-olvera
06/15/2021, 9:06 AMFlow
API a colleague noticed that there are several operators (e.g. mapLatest
) still experimental. What would be your recommendation about those? Is it safe to adopt them? Is there typically a migration supported in case of deprecation?myanmarking
06/15/2021, 10:48 AMOsmium
06/15/2021, 6:55 PMlaunch
methods so coroutine hierarchy will be structured.
But. How to deal with callbacks?
fun main() {
GlobalScope.launch {
launch {
root("main") { div("Hello KVision!") }
}
launch {
root("extras") {
button {...}.onClick {
this@launch.launch {
ktorClient.get("/api/users/current") {...} // will not be called
}
}
}
}
}
}
Should I use GlobalScope in case of event listeners like onClick and so on?Tower Guidev2
06/16/2021, 8:13 AMmerge(flow1, flow2, flow3...)
and have the following use case...Tower Guidev2
06/16/2021, 8:13 AMmerge(flow1, flow2, flow3...)
and have the following use case...sealed class State {
object Resting : State()
object Working : State()
}
fun main() = runBlocking {
backgroundStartsFirst()
}
@OptIn(ExperimentalCoroutinesApi::class)
suspend fun backgroundStartsFirst() {
val backgroundFlow = flow {
emit(State.Resting)
delay(1000)
emit(State.Working)
delay(10000)
emit(State.Resting)
}
val foregroundFlow = flow {
emit(State.Resting)
delay(2000)
emit(State.Working)
delay(1000)
emit(State.Resting)
}
val workingCount = AtomicInteger(0)
merge(backgroundFlow, foregroundFlow)
.collect {
when (it) {
State.Working -> {
println("SOME ARE WORKING")
workingCount.incrementAndGet()
}
State.Resting -> {
println("SOME ARE RESTING")
if (workingCount.get() > 0) workingCount.decrementAndGet()
}
}
if (workingCount.get() == 0) println("\tWE ARE ALL RESTING")
}
}
Resting
or Working
.
Each flow is completely independent of all other flows.
When atleast one flows latest "report" was Working
my merged state must be Working
...
Once all Flows latest report is Resting
my merged state must be Resting
Currently I have employed a WorkingCount to establish whether or not there is a Working
Flow.
I feel there must be a better solution than employing an AtomicCounter
Is There?
🤔wasyl
06/16/2021, 8:49 AMcombine
to get a new emission every time one of the combined flows emits:
val isRestingFlow = combine(flow1, flow2) { v1, v2 -> v1 is Resting && v2 is Resting }
Tower Guidev2
06/16/2021, 9:07 AMcombine
is a clean solution, thanks