What is the reason, that the lifecycle of a child ...
# decompose
b
What is the reason, that the lifecycle of a child component must not be destroyed? https://arkivanov.github.io/Decompose/component/child-components/ Our use case: We have an infinite list (messenger timeline), where elements are created and removed when scrolling up and down. Each element view model uses a childContext of the timeline. We pass a lifecycle to destory it, when the element is remove from the list. Now we get
The lifecycle of a child ComponentContext must never be destroyed manually.
a
The reason is because the
ComponentContext
created via
childContext
is attached to the parent
ComponentContext
, i.e. it saves/restores the state, keeps its retained
InstanceKeeper.Instance
, handles back button presses, etc. If you need to dynamically switch components, then most likely you need navigation.
If you just need a lifecycle per item, then you can try using
LifecycleRegistry
manually.
b
Thank you for the response. But what is the problem with destoying a child and therefore remove it's instance from the InstanceKeeper? Using navigation feels a bit overcomplicated for a dynamic list.
a
That's not it. There are many details that the developer would have to take care of. E.g. recreate the exact same set of components on Android configuration change or after process death to avoid memory leaks, which means they should store this information somewhere, which is error prone and this is what navigation is doing. You can try it yourself, just copy-paste the childContext extension function and MergedLifecycle into your project, remove everything unnecessary for your use case and allow destroying.
b
Using navigation is way too complicated for us. We are using an SDK, that is managing an infinte list of elements for us. We map these elements to view models and are starting or destroying a lifecycle when it is added or removed from the list. copy-paste childContext does not really work as most of the used functions are internal. We are now using
DefaultComponentContext(MergedLifecycle(this.lifecycle, lifecycle))
but this is only a workaround. I still don't see, why it is not allowed to destroy the lifecycle of a child. Is there any technichal limitation?
a
Well, that was a design decision back two years ago, because people were having memory leaks when destroying lifecycles without taking extra care. If you have a set of currently created components, and then a configuration change or process death occurs, then the exact same set of components must be recreated as well. There are also some other details I don't remember nowadays. The navigation is there for a reason and it's not just switching lifecycles. Instead of managing a list of lifecycles you could just use a navigation model. Doesn't sound like a big difference and it would be much safer. Or actually copy-paste childContext and all its internals (not too much actually) and adapt for your needs.
b
But what exactly would be the memory leak? As far as I undestand the code in decompose: when I destory the lifecycle of a child context, all it's references are removed from the parent. In fact: not being able to destroy the lifecycle (and therefore removing references) would create memory leaks for us. Using Navigation just feels wrong and way too complicated as we don't really navigate. We are just rendering a list. Is using
DefaultComponentContext(MergedLifecycle(this.lifecycle, lifecycle))
okay instead of copying many internal functions of decompose?
a
If there is a retained instance or a saved state in a child component, and the component wasn't recreated after a configuration change or process death, that retained instance or save state will keep hanging in memory. Moreover, if you later create a component with the same key again, that state and/or retained instance will be picked up by the component, which might be unexpected. This function is just not for your use case. It's for so-called permanent components by design.
b
Okay, I think I understand it now. When the parent is re-recreated, it may still have a reference to a child that may not exist in the underlying list anymore. So the problem is not that
destroy
creates a memory leak, but how the child is (or is not) created, because it is not permanent. With that in mind it seems to be okay to call
DefaultComponentContext(MergedLifecycle(this.lifecycle, lifecycle))
for each element in the list as it is either manually destoyed or removed by the garbage collector, because no one holds a reference to it.
a
With that in mind it seems to be okay to call
DefaultComponentContext(MergedLifecycle(this.lifecycle, lifecycle))
for each element in the list as it is either manually destoyed or removed by the garbage collector, because no one holds a reference to it.
Yes, though only the
Lifecycle
from the created
DefaultComponentContext
will be functional. The reset won't work, which may also affect nested navigation if needed.
And this leads to a conclusion that you can just pass a
Lifecycle
instead of the
ComponentContext
.
b
We are using a wrapper around ComponentContext for our view models, so lifecycle only is no option. Thank you for your help! Decompose helped us very much to create this Open Source Kotlin Multiplatform Matrix messenger: https://tammy.connect2x.de/
a
Thank you for the feedback! Feel free to add your project to the used-by list: https://github.com/arkivanov/Decompose/discussions/366