Process Death - Part 1: Introduction and Overview
Introduction
- Process Death - Part 1: Introduction and Overview
- An overview and a short code example to demonstrate
- Process Death - Part 2: TBA
- Process Death - Part 3? - We'll see if this is needed (hopefully not)
What is it, and when does it occur?
Process Death means your app has been terminated by Android and no longer resides in memory, but it still appears in Recents*. If the app has implemented proper state saving, it should generally restore to a similar state when reopened.
* The button that shows recent tasks — bottom left on my Samsung phone.
The app remains listed in Recents and looks the same as always, so there is no easy way to know if Process Death has occurred. This makes testing slightly harder (see below for more information).
Process Death occurs when a device needs to reclaim memory from an app that is no longer in the foreground. This can sometimes be seen with apps in Recents that haven’t been accessed for a while. On devices with less memory or stricter task-retention policies, it may happen much sooner.
You may be able to observe this on a real device by opening Recents and selecting one of the older apps you haven’t used for a while. If it restarts instead of resuming instantly, Process Death likely occurred. If you have any of your own apps on the device, I encourage you to try this.
How can we cause it?
- From Developer Options* go most of the way down the screen (around 4/5th) until you find the Apps section and the Don't keep activities option. Select this.
The Lifecycle and how to see Process Death
private val TAG = "Main"
override fun onCreate(savedInstanceState: Bundle?) {
val FUNC = "onCreate(savedInstanceState: Bundle?)"
Log.d(TAG, "Lifecycle: $FUNC")
super.onCreate(savedInstanceState)
//...
}
override fun onRestart() {
val FUNC = "onRestart()"
Log.d(TAG, "Lifecycle: $FUNC")
super.onRestart()
}
override fun onStart() {
val FUNC = "onStart()"
Log.d(TAG, "Lifecycle: $FUNC")
super.onStart()
}
override fun onResume() {
val FUNC = "onResume()"
Log.d(TAG, "Lifecycle: $FUNC")
super.onResume()
}
override fun onPause() {
val FUNC = "onPause()"
Log.d(TAG, "Lifecycle: $FUNC")
super.onPause()
}
override fun onSaveInstanceState(outState: Bundle) {
val FUNC = "onSaveInstanceState()"
Log.d(TAG, "Lifecycle: $FUNC")
super.onSaveInstanceState(outState)
}
override fun onStop() {
val FUNC = "onStop()"
Log.d(TAG, "Lifecycle: $FUNC")
super.onStop()
}
override fun onDestroy() {
val FUNC = "onDestroy()"
Log.d(TAG, "Lifecycle: $FUNC")
super.onDestroy()
}
Can we ignore it?
Code: MinimalProcessDeathDetection
ViewModel
class MinimalProcessDeathDetectionViewModel(
savedStateHandle: SavedStateHandle
) : ViewModel() {
private val PD_KEY = "KEY-PD"
private val _processDeathOccurred = MutableStateFlow(
savedStateHandle[PD_KEY] ?: false
)
val processDeathOccurred = _processDeathOccurred.asStateFlow()
init { savedStateHandle[PD_KEY] = true }
}
UI
@Composable
fun PdMinUi(viewModel: MinimalProcessDeathDetectionViewModel) {
val processDeath by viewModel.processDeathOccurred.collectAsStateWithLifecycle()
Text("Process Death Occurred: $processDeath")
}
UI Host (minimal and basic, but useful if needed)
@Composable
fun ProcessDeathMonitorMinimalApp() {
val viewModel = viewModel<MinimalProcessDeathDetectionViewModel>()
Column(Modifier.padding(vertical = 128.dp, horizontal = 64.dp)) {
Text("ProcessDeathMonitorMinimalApp")
PdMinUi(viewModel)
}
}
Summary
Next
- Process Death - Part 2: TBA
References
The ULTIMATE Guide to Sharing Data Between Screens in Jetpack Compose - Philipp Lackner
Full Video: https://www.youtube.com/watch?v=h61Wqy3qcKg- Gemini Summary of Video:
The video "Sharing Data Between Screens In Android" provides a guide on five different methods to share data between screens in Android using Jetpack Compose:- Navigation Arguments: A simple method for passing stateless data between one or two screens. It's easy to use and the values survive process death [01:04].
- Shared View Model: A more popular approach where a single view model instance is shared between multiple screens. It supports real-time state changes and is suitable for sharing data across several screens within a feature [06:03].
- Sharing a Stateful Dependency (Singleton): The video advises against this approach as it can be dangerous and difficult to maintain [13:29].
- Composition Locals: A Compose-specific way to share data within a composable tree, simplifying data sharing and avoiding prop drilling [17:12].
- Persistent Storage: Uses persistent storage like SharedPreferences to save and retrieve global data that needs to survive application and process death. It's ideal for data like user sessions [20:20].
The video concludes that all methods, except the Singleton approach, have a valid use case and understanding when to use each is crucial for building robust Android applications [22:57].
- Gemini Summary of Video:
- Navigation Arguments: A simple method for passing stateless data between one or two screens. It's easy to use and the values survive process death [01:04].
- Shared View Model: A more popular approach where a single view model instance is shared between multiple screens. It supports real-time state changes and is suitable for sharing data across several screens within a feature [06:03].
- Sharing a Stateful Dependency (Singleton): The video advises against this approach as it can be dangerous and difficult to maintain [13:29].
- Composition Locals: A Compose-specific way to share data within a composable tree, simplifying data sharing and avoiding prop drilling [17:12].
- Persistent Storage: Uses persistent storage like SharedPreferences to save and retrieve global data that needs to survive application and process death. It's ideal for data like user sessions [20:20].
Processes and app lifecycle:
https://developer.android.com/guide/components/activities/activity-lifecycle
Saved State module for ViewModel
https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-savedstate
Comments
Post a Comment