Keywords: Android Configuration Changes | Activity Restart | State Preservation | ViewModel | Application Class
Abstract: This article provides a comprehensive examination of Android's Activity restart mechanism triggered by device configuration changes such as screen rotation and keyboard visibility. It analyzes the system's default behavior and its impact on application state. Three primary solutions are detailed: using Application class for global initialization, preserving UI state with ViewModel, and manually handling changes via android:configChanges. Code examples illustrate implementation details and appropriate use cases for each approach, helping developers optimize user experience during configuration transitions.
Android Configuration Changes and Activity Restart Mechanism
In Android application development, device configuration changes (such as screen orientation rotation and keyboard availability) trigger the system's default Activity restart mechanism. When a configuration change occurs, the system destroys the current Activity instance and recreates a new one, causing the onCreate method to be called again. This mechanism ensures applications automatically adapt to new device configurations but also introduces state loss issues.
Types and Impact of Configuration Changes
Common configuration changes include screen orientation, keyboard availability, screen size, locale, font size, and system theme. These changes are typically triggered by user interactions like rotating the device or modifying system settings. Activity restart clears all state stored in the Activity instance and its contained objects like Fragments and Views. If not properly handled, this leads to lost user input or inconsistent application state.
Using Application Class for Global Initialization
For initialization code that needs to persist throughout the application lifecycle, it can be moved to a custom Application class. The Application class's onCreate method is called only when the application first starts and is not repeated during configuration changes.
Kotlin implementation example:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Place application initialization code here
initializeAppComponents()
}
private fun initializeAppComponents() {
// Initialize network components, database, etc.
}
}
Java implementation example:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// Place application initialization code here
initializeAppComponents();
}
private void initializeAppComponents() {
// Initialize network components, database, etc.
}
}
Register the custom Application class in AndroidManifest.xml:
<application
android:name=".MyApplication"
...>
<activity ... />
</application>
Using ViewModel to Preserve UI State
ViewModel is part of Android's architecture components, specifically designed to retain UI-related data during configuration changes. ViewModel instances are not destroyed when Activities are recreated, maintaining data consistency.
Kotlin implementation example:
class MainViewModel : ViewModel() {
private val _userData = MutableLiveData<String>()
val userData: LiveData<String> = _userData
fun updateUserData(data: String) {
_userData.value = data
}
}
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
// Observe data changes in ViewModel
viewModel.userData.observe(this) { data ->
// Update UI
updateUIWithData(data)
}
}
private fun updateUIWithData(data: String) {
// Update interface based on data
}
}
Manual Handling of Configuration Changes
By configuring the android:configChanges attribute, you can prevent the system from automatically handling specific configuration changes, instead handling them manually within the application.
Configuration in AndroidManifest.xml:
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:label="@string/app_name">
</activity>
Then override the onConfigurationChanged method in the Activity:
Kotlin implementation example:
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// Update UI based on new configuration
when {
newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE -> {
// Landscape layout handling
setContentView(R.layout.activity_main_landscape)
}
newConfig.orientation == Configuration.ORIENTATION_PORTRAIT -> {
// Portrait layout handling
setContentView(R.layout.activity_main_portrait)
}
}
// Check keyboard availability
if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO) {
// Handle keyboard visibility
handleKeyboardVisible()
}
}
Best Practices for State Preservation and Restoration
Regardless of the chosen approach, proper handling of application state preservation and restoration is essential:
- Use onSaveInstanceState: Save temporary UI state
- Persistent storage: Use databases or SharedPreferences for important data
- Avoid memory leaks: Ensure resource release when Activity is destroyed
- Test various scenarios: Include configuration changes, process death, and other edge cases
State preservation example:
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
// Save current state
outState.putString("user_input", editText.text.toString())
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Restore saved state
savedInstanceState?.getString("user_input")?.let { savedText ->
editText.setText(savedText)
}
}
Performance Optimization Considerations
When handling configuration changes, consider performance impacts:
- Avoid time-consuming operations in
onCreate - Use asynchronous tasks for network requests or file operations
- Use caching appropriately to reduce repeated initialization
- Monitor memory usage to prevent issues from frequent recreations
Compatibility Considerations
Different Android versions handle configuration changes differently:
- Android 3.2 and above require including
screenSize - Some configuration changes cannot be disabled, like dynamic colors introduced in Android 12L
- Consider special requirements for different device form factors (foldables, tablets, etc.)
By appropriately selecting and implementing configuration change strategies, developers can significantly enhance application user experience, ensuring consistent and smooth operation across various device configurations.