基础回顾
| 维度 | 进程 | 线程 |
|---|---|---|
| 定义 | 操作系统资源分配的基本单位 | CPU调度的基本单位 |
| 内存空间 | 独立内存空间,相互隔离 | 共享进程的内存空间 |
| 通信方式 | IPC(Binder、Socket、共享文件等) | 共享内存、Handler等 |
| 创建开销 | 大(需分配独立内存空间) | 小(共享进程资源) |
| Android 体现 | 每个应用默认一个进程,可配置多进程 | 主线程(UI线程)+ 工作线程 |
Android进程
Android基于Linux内核,但有自己的进程管理策略
xml
// 在AndroidManifest.xml中声明进程
<application
android:process=":remote"> <!-- 冒号表示私有进程 -->
<!-- 默认情况下,所有组件运行在application指定的进程中 -->
<activity android:name=".MainActivity" />
<!-- 可以为特定组件指定不同进程 -->
<service
android:name=".RemoteService"
android:process=":background" />
<activity
android:name=".WebViewActivity"
android:process=":webview" />
</application>
默认情况下,一个Android应用运行在一个进程中。这个进程的名称通常就是应用的包名。
虽然默认是单进程,但在以下场景中需要考虑多进程:
- 规避内存限制
xml
<!-- 在AndroidManifest中为组件指定独立进程 -->
<application>
<!-- 主进程,负责UI -->
<activity android:name=".MainActivity" />
<!-- 独立进程,处理内存密集型任务 -->
<service
android:name=".ImageProcessingService"
android:process=":image_process" />
</application>
每个进程有独立的内存空间。Android为每个进程设置内存上限(比如256MB)。如果应用需要处理大图、视频等,可以放到独立进程,避免主进程OOM。
- 独立运行的组件
kotlin
// 音乐播放器应用:播放服务运行在独立进程
// 这样即使主进程崩溃,音乐还能继续播放
class MusicService : Service() {
override fun onCreate() {
super.onCreate()
Log.d("MusicService", "运行在独立进程,进程名: ${getProcessName()}")
}
}
使用场景例如:播放器,音乐播放不受UI进程影响;下载器,下载任务独立运行;推送服务,保持长连接,与UI逻辑分离。
- 隔离不稳定模块
xml
<!-- WebView独立进程,避免崩溃影响主应用 -->
<activity
android:name=".WebViewActivity"
android:process=":webview" />
WebView、第三方SDK等可能不稳定的模块,放在独立进程可以避免崩溃影响主应用。
- 提升性能(谨慎使用)
xml
<!-- 将频繁GC的后台任务放到独立进程 -->
<service
android:name=".DataSyncService"
android:process=":sync" />
如果某个组件频繁创建/销毁对象导致GC,会影响UI流畅度。独立进程后,GC只影响该进程。
多进程优缺点
| 优点 | 说明 | 示例 |
|---|---|---|
| 内存隔离 | 每个进程独立内存空间,可突破单进程内存限制 | 图片编辑应用:UI进程 + 图片处理进程 |
| 稳定性提升 | 一个进程崩溃不影响其他进程 | WebView崩溃不会导致整个应用退出 |
| 安全隔离 | 敏感操作可在独立进程进行 | 支付模块运行在独立进程 |
| 性能优化 | 可利用多核CPU并行计算 | 视频转码应用使用多进程并行处理 |
| 缺点 | 说明 | 解决方案 |
|---|---|---|
| 通信复杂 | 进程间不能直接共享内存,需要IPC | 使用Binder、Messenger、AIDL等 |
| 开销增大 | 每个进程都有独立的内存、类加载等开销 | 谨慎创建,避免过多进程 |
| 数据同步难 | 共享数据需要额外机制 | 使用ContentProvider、文件、SharedPreferences(MODE_MULTI_PROCESS已废弃) |
| 调试困难 | 多进程调试复杂,需附加到不同进程 | 使用Android Studio的多进程调试功能 |
Android进程的创建过程
Zygote:所有应用的"母体"

当系统需要启动一个新的应用时,如果为每个应用都从头初始化整个运行环境(包括加载系统资源、类库等),会非常耗时。Zygote进程就是为了解决这个问题而设计的。
Zygote进程在系统启动时被创建,它已经完成了:
- 初始化虚拟机(DVM/ART)
- 加载系统资源
- 预加载Android框架的类和资源
当需要启动新应用时,Zygote会fork出一个子进程。这个子进程会继承Zygote进程已经加载的系统资源和类,这样新进程就无需重新加载这些,从而大大加快了启动速度。
fork是Unix/Linux系统中创建新进程的系统调用。传统上,fork会复制父进程的整个地址空间给子进程。但是,如果父进程很大,那么复制整个地址空间将会非常耗时,而且子进程可能很快就会通过exec系统调用加载一个新的程序,这样刚才复制的地址空间就被完全替换了,导致复制操作白费了。
为了解决这个问题,现代操作系统(包括Linux)实现了写时复制(Copy-on-Write,简称COW)技术。其核心思想是:当父进程调用fork创建子进程时,内核并不复制整个进程的地址空间,而是让子进程和父进程共享相同的物理内存页。但是,这些内存页会被标记为写时复制。也就是说,当父进程或子进程试图修改某个内存页时,内核才会复制这个页,然后让修改方拥有这个页的私有副本。
这样,fork操作就变得非常轻量,因为只需要复制父进程的页表(一种数据结构,用于将虚拟地址映射到物理地址),而不需要复制实际的物理内存页。子进程几乎可以立即开始运行。
进程优先级:Android如何管理进程生死
Android根据进程的重要性动态调整优先级:
java
// ProcessList.java中定义的进程优先级
// 数字越小优先级越高
static final int FOREGROUND_APP_ADJ = 0; // 前台应用
static final int VISIBLE_APP_ADJ = 100; // 可见应用
static final int PERCEPTIBLE_APP_ADJ = 200; // 可感知应用(如播放音乐)
static final int BACKUP_APP_ADJ = 300; // 备份进程
static final int CACHED_APP_MIN_ADJ = 350; // 缓存应用(最低)
static final int CACHED_APP_MAX_ADJ = 400;
应用启动 → 前台进程(最高优先级)
用户按Home键 → 可见进程(次高优先级)
转到其他应用 → 服务进程(如果有Service)
长时间在后台 → 缓存进程(可能被回收)
Android的进程回收机制主要通过 Low Memory Killer (LMK) 和 OOM Adj评分系统来实现智能管理。
java
// 简化版LMK内核代码逻辑(kernel/drivers/staging/android/lowmemorykiller.c)
static int lowmemory_killer(void *arg) {
while (!kthread_should_stop()) {
// 1. 检查当前内存压力
int other_free = global_page_state(NR_FREE_PAGES);
int other_file = global_page_state(NR_FILE_PAGES);
// 2. 遍历进程,计算"badness"分数
for_each_process(p) {
// 跳过不可杀进程(init、kthread等)
if (p->flags & PF_KTHREAD)
continue;
// 获取进程的oom_score_adj(用户空间设置)
int oom_score_adj = p->signal->oom_score_adj;
if (oom_score_adj == OOM_SCORE_ADJ_MAX)
continue;
// 3. 计算综合分数
points = oom_badness(p, NULL, nodemask, totalpages);
// 4. 找到最"坏"的进程
if (points > selected_points) {
selected = p;
selected_points = points;
}
}
// 5. 如果内存紧张,杀掉选中的进程
if (selected) {
send_sig(SIGKILL, selected, 0);
}
// 6. 休眠,等待下一次检查
schedule_timeout_interruptible(poll_interval);
}
return 0;
}