--------------------- | Counter Screen | |-------------------| | Count: 0 | | | | [+] Increment | | | ---------------------
SavedStateHandle in Android Kotlin - Mini App: Build & Ship
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Button import android.widget.TextView class CounterViewModel(private val state: SavedStateHandle) : ViewModel() { // TODO: Add count property using SavedStateHandle // TODO: Add increment function } class MainActivity : AppCompatActivity() { private lateinit var viewModel: CounterViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // TODO: Initialize ViewModel with SavedStateHandle val countText = findViewById<TextView>(R.id.countText) val incrementButton = findViewById<Button>(R.id.incrementButton) // TODO: Observe count and update UI incrementButton.setOnClickListener { // TODO: Call increment on ViewModel } } }
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Button import android.widget.TextView import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Observer class CounterViewModel(private val state: SavedStateHandle) : ViewModel() { companion object { private const val COUNT_KEY = "count" } private val _count = MutableLiveData(state.get<Int>(COUNT_KEY) ?: 0) val count: LiveData<Int> = _count fun increment() { val newCount = (_count.value ?: 0) + 1 _count.value = newCount state.set(COUNT_KEY, newCount) } } class MainActivity : AppCompatActivity() { private lateinit var viewModel: CounterViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewModel = ViewModelProvider(this, object : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(CounterViewModel::class.java)) { @Suppress("UNCHECKED_CAST") return CounterViewModel(SavedStateHandle()) as T } throw IllegalArgumentException("Unknown ViewModel class") } }).get(CounterViewModel::class.java) val countText = findViewById<TextView>(R.id.countText) val incrementButton = findViewById<Button>(R.id.incrementButton) viewModel.count.observe(this, Observer { count -> countText.text = "Count: $count" }) incrementButton.setOnClickListener { viewModel.increment() } } }
We created a CounterViewModel that takes a SavedStateHandle to store the count value. The count is stored in a MutableLiveData initialized from the saved state or 0 if none exists. The increment() function updates the count and saves it back to SavedStateHandle.
In MainActivity, we initialize the ViewModel with a factory that provides a SavedStateHandle. We observe the count LiveData to update the UI text. When the '+' button is clicked, we call increment() to increase the count.
This setup ensures the count value survives configuration changes and process death because SavedStateHandle saves the state automatically.
--------------------- | Counter Screen | |-------------------| | Count: 3 | | | | [+] Increment | | | ---------------------
Practice
What is the main purpose of SavedStateHandle in Android Kotlin?
Solution
Step 1: Understand the role of SavedStateHandle
SavedStateHandle is designed to save small pieces of data like UI state during configuration changes such as screen rotations.Step 2: Compare with other options
Options A, B, and D relate to network requests, database connections, and UI layout creation, which are not the purpose of SavedStateHandle.Final Answer:
To save and restore small UI-related data automatically during configuration changes -> Option AQuick Check:
SavedStateHandle saves UI state = C [OK]
- Confusing SavedStateHandle with database or network management
- Thinking it manages large data or files
- Assuming it creates UI layouts
Which of the following is the correct way to store a value "username" in SavedStateHandle named savedStateHandle?
?Solution
Step 1: Recall SavedStateHandle API methods
The correct method to store data isset(key, value).Step 2: Check method names in options
Only savedStateHandle.set("username", "John") usesset, others use incorrect method names.Final Answer:
savedStateHandle.set("username", "John") -> Option BQuick Check:
Use set() to store data = A [OK]
- Using put(), save(), or store() which do not exist
- Confusing with SharedPreferences methods
- Forgetting to use quotes around keys
Given the code below, what will be the output of println(savedStateHandle.get<String>("name"))?
val savedStateHandle = SavedStateHandle()
savedStateHandle.set("name", "Alice")
savedStateHandle.set("age", 30)
savedStateHandle.set("name", "Bob")Solution
Step 1: Analyze the set operations
The key "name" is first set to "Alice", then overwritten with "Bob".Step 2: Determine the value returned by get
Getting "name" returns the latest value, which is "Bob".Final Answer:
Bob -> Option CQuick Check:
Last set value returned = A [OK]
- Assuming first set value is returned
- Confusing key names
- Expecting numeric value for string key
Identify the error in the following code snippet using SavedStateHandle:
val savedStateHandle = SavedStateHandle()
savedStateHandle.set("count", 10)
val count: String = savedStateHandle.get("count") ?: "0"
println(count)Solution
Step 1: Check variable types
The value stored is Int (10), but retrieved as String without conversion.Step 2: Understand Kotlin type safety
Assigning Int to String variable causes a type mismatch error.Final Answer:
Type mismatch: trying to assign Int to String without conversion -> Option DQuick Check:
Type mismatch error = D [OK]
- Ignoring type mismatch errors
- Assuming automatic type conversion
- Thinking SavedStateHandle only stores strings
You want to save a user's score in SavedStateHandle and restore it after a configuration change. Which approach correctly ensures the score is saved and retrieved safely?
class ScoreViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
fun saveScore(score: Int) {
// What to do here?
}
fun getScore(): Int {
// What to do here?
}
}Solution
Step 1: Use correct methods to save and get data
Usesetto save andget<Int>to retrieve with a default fallback.Step 2: Validate type safety and default value
saveScore: savedStateHandle.set("score", score) getScore: savedStateHandle.get<Int>("score") ?: 0 uses correct method names and safely returns 0 if no score is saved.Final Answer:
saveScore: savedStateHandle.set("score", score) getScore: savedStateHandle.get<Int>("score") ?: 0 -> Option AQuick Check:
Use set() and get<Int>() with default = B [OK]
- Using non-existent methods like put() or save()
- Storing Int as String unnecessarily
- Not providing default value on get()
Ready to prove it?
Pass the test and this pattern moves unknown
