is a suspend function that polls a network API every 30 seconds to get data updates. They get put into a Room DB, and updates to the DB trigger updates to the ViewModel’s
.Because it’s doing network polling very often, I only want the polling to run while the user actually using the app, so that I’m not needlessly draining the user’s battery and data plan while the app is backgrounded, or otherwise not being used. When this screen was built using the old View system, I would launch a coroutine for the polling whenever the Fragment was in the RESUMED state, and cancel it in the PAUSED state. That ensures that I am only polling when absolutely necessary.The above code is how I implemented similar functionality with compose.
that you just attach to the host Activity/Fragment, and leave it out of the
/ rendering layers
10 months ago
Sure. No reason the composable above couldn’t adhere to both things mentioned by @jim. The
could definitely be an interface, and it already does the 2nd thing. The logic for the polling is encapsulated in a very top level composable for this Screen, and that composable only does only this one thing and then calls the “Content” composable which only receives state that it needs to draw itself and no view model at all.I agree that in general, decoupling the ViewModel and the composables that render content as much as possible is a great idea. But at very high levels (such as the composable that is called from the NavigationGraph when a particular route is given to the NavController and is the root of an entire Screen), there is a coupling of the ViewModel, which provides the state that is then handed down the tree to child composables that render the ui that makes up the screen.My question is more abstract really than ViewModels …First, I have some producer that produces state that is passed to a composable, and that producer is long running and resource intensive, so I want to make sure that it only runs while the composable is in the composition.
gives me that. I think that is the best way to handle that part of the problem, but I’m curious if there are other/better ways.Second, there are times when the composable is in the composition (so the LaunchedEffect is NOT canceled), but is not visible and as such, the producer should not be running. An example is when an app is put into the background. So, to handle that, I use the
to cancel/restart the producer when the Lifecycle exits/re-enters the RESUMED state. That works for that part of the problem, but I’m curious if there are other ways to do that as well.
10 months ago
You could, in the view model/state object, expose a
that suspends while the lifecycle is not resumed, and collect that in the Composable with
. But I prefer LaunchedEffect myself.
10 months ago
In general the lifecycle should be kept out of the viewmodel for the architecture to feature clear separations.
With your LaunchedEffect construction you are scoping the operations with two things: 1) Scoped to the composition via LaunchedEffect 2) Scoped to the Lifecycle between Resumed and Paused via repeatOnLifecycle
This seems like a well build architecture to me.
You might want to add lifecycleowner as second key for the LaunchedEffect though!