https://kotlinlang.org logo
#android
Title
# android
s

Simon Buechner

11/24/2023, 2:07 PM
Hi, i hope i ask in the right channel. I have a sharedflow from a KMP Lib that gets emitted pretty often (400hz), i want to display the data in a text composable from jetpack compose in an Android App. On iOS the data is updated in 400Hz but in my Android App the data seems laggy and like it skips some of the data from the flow. I just have a viewmodel that passes the flow to the view and then i collect it as state and pass the data to the text composable. Is this a wrong approach or are those boundaries from android or Jetpack Compose? Has anyone experience with this?
g

gildor

11/28/2023, 1:15 PM
There is no point updating the UI 400 times per second, you cannot do it more often than the amount of frames per second (60 as baseline, or by requesting frame rate of particular device)
And yes, SharedFlow will drop data (conflate) if client cannot consume it fast enough, you can use non shared flow, but it will require to have buffer for events (there is operator for this) or suspend the consumer, none of which looks as what you need for your case
Updating something every frame with compose is possible, but you really should carefully check that you recompose oy specific element and do not have other bottlenecks
s

Simon Buechner

11/28/2023, 2:31 PM
Ah okay, that explains a lot. If i just buffer the sharedFlow data and don't display it directly, i should be able to display the data in a chart that runs more or less fluent without discarded data packages. @gildor Thanks for your help
g

gildor

11/29/2023, 2:54 AM
Also, just updating string so often may be a bottleneck itself. Each new string allocate an object (not mentioning other stuff which happening) on slower devices, it may cause visible lag by itself becayse if GC pressure We have code which works with millisecond precision and require to be updated every frame and we spent quite a lot of time to make it work properly with minimal allocations to reduce potential slowdonws and GC pauses. In old times, before Flow, we had another project with the same requirements and there we used just released coroutines Channels, instead of our usual RxJava as it worked on the main project, and we found that the broadcast channel does a lot of allocations, as I remember, something like 8 objects per emit (because it's thread-safe and supports multiple emitters and multiple observers), so we have to get rid of all channels in this area and just replace them with reusable callback to avoid performance hit It's shouldn't be a problem with SharedFlow by itself, just remember that it conflates value, so you cannot rely that you get all the updates (and for places like UI you usually shouldn't rely on this too, and only use latest value)
> i should be able to display the data in a chart that runs more or less fluent without discarded data packages For me it looks like something what should be separated between "updating data set" and "notify UI to redraw with current data set" Because buffer allows do not to lose events (but you paying memory usage for it), but do you really need to collect it on level of Flow instead of just updating some storage and notify UI to redraw, probably updating something 400 times per second is still could be performance crticial, so it's better to see how it can be optimized, especially when it related to UI, where even updating 60 times per second can be a challenge and require checking performance
Hi, i hope i ask in the right channel
And just in case, #coroutines is probably a better place, but nothing wrong to ask here, after all it depends on Android too
s

Simon Buechner

11/29/2023, 6:14 AM
@gildor thanks for the detailed explanation 😃 the problem is, it’s a realtime chart to display sensor data changes in at least a few seconds range. So I need to save 400-500 Samples in an array and just rewrite the index. Haven’t found a good chart lib for compose yet that handles fast redrawing. I will then just wait for 20 packages and then I will trigger the redraw of the chart if possible somehow.
g

gildor

11/29/2023, 8:26 AM
Realtime chart drawing may be challenging indeed, it's just that this real-time should be synced with frame rate, so there is no way to redraw it 400 times per second anyway Essentially you should decide how often you would like to redraw (every frame or more rarely) and just get latest version of data As I see, your sensor data should be just written to an array to collect and separate flow which emits new chart data every frame (or every few frames, it really may be up to you), so what you suggesting
Haven’t found a good chart lib for compose yet that handles fast redrawing
I believe many chart libraries just not optimized for this use case, it may require custom drawing to be really efficient (this what we do for our use case, to show audio waveform)
But another solution just make it less realitime, redraw let's say every 10th frame, depending on your requirements and UX
s

Simon Buechner

11/29/2023, 8:38 AM
@gildor thanks again for the nice insights. So i have to update as example the data the chart is watching on (array of Pairs i think) every 15 milliseconds, this would trigger a rewdraw with the new array no with index 0 to 39 with new elements, then i collect new values in the next 15 milliseconds and then i update the array index again with the next 40 packages. Then index 0-40 are the newest samples and the other 40 samples are pushed to the index 40 to 80. would this an approach that would trigger it less often work, i mean from the concept, or are there any better ways to do this. I am already very thankful for your help so feel free to say i should figure it out on my one 🙂 but i am really interested in your thoughts on this matter.
👍 1
g

gildor

11/30/2023, 3:03 AM
> array of Pairs In case of realtime, you may consider using a single array or two arrays, so it will not allocate pair on every item and do not create GC pressure But your approach looks fine, there are other solutions like sync it explicitly with drawing frames, so do not notify it from outside, but instead use LaunchedEffect and awaitFrame() to actually wait for frame to pull data, usually it used for animations, but maybe useful here too
s

Simon Buechner

11/30/2023, 6:15 AM
Okay thank you @gildor I think I will try both approaches, never used awaitFrame but explicitly triggering the update seems like a good way to maximise the chart updates while staying inside of the device frame rate. I think I learned a lot from your help, thanks a lot. :)
👍 1