Android Architecture Lifecycle support?
Ghedeon opened this issue · 8 comments
Correct me if I'm wrong, but judging by the wiki, one has to override Activity/Fragment lifecycle methods in order to start/stop/disconnect mobius controller. This boilerplate code could be avoided with a Lifecycle
support. So far I ended up with a simple kotlin extension function:
class MobiusLifecycleObserver<MODEL, EVENT>(
lifecycle: Lifecycle,
private val controller: MobiusLoop.Controller<MODEL, EVENT>
) : LifecycleObserver {
init {
lifecycle.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun start() = controller.start()
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun stop() = controller.stop()
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun disconnect() = controller.disconnect()
}
fun <MODEL, EVENT>MobiusLoop.Controller<MODEL, EVENT>.bind(lifecycle: Lifecycle) {
MobiusLifecycleObserver(lifecycle, this)
}
that could be used like this:
controller.connect(...)
controller.bind(lifecycle)
It works like a charm but I'm wondering if a built-in framework support would be beneficial.
@Ghedeon yeah that might work! We're still in the process of defining how to interact with AAC since it can get a little tricky with lifecycle and dependencies. I imagine we'll have an extension at one point with AAC-Mobius utilities. For now we think overriding onResume
, onPause
and onDestroyView
doesn't really enough overhead to warrant lib support yet. But if you believe so, please do create an open source library for it.
Just seems to be a nice thing to have, considering last two Google I/Os and android documentation: https://developer.android.com/topic/libraries/architecture/lifecycle. As you can see, a very similar use-case, where they start with overriding activity lifecycle methods and work their way up towards independent lifecycle-aware component. Among other benefits listed there, easy testing is quite valuable, allowing you to trigger lifecycle events without creating heavy activity/fragment:
val lifecycle = LifecycleRegistry(mockk())
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
Definitely, not a deal-breaker, more like an extension as you rightly remarked.
I wouldn't recommend using resume/pause
for this, that means your app's mobius controller would stop if you click on another app in multi-window mode.
start/stop
is better for, well, starting and stopping. Unless you're executing fragment transaction.
@Zhuinden good point! But let's also consider another scenario. If we imagine that our model can be written to a bundle, and basically whenever our fragment is being killed for whatever reason, we save the current model to the bundle, if onSaveInstanceState
is called, and later restore it upon recreation and use that as our defaultModel
when creating a controller (i.e. we're doing a state restore). Since onSaveInstanceState
is called after onPause
, if we move the calls to onStop
, then it is possible that the model changes after we've already persisted it to the bundle. That means we could be restoring to an older state instead of the latest one.
Since onSaveInstanceState is called after onPause, if we move the calls to onStop, then it is possible that the model changes after we've already persisted it to the bundle.
Yeah that's a pain -_- now I'm starting to understand why they movedonSaveInstanceState
after onStop
if you target Android P
onSaveInstanceState(): if called, this method will occur after onStop() for applications targeting platforms starting with Build.VERSION_CODES.P. For applications targeting earlier platform versions this method will occur before onStop() and there are no guarantees about whether it will occur before or after onPause()
Talking about trickiness with AAC, take a look at this Post before using @Ghedeon solution.
When using the Navigation AAC, serving Fragment#getLifecycle
will cause multiple LifecycleObserver
registrations if you navigate to a new Fragment
and then the library Fragment#popBackStack()
. I believe this happens when the previous Fragment
gets its View
destroyed, but the Fragment
itself doesn't. The previous LifecycleObserver
doesn't get removed and, when bind
gets called again, a new LifecycleObserver
will be registered.
To solve this, you should use getViewLifecycleOwner().getLifecycle()
instead, as the article suggests.
#78 and friends should address most of these concerns, AFAICT. If you are interested in this feature, please take a look and let us know if it helps!