After a cancellation, if the LiveData becomes active again, the block will be re-executed
from the beginning. If you would like to continue the operations based on where it was stopped
last, you can use the LiveDataScope.latestValue function to get the last
LiveDataScope.emited value.

If the block completes successfully or is cancelled due to reasons other than LiveData
becoming inactive, it will not be re-executed even after LiveData goes through active
inactive cycle.

As a best practice, it is important for the block to cooperate in cancellation. See kotlin
coroutines documentation for details
https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html.

After a cancellation, if the LiveData becomes active again, the block will be re-executed
from the beginning. If you would like to continue the operations based on where it was stopped
last, you can use the LiveDataScope.latestValue function to get the last
LiveDataScope.emited value.

If the block completes successfully or is cancelled due to reasons other than LiveData
becoming inactive, it will not be re-executed even after LiveData goes through active
inactive cycle.

As a best practice, it is important for the block to cooperate in cancellation. See kotlin
coroutines documentation for details
https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html.

The upstream flow collection starts when the returned LiveData becomes active
(LiveData.onActive).
If the LiveData becomes inactive (LiveData.onInactive) while the flow has not completed,
the flow collection will be cancelled after timeoutInMs milliseconds unless the LiveData
becomes active again before that timeout (to gracefully handle cases like Activity rotation).

After a cancellation, if the LiveData becomes active again, the upstream flow collection will
be re-executed.

If the upstream flow completes successfully or is cancelled due to reasons other than
LiveData becoming inactive, it will not be re-collected even after LiveData goes through
active inactive cycle.

asLiveData

The upstream flow collection starts when the returned LiveData becomes active
(LiveData.onActive).
If the LiveData becomes inactive (LiveData.onInactive) while the flow has not completed,
the flow collection will be cancelled after timeout unless the LiveData
becomes active again before that timeout (to gracefully handle cases like Activity rotation).

After a cancellation, if the LiveData becomes active again, the upstream flow collection will
be re-executed.

If the upstream flow completes successfully or is cancelled due to reasons other than
LiveData becoming inactive, it will not be re-collected even after LiveData goes through
active inactive cycle.

findViewTreeLifecycleOwner

Locates the LifecycleOwner responsible for managing this View, if present.
This may be used to scope work or heavyweight resources associated with the view
that may span cycles of the view becoming detached and reattached from a window.

observe

Adds the given onChanged lambda as an observer within the lifespan of the given
owner and returns a reference to observer.
The events are dispatched on the main thread. If LiveData already has data
set, it will be delivered to the onChanged.

switchMap

Returns a LiveData mapped from the input thisLiveData by applying
transform to each value set on this.

Note that when the backing LiveData is switched, no further values from the older
LiveData will be set to the output LiveData. In this way, the method is
analogous to io.reactivex.Observable.switchMap.

Here is an example class that holds a typed-in name of a user
String (such as from an EditText) in a MutableLiveData and
returns a LiveData containing a List of User objects for users that have
that name. It populates that LiveData by requerying a repository-pattern object
each time the typed name changes.

Note that this won't effect any sub coroutine if they use a different CoroutineDispatcher.
However, the block will not resume execution when the sub coroutine finishes unless the
Lifecycle is at least in minState.

If the Lifecycle is destroyed while the block is suspended, the block will be cancelled
which will also cancel any child coroutine launched inside the block.

If you have a try finally block in your code, the finally might run after the Lifecycle
moves outside the desired state. It is recommended to check the Lifecycle.getCurrentState
before accessing the UI. Similarly, if you have a catch statement that might catch
CancellationException, you should check the Lifecycle.getCurrentState before accessing the
UI. See the sample below for more details.

// running a block of code only if lifecycle is STARTED
viewLifecycle.whenStateAtLeast(Lifecycle.State.STARTED) {
// here, we are on the main thread and view lifecycle is guaranteed to be STARTED or RESUMED.
// We can safely access our views.
loadingBar.visibility = View.VISIBLE
try {
// we can call any suspend function
val data = withContext(Dispatchers.IO) {
// this will run in IO thread pool. It will keep running as long as Lifecycle
// is not DESTROYED. If it is destroyed, this coroutine will be cancelled as well.
// However, we CANNOT access Views here.
// We are using withContext(Dispatchers.IO) here just for demonstration purposes.
// Such code should live in your business logic classes and your UI should use a
// ViewModel (or similar) to access it.
api.getUser()
}
// this line will execute on the main thread and only if the lifecycle is in at least
// STARTED state (STARTED is the parameter we've passed to whenStateAtLeast)
// Because of this guarantee, we can safely access the UI again.
loadingBar.visibility = View.GONE
nameTextView.text = user.name
lastNameTextView.text = user.lastName
} catch(ex : UserNotFoundException) {
// same as above, this code can safely access UI elements because it only runs if
// view lifecycle is at least STARTED
loadingBar.visibility = View.GONE
showErrorDialog(ex)
} catch(th : Throwable) {
// Unlike the catch statement above, this catch statements it too generic and might
// also catch the CancellationException. Before accessing UI, you should check isActive
// or lifecycle state
if (viewLifecycle.currentState >= Lifecycle.State.STARTED) {
// here you can access the view because you've checked the coroutine is active
}
} finally {
// in case of cancellation, this line might run even if the Lifecycle is not DESTROYED.
// You cannot access Views here unless you check `isActive` or lifecycle state
if (viewLifecycle.currentState >= Lifecycle.State.STARTED) {
// safe to access views
} else {
// not safe to access views
}
}
}