One Android app. Why so many processes?

I looked inside fifteen major Android apps to see what they run separately, and why.

Rotem MeidanBy Rotem Meidan · · 16 min read
One Android app. Why so many processes? - I looked inside fifteen major Android apps to see what they run separately, and why.

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 from isolatedProcess="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" />

Chromium ImageDecoder source.

Rendering diagram...

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.

Rendering diagram...

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.

Rendering diagram...

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.

Rendering diagram...

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" />
Rendering diagram...

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" />
Rendering diagram...

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" />
Rendering diagram...

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.

Rendering diagram...

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.

Rendering diagram...

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.

Rendering diagram...

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" />
Rendering diagram...

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.

Rendering diagram...

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.

Rendering diagram...

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.

AppSelected componentsWhat the evidence shows
FirefoxWeb content, graphics, networking, and media workersWeb-content services include both ordinary and isolated variants; only the isolated variants remove app permissions.
ChromeSandboxed browser workers, image decoder, and restart helperRestricts worker access; returns decoded images through shared memory; uses a separate activity to restart the browser.
FacebookIsolated browser workers and Instant GamesRestricts browser-worker access. The game activity and preparation service share an ordinary process separate from the feed.
InstagramIsolated browser workers and lock-screen camera activitiesRestricts browser-worker access. The camera uses a smaller initialization path; faster startup remains unmeasured.
TikTokDownload service and memory-triggered restart helperCan assign downloads to another process. A configurable restart path replaces the main process without opening the feed.
MapsOn-device training service and restart helperLoads training code in a separate process; uses the restart helper during an Incognito-mode transition.
YouTubeGraphics and media capability-test servicesRuns device tests outside the process hosting playback components; these services are not isolated sandboxes.
SnapchatKeyboard and restart helperRuns the keyboard in its own process with normal app permissions; ProcessPhoenix handles app restarts.
Google PhotosMedia metadata serviceReads media files and returns selected metadata; the worker retains normal app permissions.
UberSSO and device-session providers; restart helperValidates cross-app account requests in provider processes. A configuration tool uses ProcessPhoenix to restart the app.
WhatsAppIsolated JPEG compressor, account-switching activity, and restart helperCompresses decoded bitmaps with restricted access; separate activities handle account-switch relaunches and restarts.
DiscordProcessPhoenix restart helperStops the main process, relaunches the app, then exits.
TelegramNo separate process declared for its own componentsIts declared services share the main app process.
SpotifyNo separate process declared for its own componentsPlayback, download, and cross-app services share the main app process.
ChatGPTNo separate process declared for its own componentsVoice 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.

Share

Keep reading

You just read the theory. Now practice the interview.

AI-guided mock interviews on a live canvas. Real system design problems, graded like the real thing.

Discussion