Bird
Raised Fist0
Android Kotlinmobile~20 mins

SavedStateHandle in Android Kotlin - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Counter Screen with SavedStateHandle
A simple counter screen that saves its count value using SavedStateHandle so the count persists across process death and configuration changes.
Target UI
---------------------
| Counter Screen    |
|-------------------|
| Count: 0          |
|                   |
| [+] Increment     |
|                   |
---------------------
Display a count starting at 0
Add a button labeled '+' to increment the count
Use SavedStateHandle in ViewModel to save and restore the count
Count value should persist after screen rotation or app process death
Starter Code
Android Kotlin
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
        }
    }
}
Task 1
Task 2
Task 3
Task 4
Task 5
Solution
Android Kotlin
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.

Final Result
Completed Screen
---------------------
| Counter Screen    |
|-------------------|
| Count: 3          |
|                   |
| [+] Increment     |
|                   |
---------------------
User taps '+' button increments count by 1
Count text updates immediately to show new count
Count value persists after rotating the device
Count value persists if app is killed and restored by system
Stretch Goal
Add a 'Reset' button that sets the count back to zero and updates the saved state.
💡 Hint
Add a new function reset() in ViewModel that sets count to 0 and updates SavedStateHandle. Add a button in layout and call reset() on click.

Practice

(1/5)
1.

What is the main purpose of SavedStateHandle in Android Kotlin?

easy
A. To save and restore small UI-related data automatically during configuration changes
B. To manage database connections
C. To handle network requests
D. To create UI layouts dynamically

Solution

  1. 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.
  2. 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.
  3. Final Answer:

    To save and restore small UI-related data automatically during configuration changes -> Option A
  4. Quick Check:

    SavedStateHandle saves UI state = C [OK]
Hint: Remember: SavedStateHandle saves UI data on config changes [OK]
Common Mistakes:
  • Confusing SavedStateHandle with database or network management
  • Thinking it manages large data or files
  • Assuming it creates UI layouts
2.

Which of the following is the correct way to store a value "username" in SavedStateHandle named savedStateHandle?

?
easy
A. savedStateHandle.put("username", "John")
B. savedStateHandle.set("username", "John")
C. savedStateHandle.save("username", "John")
D. savedStateHandle.store("username", "John")

Solution

  1. Step 1: Recall SavedStateHandle API methods

    The correct method to store data is set(key, value).
  2. Step 2: Check method names in options

    Only savedStateHandle.set("username", "John") uses set, others use incorrect method names.
  3. Final Answer:

    savedStateHandle.set("username", "John") -> Option B
  4. Quick Check:

    Use set() to store data = A [OK]
Hint: Use set() to save data in SavedStateHandle [OK]
Common Mistakes:
  • Using put(), save(), or store() which do not exist
  • Confusing with SharedPreferences methods
  • Forgetting to use quotes around keys
3.

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")
medium
A. null
B. Alice
C. Bob
D. 30

Solution

  1. Step 1: Analyze the set operations

    The key "name" is first set to "Alice", then overwritten with "Bob".
  2. Step 2: Determine the value returned by get

    Getting "name" returns the latest value, which is "Bob".
  3. Final Answer:

    Bob -> Option C
  4. Quick Check:

    Last set value returned = A [OK]
Hint: Last set value for a key is returned by get() [OK]
Common Mistakes:
  • Assuming first set value is returned
  • Confusing key names
  • Expecting numeric value for string key
4.

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)
medium
A. println() cannot print variables
B. Missing key in get() method
C. SavedStateHandle cannot store Int values
D. Type mismatch: trying to assign Int to String without conversion

Solution

  1. Step 1: Check variable types

    The value stored is Int (10), but retrieved as String without conversion.
  2. Step 2: Understand Kotlin type safety

    Assigning Int to String variable causes a type mismatch error.
  3. Final Answer:

    Type mismatch: trying to assign Int to String without conversion -> Option D
  4. Quick Check:

    Type mismatch error = D [OK]
Hint: Match types when getting values from SavedStateHandle [OK]
Common Mistakes:
  • Ignoring type mismatch errors
  • Assuming automatic type conversion
  • Thinking SavedStateHandle only stores strings
5.

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?
    }
}
hard
A. saveScore: savedStateHandle.set("score", score) getScore: savedStateHandle.get<Int>("score") ?: 0
B. saveScore: savedStateHandle.put("score", score) getScore: savedStateHandle.get("score") as Int
C. saveScore: savedStateHandle.save("score", score) getScore: savedStateHandle.get("score") ?: 0
D. saveScore: savedStateHandle.set("score", score.toString()) getScore: savedStateHandle.get<String>("score")?.toInt() ?: 0

Solution

  1. Step 1: Use correct methods to save and get data

    Use set to save and get<Int> to retrieve with a default fallback.
  2. 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.
  3. Final Answer:

    saveScore: savedStateHandle.set("score", score) getScore: savedStateHandle.get<Int>("score") ?: 0 -> Option A
  4. Quick Check:

    Use set() and get<Int>() with default = B [OK]
Hint: Always use set() and get<Type>() with default fallback [OK]
Common Mistakes:
  • Using non-existent methods like put() or save()
  • Storing Int as String unnecessarily
  • Not providing default value on get()
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard