Four Lifecycles
A bloc has four relevant lifecycles:
- The
Viewlifecycle which is a platform-specific lifecycle. - The Essenty lifecycle which translates the
Viewlifecycle to a platform-agnostic lifecycle. - There's typically a platform specific lifecycle between the
Viewand the Essenty lifecycle. On Android that would normally be aViewModellifecycle. On iOS there's aBlocHolderorBlocComponentlifecycle. This and how it ties to the other two lifecycles will be discussed in more detail in Extensions. - The bloc has a lifecycle of its own. It's "driven" by the Essenty lifecycle but adds states to manage its internal workings (see Bloc Lifecycle).
From a Bloc's perspective, the Essenty lifecycle is the most relevant one.
Essenty Lifecycle
onCreate()
onCreate() is called when the component controlling the lifecycle is created. If there's an Initializer, the bloc will start executing it in its own CoroutineScope.
onStart()
onStart() is called when the component controlling the lifecycle is started. That's typically when the UI is being displayed / rendered (platform specific meaning). This is the moment the bloc starts processing actions for thunks and reducers. The workers that process the two queues / channels are started within their own CoroutineScope.
onStart() doesn't move the bloc to its Started state if the initializer hasn't finished yet (see Bloc Lifecycle)
onResume() / onPause()
These two events are ignored by the bloc.
onStop()
onStop() is called when the component controlling the lifecycle is stopped. That's typically when the UI stops showing (platform specific meaning). When this happens, the bloc stops processing actions for thunks and reducers. The CoroutineScopes created for the workers that process the two queues / channels are cancelled (and thus will all started coroutines).
After onStop() all actions sent to the bloc will be ignored. If the bloc is started again (onStart()), new actions will be processed but the ones that were sent in Stopped state are gone.
onDestroy()
When the component controlling the lifecycle is destroyed, the Bloc will cancel the
Initializer CoroutineScope. If the initializer is still running, it would be stopped (all coroutines are cancelled). Once a bloc is destroyed, it's can't be used any more (onCreate will be ignored).
Bloc Lifecycle
The Bloc lifecycle is driven by the Essenty lifecycle on one hand and by the logic to synchronize initializer and reducer/thunk execution on the other hand. As explained here initializers need to finish execution before the bloc transitions to the Started state (which will start the processing of thunks and reducers).
The Bloc lifecycle is driven by the following state machine:
For Kotlin Bloc users this isn't really relevant. Important to remember is only the fact that the order of execution is guaranteed. An initializer will always run and terminate before thunks and reducers are executed, regardless of the Essenty lifecycle. Actions (and thunks/reducers in MVVM+ style) dispatched to the bloc after Essenty's lifecycle onStart() will be sent to a queue and processed once the initializer is done (in the same order they were sent).
CoroutineScopes
A bloc has three CoroutineScopes which are tied to the lifecycle of the bloc as follows:
| Lifecycle Event | CoroutineScope | Operation |
|---|---|---|
| onCreate() | InitializerScope | create scope -> start initializer |
| onStart() | ThunkScope | create scope -> start processing thunk queue (if initializer is done) |
| ReduceScope | create scope -> start processing reducer queue (if initializer is done) | |
| onStop() | ThunkScope | cancel scope -> stop thunk coroutines |
| ReduceScope | cancel scope -> stop reduce coroutines | |
| onDestroy() | InitializerScope | cancel scope -> stop initializer coroutines |