louiscad
08/25/2021, 12:23 PMComposeView
despite the State
objects being mutated from the class holding them?BenjO
08/25/2021, 12:26 PMBenjO
08/25/2021, 12:28 PMlouiscad
08/25/2021, 12:29 PMComposeView
was in Visibility.GONE
, but even after changing it, it's visible, but the UI keeps the render of the initial composition, and further compositions don't happen when the State
s are mutated.
I'm a bit surprised because I already use ComposeView
successfully in other parts of the project, in a very similar way. And I'm not using the @Stable
annotation.BenjO
08/25/2021, 12:37 PMThisAre you in this case ?requires that the window it is attached to contains aandroid.view.View
.ViewTreeLifecycleOwner
louiscad
08/25/2021, 12:39 PMContext
is an AppCompatActivity
.louiscad
08/25/2021, 12:40 PMBenjO
08/25/2021, 12:41 PMAlbert Chang
08/25/2021, 1:23 PMlouiscad
08/25/2021, 1:24 PMlouiscad
08/25/2021, 1:25 PMlouiscad
08/25/2021, 1:54 PMlouiscad
08/25/2021, 1:56 PMproduceState
that loops updates a state that will display something, to see if that at least gets updated.louiscad
08/25/2021, 2:11 PMText
to display its value:
val iterationsState = produceState(0) {
value = 100
repeatWhileActive {
value = 1000
delay(500)
value = value + 10
}
}
And I noticed that when the app cold starts, it briefly shows 1000
before updating to 0
. Subsequent Activity starts lead to 0
being displayed straightaway.
It looks like something is resetting composition and freezes all future compositions.louiscad
08/25/2021, 3:54 PMsomeValueFlow
, and only in one of the 3 ComposeView
I see the value being updated correctly.
private val fileScope = MainScope()
val someValueFlow: StateFlow<Int> = MutableStateFlow(0).also {
fileScope.launch {
while (true) {
delay(500)
it.value++
}
}
}
I use collectAsState()
in all cases, and in a new project it works, but in company's project, only in one of the 3 views, and I have no clue why. The one working was already there, the 2 others are new ones I added, just like in the brand new project.
It seems like there's some shared mutable state under the hood of the Compose runtime that puts itself in an inconsistent state.
I hope you're not mad at me pinging you 🙏🏼Adam Powell
08/25/2021, 4:11 PMAdam Powell
08/25/2021, 4:11 PMlouiscad
08/25/2021, 4:13 PMAdam Powell
08/25/2021, 4:18 PMAdam Powell
08/25/2021, 4:21 PMSnapshot.sendApplyNotifications
is getting called as expected, and in the Recomposer's snapshot apply observer that schedules recompositionsAdam Powell
08/25/2021, 4:22 PMAdam Powell
08/25/2021, 4:23 PMlouiscad
08/26/2021, 7:54 AMlouiscad
08/26/2021, 8:09 AMContinuation
somewhere.
Job
inside (unlike GlobalScope
or a custom implementation of the interface) that would keep a strong reference to the child coroutines, this scope itself has no strong reference pointing to it, so almost the entire object graph is orphan for the GC (garbage collector). I still don't understand how it can work for a while or forever in some cases though.louiscad
08/26/2021, 8:52 AMActivity
, in a way that would not let the coroutine be garbage collected, but it didn't fix the end issue of the Composable not updating.
class TheActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// ... Other code
lifecycleScope.launch {
val channel = Channel<Unit>(Channel.CONFLATED)
launch(AndroidUiDispatcher.Main) {
channel.consumeEach {
Snapshot.sendApplyNotifications()
}
}
Snapshot.registerGlobalWriteObserver {
channel.trySend(Unit)
}
}
}
// ... Other code
}
louiscad
08/26/2021, 9:33 AMsetContent
function in ComposeView
is not idempotent.
In my project, if I call it after some delay, everything works as expected.
However, if I call it at the earliest time possible, so as I'm creating the View hierarchy (I do this programmatically with Splitties Views DSL BTW), recomposition never happens.
That means I have a workaround, but I'm not sure yet if I'll succeed in making a reproducer that is not my company's project.louiscad
08/26/2021, 10:14 AMActivity
, I'm calling setContentView
twice with the same view hierarchy instance, expecting that function to be idempotent, and it is, in the Android platform.
However, I see that the ComponentActivity
from AndroidX calls the initViewTreeOwners()
function everytime (no conditions), and the name of that function having "init" in it likely means that it wasn't designed to be idempotent, breaking the contract set by the Android platform.
Whenever the view hierarchy that includes the composables is passed a second time to setContentView
, in another dispatch loop (calling twice in a row doesn't break anything), the composables stop updating, and there's no going back.louiscad
08/26/2021, 10:36 AMlouiscad
08/26/2021, 10:58 AM