In 2012, I worked at a startup building an Android launcher. We had a daemon whose job was to keep the launcher process running, and a native search indexer for fast searches. Each ran in its own process. I've always been curious about how big apps work under the hood. When do they reach for a separate process, and why?
I inspected the manifests and available bytecode of fifteen major Android apps to see what they run in separate processes. The declarations show where code can run; tracing the code helps explain what those processes do. Where I could not establish the reason for a split, I label my interpretation as an assumption.
These examples come from the APKs I examined, rather than a complete runtime trace of every feature. For the underlying Android mechanisms, see multi-process architecture.
Why do Android apps use multiple processes?
An extra process adds startup, memory, and communication costs. The useful question is what it provides that an ordinary activity, service, or thread cannot.
Security isolation and sandboxing
Apps routinely process content they don't control: web pages, executable code, images, and videos. A bug in the code handling that content can let an attacker execute code inside the app. Running it in a sandboxed process limits what that code can access: the worker does not inherit the app's normal permissions or direct access to its private files. The separate process also contains crashes that exception handling cannot recover from.
Moving the same code to a background thread would keep the UI responsive, but it would still share the app's memory and permissions. Exception handling can catch recoverable errors; it cannot contain arbitrary memory corruption or stop exploited native code from using the process's access. The restricted process is what changes that access.
The manifest snippets below are taken directly from the examined APKs. The diagrams show selected process boundaries, not every process running in an app.
Reading the diagrams
- Main app process
- Isolated worker
- Separate app process
- Restart helper
- Android, another app, or a file
Chrome
On Android, Chrome declares its sandboxed worker processes as isolated services. Here is one declaration from the APK.
<service
android:name="org.chromium.content.app.SandboxedProcessService0"
android:process=":sandboxed_process0"
android:isolatedProcess="true"
android:exported="false"
android:useAppZygote="true" />android:process names the separate worker process. Three flags explain how
Android starts and restricts it:
isolatedProcess="true"runs the service under a separate, restricted user ID (UID), without the app's normal permissions or direct access to its private files.exported="false"keeps the service private to the app, so other apps cannot directly start it or connect to it. Android also allows apps sharing the same UID, which is a rare exception.useAppZygote="true"lets Android prepare code and data once in a helper process, then create isolated workers from it. Workers can reuse that preparation instead of repeating it at startup, which can reduce startup work and let workers share preloaded memory pages. The sandbox comes fromisolatedProcess="true": Android gives each worker a restricted UID without the app's permissions or direct access to its private files.
Chrome runs web content in restricted renderer processes, limiting what a compromised page can access outside its sandbox. Chromium keeps the browser as the trusted coordinator. The browser passes each worker a file descriptor for its IPC channel, while Android gives the worker a restricted identity. This way, a compromised renderer does not inherit the browser's filesystem and account access. Chrome's Android sandbox design, Chromium's multi-process architecture.
For how sandboxed web content appears inside a native app's window, see Multi-process WebView.
Chrome applies the same pattern to photo decoding. It opens the image for an isolated decoder through a file descriptor. The decoder returns the result through shared memory. If a malformed image crashes or compromises the decoder, the separate, restricted process limits the damage. Shared memory keeps large image payloads out of ordinary Binder transactions.
<service
android:name="org.chromium.chrome.browser.photo_picker.DecoderService"
android:process=":decoder_service"
android:isolatedProcess="true"
android:exported="false" />
Firefox
Firefox also separates web content from the main browser process. It declares both ordinary and isolated web-content services, along with workers for graphics, networking, and media.
Compare one ordinary tab service with one isolated tab service:
<service
android:name="org.mozilla.gecko.process.GeckoChildProcessServices$tab0"
android:process=":tab_disable_art_image_0"
android:isolatedProcess="false"
android:exported="false" />
<service
android:name="org.mozilla.gecko.process.GeckoChildProcessServices$isolatedTab0"
android:process=":isolatedTab_disable_art_image_0"
android:isolatedProcess="true"
android:exported="false" />Both run outside the main process. Only the second requests an isolated identity. These are alternative service variants, not two required workers for each page. A separate process alone does not remove Firefox's permissions.
Firefox's main process coordinates these workers and handles the loss of a worker if Android stops it. Isolated services add the permission restrictions shown above. Mozilla explains this in its GeckoView architecture guide and isolated-process design.
WhatsApp
WhatsApp uses an isolated process to compress images into JPEGs. The app sends it an already-decoded bitmap, compression settings, and a file descriptor for writing the result. The worker runs the native compression code with restricted access, limiting the damage if that code crashes or is compromised.
<service
android:name="com.whatsapp.security.sandbox.SecuritySandboxService"
android:enabled="true"
android:isolatedProcess="true" />There is no android:process attribute here. Setting isolatedProcess="true"
is enough for Android to run the service in a separate process with restricted
access.
I couldn't find a technical post from WhatsApp explaining this design. My assumption is that it isolates the native compression code to contain crashes and limit the damage from an attack. For example, if a malicious image could trigger a bug in the compression code after being decoded into a bitmap, an attacker who exploited that bug would not gain WhatsApp's normal permissions or direct access to its private files.
Facebook and
Instagram
Facebook and Instagram both include services that load Chromium-based browser code into isolated processes. This lets browser work run without the app's normal permissions or direct access to its private files, using the same isolation mechanism as Chrome's workers.
Both apps declare this isolated browser service:
<service
android:name="com.facebook.browser.helium.content.SandboxedProcessService0"
android:process=":sandboxed_process0"
android:isolatedProcess="true"
android:exported="false"
android:useAppZygote="true" />In a 2022 post, Meta explained why it ships a Chromium-based WebView with Facebook: the engine can update with the app, and Facebook avoids crashes caused when Android replaces System WebView code already loaded into its process. Meta also described a separate GPU process in its controlled rendering path. Meta's Chromium-based WebView account.
Restart, mode change and recovery coordination
An activity or service in the main process stops when that process stops. Moving the restart code to another thread would not help: all of the process's threads stop together. A helper in a different process can stay alive long enough to reopen the app.
Discord
Discord uses a separate process to restart the app. Its ProcessPhoenix activity
stops the main process, relaunches the app, and then exits. Running separately
lets it finish the restart even after the main process has stopped. The main
process passes its process ID (PID) and relaunch instructions to the helper.
<activity
android:name="com.jakewharton.processphoenix.ProcessPhoenix"
android:process=":phoenix"
android:exported="false" />
Chrome
Chrome needs to restart to apply some changes, such as a new display language
or experimental settings changed in chrome://flags. When the user accepts the
language change or taps Relaunch, Chrome handles closing and reopening itself.
See its language-change code
and flags relaunch handler.
Its BrowserRestartActivity runs in a separate process so it can stop the main
browser process, relaunch Chrome, and then exit. Chromium's source documents why
it uses this helper instead of scheduling a restart with Android's AlarmManager:
the alarm-based approach introduced a minimum five-second wait.
Chromium BrowserRestartActivity source.
<activity
android:name="org.chromium.chrome.browser.BrowserRestartActivity"
android:process=":browser_restart_process"
android:exported="false" />
WhatsApp,
Maps, and
Snapchat
Starting a new activity does not reset the whole app. It still shares singletons, caches, database connections, and background tasks with the old activity, and some may still refer to the previous account. Restarting the process lets the app rebuild that in-memory state for the next account.
WhatsApp puts its account-switching activity in a separate process. My assumption
is that this lets it coordinate a clean restart without being stopped along
with the main process. The account-switching activity connects to a provider
in the main process, then relaunches WhatsApp with information about the switch.
WhatsApp also declares a separate RestartAppActivity for restarting the app.
<activity
android:name="com.whatsapp.backup.google.restart.RestartAppActivity"
android:process=":app_restart" />
<activity
android:name="com.whatsapp.accountswitching.secondaryprocess.AccountSwitchingActivity"
android:process=":account_switching" />Maps uses its restart helper during an Incognito-mode transition. The code
labels the request "Incognito v2 restart." and supplies an Incognito activity
to open afterward. The helper waits for the old process to exit before launching
that activity.
Opening another activity in the existing process would keep its shared objects and caches alive. My interpretation is that restarting lets Maps rebuild that in-memory state for Incognito mode. The separate helper can wait for shutdown and reopen Maps without being stopped along with the old process.
Maps declares its restart activity this way:
<activity
android:name="com.google.android.apps.gmm.base.activities.GmmSimpleRestartActivity"
android:process=":simple_restart_process"
android:exported="false" />Snapchat uses the same ProcessPhoenix library as Discord. Its helper stops
the old process, starts the activities needed to reopen the app, and then exits.
TikTok
TikTok has a more surprising restart path: it can replace its main process when configured memory checks indicate pressure. The fallback configuration in the APK disables this path; remote settings can enable it. When enabled, it checks time since launch, time in the background, and audio activity, then checks again that the app is not in the foreground before starting the restart helper.
The helper runs in a separate process. It kills the main process, brings it back by connecting to a content provider, and then exits. This rebuilds the process without opening the feed screen:
<service
android:name="com.ss.android.ugc.aweme.memory.kill.RestartApplicationService"
android:process=":memopt"
android:exported="false" />But why restart instead of releasing memory? Clearing caches and closing unused resources would avoid rebuilding the entire process. A restart can reclaim private memory that ordinary cleanup leaves behind, but the new process also allocates memory again. If it quickly grows back, the app has paid the startup cost for little lasting benefit. Repeating that work can also waste battery.
I am not sure why TikTok chose this approach. I did not find a cleanup attempt followed by a failure check in this restart path, so I cannot call it a last resort. It may be a practical workaround for particular devices or workloads, but without measurements of memory savings, crashes, and restart costs, I cannot say whether it is worth the complexity.
Uber
Uber also uses ProcessPhoenix. The restart path I traced appears to support
debugging: it applies configuration overrides and offers an action to restart
the app. Restarting lets the app initialize with the new values instead of
retaining objects created under the previous configuration. Although this code
ships in the production APK, I haven't established whether it is accessible to
ordinary users.
<activity
android:name="com.jakewharton.processphoenix.ProcessPhoenix"
android:process=":phoenix" />The helper runs only while coordinating the restart, then exits.
Letting a feature start and stop independently
A service can run without an activity being visible, and a background thread can keep slow work off the UI thread. Neither requires a second process. The extra process matters when the app also needs separate memory, initialization, or a place where a crash can occur without directly stopping the main process.
Facebook
Facebook runs its Instant Games screen in a separate process from the feed. It was previously reported that games running out of memory could crash the entire app. Separating the game gives it a process that can fail without necessarily taking the feed down, something a separate activity in the same process cannot provide. A preparation service can also start the process before the game screen opens. These choices fit the reported problems with crashes and startup delays, although I haven't found a published explanation of this exact implementation.
The game activity and preparation service share the process name :quicksilver:
<activity
android:name="com.facebook.quicksilver.webviewprocess.QuicksilverWebViewActivity"
android:process=":quicksilver"
android:exported="false" />
<service
android:name="com.facebook.quicksilver.webviewprocess.QuicksilverWarmupService"
android:process=":quicksilver"
android:exported="false" />Ending the game process can also release its private memory without restarting the feed. Closing the game activity alone does not guarantee that the process exits.
Instagram
Instagram declares a separate process for a lock-screen camera flow. Its launcher
checks for Samsung, Honor, and Oppo devices, and the camera activity can appear
over the lock screen. Availability also depends on a feature-eligibility check.
The camera activity and its shortcut activity share the process name :honolulu:
<activity
android:name="com.instagram.honolulu.activities.CameraActivity"
android:process=":honolulu" />
<activity
android:name="com.instagram.honolulu.activities.CameraShortcutActivity"
android:process=":honolulu" />This process uses a smaller application initialization path than the main app. My assumption is that this helps the camera open quickly for capturing a moment, but I couldn't measure its startup time. A separate activity in the main process would still use that process's application initialization and shared objects. These camera activities retain Instagram's normal permissions; the manifest does not make them an isolated security sandbox.
TikTok and
Maps
TikTok's download framework can hand tasks to a service in a separate process. A service in the main process could already keep downloading after the feed screen closes. The extra process instead gives the download engine a chance to continue if the main process crashes or restarts.
Why not WorkManager? WorkManager handles scheduling and retries, and it can also run workers in a separate process. TikTok uses its own download framework here, but I haven't established why it chose that framework or whether downloads actually survive a main-process restart.
<service
android:name="com.ss.android.socialbase.downloader.downloader.IndependentProcessDownloadService"
android:process=":downloader"
android:exported="false" />Maps puts an on-device training service in a separate process. The service loads its implementation dynamically, so the APK wrapper does not reveal which model it trains.
<service
android:name="com.google.android.gms.learning.internal.training.InAppTrainingService"
android:process=":learning_bg"
android:exported="false" />A background thread could run the computation without blocking the UI. A separate process additionally allows the training process to exit and release its memory without restarting navigation. It can also contain a process-local crash. These are plausible benefits, but I haven't confirmed Google's reason for the split or measured its memory impact.
Snapchat's keyboard
Snapchat packages a keyboard that Android can show while the user types in another app. An ordinary keyboard service could already do this without opening Snapchat's camera or messaging screens. Putting it in a separate process means it also does not have to share those features' process lifetime or memory. A crash in Snapchat's main process need not directly terminate the keyboard.
The keyboard and its settings activity share :snapkeyboard:
<service
android:name="com.snap.keyboard.lib.SnapKeyboardIME"
android:process=":snapkeyboard"
android:exported="true"
android:permission="android.permission.BIND_INPUT_METHOD" />
<activity
android:name="com.snap.keyboard.lib.rkr.simplekeyboard.inputmethod.latin.settings.SettingsActivity"
android:process=":snapkeyboard"
android:exported="false" />Why not WorkManager? A keyboard must respond as the user types. Android's
input-method framework
connects to its service and supplies input callbacks; this is an interactive
component, not a job to schedule for later. BIND_INPUT_METHOD protects that
connection so an ordinary app cannot bind directly to the keyboard service.
My assumption is that Snapchat wants the keyboard to start and operate independently of the main app. A smaller initialization path could help, but putting the service elsewhere does not automatically skip unrelated application setup. I haven't verified a startup or memory benefit. The keyboard retains Snapchat's normal permissions; this is not an isolated security sandbox.
YouTube and
Google Photos
YouTube keeps its playback components in the main app process and uses separate services to test what the device's graphics and media systems support.
For example, one service calls OpenGL graphics APIs; another creates a graphics
context and loads a native test library. Running a small test exercises the
actual device implementation instead of relying only on reported capabilities.
The inspected tests return their results and call stopSelf().
Why not a background thread and a try/catch? A thread can keep the UI responsive, and exception handling can deal with ordinary errors. But a fatal crash in native graphics code can terminate the process, including its player. A separate process gives the test a place to fail without directly terminating the player. YouTube must still handle the test failing to return a result.
The tests also release resources explicitly. Calling stopSelf() does not end
a service while clients remain bound, and ending a service does not necessarily
end its process. See Android's bound-service lifecycle.
Here is GlCapabilityCheckService, which checks OpenGL capabilities. The
diagram follows this test, rather than all of YouTube's device checks:
<service
android:name="com.google.android.apps.youtube.app.common.devicecapabilities.devicecapabilitytest.GlCapabilityCheckService"
android:process=":glcheck"
android:exported="false" />Google Photos uses a separate process to read information from media files,
such as video and motion-photo metadata. The main process sends the file's
location and the fields it needs. The service opens the file, reads those
fields on a background thread, and sends the results back. This worker is
MediaMetadataService; requests and replies use Android's Messenger API.
<service
android:name="com.google.android.apps.photos.mediametadataservice.MediaMetadataService"
android:process=":mediametadataservice"
android:exported="false" />Isn't reading metadata a small task? The result may be small, but getting it
requires parsing the media file. Photos uses Android's MediaMetadataRetriever
and MediaExtractor, which involve native media code. A malformed file or a
parser bug can therefore cause more than an ordinary Java exception.
Photos already does this work on a background thread. The separate process adds a boundary around a fatal parser crash, rather than merely moving work off the UI thread. Sending the file's location and returning selected fields also avoids copying the entire media file through the messaging interface.
Does that make a malicious file safe? It can help contain a crash, but this worker retains Photos' normal permissions. It does not restrict compromised code's access in the way WhatsApp's isolated image worker does.
I couldn't find a technical post explaining Google's reasons for these choices. My assumption is that failure containment motivates these narrow graphics and media workers. Releasing their process memory is another possible benefit, but I haven't verified when the processes exit or measured the savings.
Uber's identity providers
Uber exposes account and device-session information to other Uber apps through Android ContentProviders. A ContentProvider is an Android component that lets another app request or update data.
One provider supports single sign-on (SSO), which lets related apps share a sign-in. Another supports looking up device sessions. These are requests from another app for account information, not requests to open Uber's ride screen.
Does sharing data require a separate process? No. A ContentProvider in Uber's main process could already answer another app without opening an activity. Uber instead gives these providers their own processes, allowing Android to start the requested provider without starting the ride UI's process. My assumption is that this separates account lookup from that process's startup and failures. I haven't verified how much initialization it avoids or whether it makes requests faster.
<provider
android:name="com.uber.firstpartysso.provider.SSOContentProvider"
android:authorities="com.ubercab.provider.sso"
android:process=":ssoProvider"
android:exported="true" />
<provider
android:name="com.uber.identity.devicesessions.contentprovider.DeviceSessionsContentProvider"
android:authorities="com.ubercab.provider.ds"
android:process=":dsProvider"
android:exported="true" />authorities gives each provider the address other apps use to reach it.
exported="true" allows calls from other apps, so the provider must check who is
calling before returning account information.
Is the separate process what protects the account data? No. Uber's providers use caller identity supplied by Android and invoke validation before returning data. They do not simply trust a package name supplied in the request. I found those validation calls, but have not fully audited which callers they accept.
The providers retain Uber's normal permissions. Their access checks protect the data; the extra processes separate execution. They also add communication and state-coordination work, so this design is not automatically simpler than keeping the providers in the main process.
One process can be enough
Telegram, Spotify, and ChatGPT declare their own components in one app process, including services for calls, downloads, playback, and voice. Spotify even handles requests from other apps through a service in its main process.
These apps are a useful reminder: having several features, background tasks, or services does not itself require several processes.
Compare all 15 apps
This summarizes the examined APKs and traced code. The roles below describe what the components do, rather than assuming why each company chose the split.
| App | Selected components | What the evidence shows |
|---|---|---|
| Web content, graphics, networking, and media workers | Web-content services include both ordinary and isolated variants; only the isolated variants remove app permissions. | |
| Sandboxed browser workers, image decoder, and restart helper | Restricts worker access; returns decoded images through shared memory; uses a separate activity to restart the browser. | |
| Isolated browser workers and Instant Games | Restricts browser-worker access. The game activity and preparation service share an ordinary process separate from the feed. | |
| Isolated browser workers and lock-screen camera activities | Restricts browser-worker access. The camera uses a smaller initialization path; faster startup remains unmeasured. | |
| Download service and memory-triggered restart helper | Can assign downloads to another process. A configurable restart path replaces the main process without opening the feed. | |
| On-device training service and restart helper | Loads training code in a separate process; uses the restart helper during an Incognito-mode transition. | |
| Graphics and media capability-test services | Runs device tests outside the process hosting playback components; these services are not isolated sandboxes. | |
| Keyboard and restart helper | Runs the keyboard in its own process with normal app permissions; ProcessPhoenix handles app restarts. | |
| Media metadata service | Reads media files and returns selected metadata; the worker retains normal app permissions. | |
| SSO and device-session providers; restart helper | Validates cross-app account requests in provider processes. A configuration tool uses ProcessPhoenix to restart the app. | |
| Isolated JPEG compressor, account-switching activity, and restart helper | Compresses decoded bitmaps with restricted access; separate activities handle account-switch relaunches and restarts. | |
| ProcessPhoenix restart helper | Stops the main process, relaunches the app, then exits. | |
| No separate process declared for its own components | Its declared services share the main app process. | |
| No separate process declared for its own components | Playback, download, and cross-app services share the main app process. | |
| No separate process declared for its own components | Voice and other declared services share the main app process. |
The cost of multi-process architecture
A separate process can restrict access, contain a crash, or finish restarting the app. But it turns a local operation into work shared between two independently running processes. That adds costs in three places.
Startup and memory
Starting a worker can mean loading libraries, creating threads, and running app initialization again. Each process has its own caches and app objects, so moving work out of the main process can make that process smaller while increasing the app's total memory use.
Starting the worker early can reduce the user's wait, but keeps its memory allocated for longer. Starting it on demand avoids that idle cost but adds a startup delay. Ending the worker releases its private memory; merely stopping its service does not guarantee that the process exits.
Communication and data transfer
A worker cannot directly use objects in the main process's memory. The app must send the inputs it needs through IPC and return the result. Converting objects into messages and copying data takes time; a synchronous call can also block the calling thread while the worker responds.
Large images or videos make those copies expensive, and Binder has a limited transaction buffer. File descriptors and shared memory can avoid copying the payload into messages, but the app still needs to agree on who writes the data, who reads it, and when to release it.
State and recovery
Each process has its own singletons and in-memory state. If the user switches accounts or changes a setting, updating the main process does not automatically update a worker. The app needs a clear owner for shared state and a way to keep the other process current.
Either process can also die during a request. A missing reply does not tell the caller whether the work failed or finished just before the worker stopped. Retries must avoid duplicating completed work, and anything needed for recovery must survive outside the process's memory.
Before adding a process, name the problem it solves. Then measure startup time, communication costs, and total memory across all processes, and test what happens when either side dies.
Where to go deeper
For the Android mechanics behind these examples, start with multi-process architecture: process identity, state ownership, and recovery after process death. The IPC guide covers Binder, AIDL, and communication between processes.
Read the full Android System Design interview prep course
Core concepts. Full system designs. Guided practice.
