The Android process lifecycle documentation details the various states the application process may be in and how it relates to the visibility of the UI to the user.
For example, when the user switches from one Activity to another and the first Activity is no longer visible, the process containing the first Activity will enter the "cached" state.
The documentation also gives some detail about how the system decides to evict process based on the state the process is in.
I cannot however, find any documentation on what the different process lifecycle states mean from a CPU scheduling standpoint.
Specifically, are cached processes no longer scheduled on the CPU? Or can they still execute code? I.e. does the system actually freeze the cached processes, or do they actually execute code but are just not visible to the user and and more likely to be killed?
This is a related question, but the focuses on the memory usage and evictability, rather than CPU scheduling.
Specifically, are cached processes no longer scheduled on the CPU?
I don't usually think of processes being scheduled on a CPU. I think of threads as being scheduled on a CPU. Perhaps we are just using the terms differently.
A cached process' threads are no different than any other process' threads. Ideally, a cached process only has threads that are blocked waiting on something (e.g., IPC from a core OS process, telling the app process to start another activity in response to the user pressing a icon from the home screen). However, there is nothing stopping an app from having leaked some thread that continues running, for however long that process remains cached.
For example, you could create an app with a single activity like this:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Executors
.newSingleThreadScheduledExecutor()
.scheduleAtFixedRate({ Log.e("BadWolves", "Zombie!") }, 5, 5, TimeUnit.SECONDS)
finish()
}
}
Here, I fork a zombie thread, then finish()
the activity. The process moves into the cached state fairly quickly, once the activity is destroyed. Yet, the zombie thread continues logging to LogCat.
How long it logs to LogCat varies by OS version and perhaps manufacturer tweaks. So, for example, on the Pixel 2 that I just tossed this onto, it has been logging for 10 minutes, which frankly is longer than I would have expected on Android 8.1.
This would mean that it was the duty of every app in existence to implement pausing correctly when backgrounded.
Yes, to an extent. The OS can terminate your process at any point, and cached processes are prime candidates to be terminated when system RAM is needed. So, leaked threads usually don't live all that long, because cached processes usually don't live all that long. Part of the reason why my zombie is staggering around as long as it is is that this device isn't used for a lot, so I don't have a lot of processes coming and going, minimizing pressure on system RAM.
Now, if you'll excuse me, I need to kill a zombie...