Design Converter
Education
Last updated on Dec 17, 2024
•4 mins read
Last updated on Dec 17, 2024
•4 mins read
When developing modern Android apps, ensuring data persistence during configuration changes or system-initiated process death is crucial. This is where SavedStateHandle comes in. SavedStateHandle is a key component in Jetpack's ViewModel architecture, enabling you to save and restore UI state seamlessly across various lifecycle events. It acts as a key-value map, helping you maintain data during screen rotations, process deaths, or other scenarios.
Understanding how SavedStateHandle works can significantly enhance your app's resilience and user experience.
Let's explore this in detail.
A SavedStateHandle is a specialized class introduced in Jetpack, designed to work within ViewModel objects. When you create a ViewModel instance, you can use SavedStateHandle to save key-value pairs that persist even when the app process is killed or restarted. This is particularly useful during configuration changes like screen rotations.
• UI State Management: Store and restore UI state across lifecycle events.
• Survives Process Death: It retains data during system-initiated process death.
• Supports Configuration Changes: You don't need to manually handle saved state when the configuration changes.
• Flexible Data Types: It supports strings, ints, and even complex objects if you implement Parcelable.
SavedStateHandle is passed as a constructor argument when you initialize a ViewModel using the ViewModel SavedStateHandle API. This allows you to retrieve data stored in the key-value map and reuse it in a new instance of the ViewModel.
1class MyViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { 2 3 fun setQuery(query: String) { 4 savedStateHandle["search_query"] = query 5 } 6 7 fun getQuery(): String? { 8 return savedStateHandle["search_query"] 9 } 10}
In the above example:
• The setQuery method saves the search query in the SavedStateHandle.
• The getQuery method retrieves the stored value, even after a process death or configuration changes.
Traditionally, developers relied on onSaveInstanceState() to manage saved state. However, this required passing data through bundles and handling them manually. With SavedStateHandle, the process is more streamlined:
No need to implement Parcelable for complex objects.
It integrates with the lifecycle of ViewModel objects.
It simplifies handling configuration changes without boilerplate code.
Imagine a scenario where you want to retain the UI state during a screen rotation. Here's how you can achieve this using SavedStateHandle:
1class CounterViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { 2 3 companion object { 4 private const val COUNTER_KEY = "counter" 5 } 6 7 fun getCounter(): Int { 8 return savedStateHandle[COUNTER_KEY] ?: 0 // Default value is 0 9 } 10 11 fun incrementCounter() { 12 val currentCount = getCounter() 13 savedStateHandle[COUNTER_KEY] = currentCount + 1 14 } 15}
1class CounterFragment : Fragment() { 2 3 private lateinit var viewModel: CounterViewModel 4 5 override fun onCreate(savedInstanceState: Bundle?) { 6 super.onCreate(savedInstanceState) 7 8 viewModel = ViewModelProvider(this).get(CounterViewModel::class.java) 9 } 10 11 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 12 val counterTextView: TextView = view.findViewById(R.id.counterTextView) 13 val incrementButton: Button = view.findViewById(R.id.incrementButton) 14 15 counterTextView.text = viewModel.getCounter().toString() 16 17 incrementButton.setOnClickListener { 18 viewModel.incrementCounter() 19 counterTextView.text = viewModel.getCounter().toString() 20 } 21 } 22}
In this example:
• The counter value is saved using a key-value pair.
• Even if the app process is killed, the SavedStateHandle retains the data stored.
When the device undergoes a configuration change, such as a screen rotation, the ViewModel instance is re-created. However, the SavedStateHandle survives configuration changes, ensuring that the UI state is retained without additional effort.
You can save and retrieve a wide range of data types in SavedStateHandle. Examples include:
• String: savedStateHandle["key"] = "value"
• Int: savedStateHandle["key"] = 123
• Parcelable Objects: Store complex objects if they implement Parcelable.
Testing your ViewModel logic becomes easier with SavedStateHandle. You can test how your ViewModel instance handles data persistence during configuration changes or process death.
1@Test 2fun testCounterViewModel() { 3 val savedStateHandle = SavedStateHandle(mapOf("counter" to 5)) 4 val viewModel = CounterViewModel(savedStateHandle) 5 6 assertEquals(5, viewModel.getCounter()) 7 8 viewModel.incrementCounter() 9 assertEquals(6, viewModel.getCounter()) 10}
• Reduces boilerplate code for UI state management.
• Simplifies handling configuration changes and process deaths.
• Supports a wide range of data types, from primitives to complex objects.
• When you want to retain UI state across configuration changes.
• To handle system-initiated process death gracefully.
• For persisting key-value pairs in a ViewModel instance.
SavedStateHandle is a powerful tool for managing saved state in Android apps. It simplifies UI state management, ensures data persistence across configuration changes, and supports recovery after a system-initiated process death. Whether you're saving key-value pairs for a navigation graph or handling data in a ViewModel, SavedStateHandle is indispensable. Adopt it in your projects to make your app more robust and user-friendly.
With SavedStateHandle, you can focus on delivering great features without worrying about managing saved state manually. Happy coding!
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.