Preserving Activity History Across Configuration Changes in Android

Preserving Activity History Across Configuration Changes in Android

Introduction

One of the biggest challenges in Android development is handling configuration changes, such as screen rotation, dark mode switches, or language changes. By default, when a configuration change occurs, the Activity is destroyed and recreated, leading to the loss of UI state and history.


What Happens During a Configuration Change?

A configuration change occurs when there is a fundamental change in the device environment that forces the system to recreate the Activity.

Common Causes of Configuration Changes

  • Screen Rotation – Switching between portrait and landscape mode.

  • System Language Change – Changing the device language from settings.

  • Dark Mode Toggle – Switching between light and dark themes.

  • Multi-Window Mode – Running multiple apps in split-screen.

How Does Android Handle Configuration Changes?

When a configuration change occurs:

  • The system destroys the current Activity.

  • A new instance of the Activity is created.

  • All UI state and user input are lost unless manually preserved.

Let’s now explore the best ways to handle these changes.


The ViewModel class is part of Jetpack’s Android Architecture Components and is designed to persist data across configuration changes.

Why Use ViewModel?

  • Retains data across configuration changes.

  • Does not require manual state saving.

  • Cleaner and more maintainable code.

Implementation:

Step 1: Create a ViewModel Class

class HistoryViewModel : ViewModel() {
    var userText: String? = null
}

Step 2: Use ViewModel in Activity

class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: HistoryViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel = ViewModelProvider(this).get(HistoryViewModel::class.java)

        val editText = findViewById<EditText>(R.id.editText)
        editText.setText(viewModel.userText)

        editText.addTextChangedListener {
            viewModel.userText = it.toString()
        }
    }
}

Result:

  • The ViewModel persists data even when the screen rotates.

  • The user’s text input is not lost.

Best for: Apps that need to maintain temporary UI state across configuration changes.


Method 2: Using onSaveInstanceState()

The onSaveInstanceState() method allows us to save small UI state data before the Activity is destroyed and restore it in onCreate().

When to Use onSaveInstanceState()?

  • For small amounts of data like text input.

  • When you don’t need data persistence beyond app restarts.

Implementation:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val editText = findViewById<EditText>(R.id.editText)
        editText.setText(savedInstanceState?.getString("user_text", ""))
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        val editText = findViewById<EditText>(R.id.editText)
        outState.putString("user_text", editText.text.toString())
    }
}

How It Works:

onSaveInstanceState() is called before the Activity is destroyed.
The stored data is retrieved in onCreate() using the savedInstanceState Bundle.

Best for: Simple UI state preservation, such as text fields or scroll positions.

Limitations:

  • Data is lost if the app is force closed.

  • Cannot store complex objects.


Method 3: Using PersistableBundle for Persistent Data

If you need long-term data persistence, you can use PersistableBundle. This is useful when data should persist even after an app restart.

Implementation:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
        super.onCreate(savedInstanceState, persistentState)
        setContentView(R.layout.activity_main)
    }

    override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
        super.onSaveInstanceState(outState, outPersistentState)
        outPersistentState.putString("user_text", "Persistent Data")
    }
}

When to Use PersistableBundle?

  • When data should persist across app restarts.

  • When handling important user sessions.

Best for: Storing long-term preferences or user sessions.


Comparison of Methods

MethodBest Use CaseSurvives App Restart?
ViewModelUI state, small data❌ No
onSaveInstanceState()Simple UI state like text fields❌ No
PersistableBundlePersistent storage across app restarts✅ Yes

Best Practices for Preserving Activity History:

  • Use ViewModel for UI-related data across configuration changes.

  • Use onSaveInstanceState() for small, temporary UI states.

  • Use PersistableBundle for long-term data persistence.


Conclusion

Handling configuration changes effectively ensures a smooth user experience in Android apps.

  • ViewModel is the best approach for UI-related data.

  • onSaveInstanceState() is great for small temporary data.

  • PersistableBundle should be used for persistent storage needs.