Android Lifecycle
Understanding the Android lifecycle and responding correctly to state changes is crucial to building apps with fewer bugs that use fewer resources and provide a good user experience. By Denis Buketa.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Android Lifecycle
25 mins
- Getting Started
- Understanding the Role of the Lifecycle in Apps
- Exploring the Activity Lifecycle
- Understanding Activity Lifecycle Callbacks
- Saving and Restoring the Activity Instance State
- Saving Instance State
- Restoring Instance State
- Passing Data Between Screens
- Understanding Intent Data
- Exploring Fragment Lifecycle
- Understanding Fragment Lifecycle Callbacks
- Using ViewModel to Store UI Data
- Where to Go From Here?
Exploring Fragment Lifecycle
Like activities, fragments have their lifecycle. When the user navigates and interacts with your app, your fragments transition through various states in their lifecycle as they’re added, removed, and enter or exit the screen.
In the figure above, you can see that fragment lifecycle is similar to the activity lifecycle but contains some additional fragment-specific methods. Before explaining each callback, check them in the app.
In the previous section, you played with two activities and saw how their lifecycle changed when you moved between screens. For this example, you’ll implement the same screens with fragments. You can find two fragments that represent each screen in the fragments package: MainFragment.kt and ShareFragment.kt. There’s also one container activity and viewmodels package. For now, ignore the viewmodels package. You’ll need that in the next section.
If you check MainFragment.kt, you’ll notice a lot of similarities with MainActivity.kt. They both share the same logic for managing states, but MainFragment.kt contains a few more lifecycle callbacks.
Before running the app, open SplashActivity.kt and update startFirstActivity()
so that it starts ActivityWithFragments
instead of MainActivity
:
private fun startFirstActivity() {
startActivity(Intent(this, ActivityWithFragments::class.java))
}
Excellent! Now, build and run the app. Then, inspect the logs.
Notice how the fragment lifecycle is synchronized with the activity lifecycle. First, the app creates and starts ActivityWithFragments. After that, it creates and starts the fragment and its view. Finally, it resumes both activity and fragment.
Next, tap back and again observe the logs.
By closing the app, you started the process of destroying the activity. As before, fragment lifecycle events follow activity lifecycle events. Both activity and fragment are first paused, then stopped and finally destroyed.
A fragment’s lifecycle state can never be greater than its parent. For example, a parent fragment or activity must start before its child fragments. Likewise, child fragments must stop before their parent fragment or activity. From the logs above, you might think otherwise — that the activity is stopping first — but that’s just because you print the log as the first call in lifecycle callbacks. Internally, the OS makes sure that all the child fragments stop before stopping the activity.
Understanding Fragment Lifecycle Callbacks
You can now dive a bit deeper into every lifecycle event to get a better understanding of the fragment lifecycle:
-
onCreate(): The fragment reaches the Created state. Similar to the activity’s
onCreate()
, this callback receivesBundle
, containing any state previously saved byonSaveInstanceState()
. - onCreateView(): It’s called to inflate or create the fragment’s view.
-
onViewCreated(): The fragment’s view is instantiated with a non-null
View
object. That view is set on the fragment and can be retrieved usinggetView()
. -
onStart(): The fragment enters the Started state. In this state, it’s guaranteed that the fragment’s view is available and that it’s safe to perform
FragmentTransaction
on the childFragmentManager
of the fragment. -
onResumed(): The fragment enters the Resumed state. It becomes visible after all
Animator
andTransition
effects have finished. The user is now able to interact with the fragment. - onPause(): The fragment goes back to the Started state. The OS invokes this callback as the user begins to leave the fragment while the fragment is still visible.
- onStop(): The fragment goes back to the Created state and is no longer visible.
- onDestroyView(): It’s triggered after all the exit animations and transitions have completed, when the fragment’s view has been detached from the window. At this point, all references to the fragment’s view should be removed, allowing the fragment’s view to be garbage collected.
-
onDestroy(): The fragment enters the Destroyed state. It happens when the fragment is removed or if
FragmentManager
is destroyed. At this point, the fragment has reached the end of its lifecycle.
Now that you understand better what goes under the hood, navigate between the Main and Share screens to see the dance of the fragment lifecycle. :]
As you’ve seen in this section and the previous one, the Android lifecycle is quite complex. Managing states and interacting with the UI at the right time can be tricky for inexperienced developers. This led to some new Android APIs and components that should make life easier for all Android developers. One such component is the ViewModel.
- To learn more about fragments, check out the Android Fragments Tutorial: An Introduction with Kotlin.
- For more information about the fragment lifecycle, refer to the Android Developer documentation.
- To learn more about fragments, check out the Android Fragments Tutorial: An Introduction with Kotlin.
- For more information about the fragment lifecycle, refer to the Android Developer documentation.
Using ViewModel to Store UI Data
ViewModel
is designed to store and manage UI-related data in a lifecycle-conscious way.
First, replace the logic for saving and restoring state in MainFragment.kt with the approach that uses ViewModel
.
In the viewmodels package, create a new class called MainViewModel.kt.
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.raywenderlich.android.puppycounter.model.DogCount
class MainViewModel : ViewModel() {
private val _dogCount: MutableLiveData<DogCount> by lazy {
MutableLiveData<DogCount>(DogCount())
}
val dogCount: LiveData<DogCount> = _dogCount
fun setDogCount(dogCount: DogCount) {
_dogCount.value = dogCount
}
}
This will be ViewModel
for your Main screen. You’ll use it to save DogCount
‘s state. Use _dogCount
for tracking the state and dogCount
for exposing it to the observers. For updating the state, use setDogCount()
.
LiveData
, refer to the Android Developer documentation.
In the MainFragment.kt, add these imports:
import androidx.fragment.app.viewModels
import com.raywenderlich.android.puppycounter.fragments.viewmodels.MainViewModel
Add the following line above onCreate()
:
private val viewModel: MainViewModel by viewModels()
With this, you added the code to create MainViewModel
for MainFragment
.
Next, add the following method to MainFragment.kt:
private fun subscribeToViewModel() {
viewModel.dogCount.observe(viewLifecycleOwner, { value ->
dogCount = value
renderDogCount(dogCount)
})
}
This method allows you to subscribe to the observable state in MainViewModel
. Whenever dogCount
‘s state changes, the app pushes the new state to the view and renderDogCount()
is called with the new state, resulting in the updated UI.
Then, modify onViewCreated()
so you subscribe to MainViewModel
after the super call:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Timber.i("PuppyCounter - MainFragment - onViewCreated()")
super.onViewCreated(view, savedInstanceState)
subscribeToViewModel()
findViews(view)
setupSmallDogViewsClickListeners(view)
setupMiddleDogViewsClickListeners(view)
setupBigDogViewsClickListeners(view)
}
You also need the logic that will update the state in MainViewModel
. Modify updateDogCount()
like this:
private fun updateDogCount(newDogCount: DogCount) {
viewModel.setDogCount(newDogCount)
}
This method is called whenever the user updates the counters. It’ll update MainViewModel
with the new state. MainViewModel
will push that state through dogCount
and the MainFragment
will be notified since it’s subscribed to the MainViewModel
.
Finally, in MainFragment
remove onSaveInstanceState()
, renderDogCount(dogCount)
call from the onResume()
and the savedInstanceState?.run { ... }
code in onCreate()
. You no longer need that. :]
Build and run the app. Tap the counters a couple times and rotate the screen. You should see something like this:
Check the illustration below to see ViewModel
‘s lifetime next to the associated activity lifecycle.
ViewModel
objects are scoped to the Lifecycle
passed to the ViewModelProvider
when getting ViewModel
. It remains in memory until the Lifecycle
that it’s scoped to goes away permanently. For an activity, that happens when it’s finished. For a fragment, that happens when it’s detached.