Android启动流程Binder机制AMS详解 面试高频问题与真实开发案例分析
写在前面
每次面试被问到”Android是怎么启动的”,很多人脑子里就像一团浆糊,要么只说一个Zygote,要么把Binder和AMS搅在一起说。今天咱们就掰开揉碎,把这条主线彻底理清。
一、Android启动流程:从内核到AMS
1.1 整个链条的骨架
Android的启动是一环扣一环的,你可以把它想象成一列接力赛,每一棒都有明确的任务:
内核启动 → init进程 → Zygote → SystemServer → AMS等系统服务 → Launcher桌面
这可不是我编的,每一个环节都有迹可循,我们可以用 ps -A | grep 命令在真机上验证。
1.2 第一阶段:内核加载
Android底层是Linux内核,开机第一件事就是内核把自己加载进内存,然后挂载根文件系统。内核做完这些之后,会启动第一个用户态进程 —— init。
这个进程在系统里PID等于1,它是所有用户进程的”老祖宗”。你可以在设备上执行:
adb shell ps -A | head -20
你会看到第一行就是 init,这说明它确实是PID 1。
1.3 init进程的初始化工作
init进程启动后会读取两个配置文件:
/init.rc—— 系统级配置/init.project.rc—— 项目级配置
init会在这里干几件重要的事:
1)创建关键目录和节点
# init.rc 中的片段
mkdir /system 0755 root root
mkdir /data 0771 system cache
mount yaffs2 mtd@system /system
2)启动关键守护进程
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
看到没?Zygote进程的启动命令就在这里定义。
3)启动logd日志守护进程
service logd /system/bin/logd
socket logd stream 660 root log
socket logdr tuple
socket logdcompress dns
class main
critical
seclabel u:r:logd:s0
这一层init的工作做完了,下一棒就交给Zygote。
二、Zygote:万物之源
2.1 为什么叫Zygote?
Zygote在生物学中是”受精卵”的意思,在Android里它就是所有App进程的”受精卵”。为什么这么设计?
因为fork比exec快得多。
fork一个进程只需要复制父进程的页表,子进程和父进程共享大部分内存(写时复制机制)。而exec需要重新加载整个程序,速度慢得多。Zygote预先加载了大量公共类和资源,子进程fork下来就能直接用,省去了重复加载的开销。
2.2 Zygote的启动流程
Zygote进程是由app_process启动的,它的入口代码在:
frameworks/base/cmds/app_process/app_main.cpp
main函数执行后,会走以下核心逻辑:
// app_main.cpp 简化逻辑
int main(int argc, char* const argv[])
{
// 解析命令行参数
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// 注册Zygote
runtime.addVmArguments();
// 判断是Zygote还是其他模式
if (startSystemServer || !Runtime::kIsZygote) {
// 启动SystemServer
} else {
// 作为Zygote运行,等待请求
}
runtime.start("com.android.internal.os.ZygoteInit", args);
return 0;
}
2.3 Zygote的核心初始化
ZygoteInit.main() 方法里做了三件最重要的事:
1)注册Socket端口
public static void main(String argv[]) {
ZygoteServer zygoteServer = new ZygoteServer();
// 创建本地Socket Server
// 端口固定为 /dev/socket/zygote
LocalServerSocket zygoteServerSocket =
new LocalServerSocket(ZYGOTE_SOCKET_ADDRESS);
// 注册到Native层
registerZygoteSocket(zygoteServerSocket);
}
这样当AMS需要创建新进程时,就能通过这个Socket发送fork请求。
2)预加载类和资源
// preloading
preload();
preloadClasses();
preloadResources();
preloadLibraries();
preloadSharedLibraries();
preloadNativeLibraries();
你在设备上看Zygote占用的内存大小,会发现它比普通的App进程大得多,这就是预加载带来的”资产”。
3)启动SystemServer
// 启动SystemServer子进程
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
if (r != null) {
r.run();
return;
}
}
forkSystemServer() 通过fork子进程并调用exec来执行SystemServer的Java代码,子进程退出后,父进程(Zygote)进入等待请求的死循环。
三、SystemServer:系统服务的摇篮
3.1 SystemServer是什么
SystemServer是一个Java进程,它运行在Zygote fork出来的子进程中,负责启动所有核心系统服务。它的入口类是:
com.android.server.SystemServer
3.2 SystemServer的启动
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
// 1. 初始化Looper
Looper.prepareMainLooper();
Looper.getMainLooper().setSlowLogThresholdMs(
SLOW_DISPATCH_THRESHOLD_MS, SLOW_RENDER_THRESHOLD_MS);
// 2. 启动引导服务
SystemServerInitThreadPool.get().submit(
() -> mSystemServiceManager.startBootPhase(
TIMING_STARTUP, SystemServiceManager.PHASE_WAIT_FOR_DEFAULT_DISPLAY));
// 3. 启动核心服务
mSystemServiceManager.startService(TelephonyRegistryService.class);
mSystemServiceManager.startService(DeviceHealthService.class);
// ... 更多服务
// 4. 启动AMS
mSystemServiceManager.startService(AmService.class);
// 5. 启动Launcher
startOtherServices();
// 6. 进入消息循环
Looper.loop();
}
3.3 关键服务启动顺序
SystemServer启动服务有严格的顺序要求,因为很多服务之间有依赖关系:
PowerManagerService(电源管理)
↓
PackageManagerService(包管理)
↓
ActivityManagerService(Activity管理)
↓
WindowManagerService(窗口管理)
↓
InputManagerService(输入管理)
↓
Launcher(桌面)
你可以这样理解:AMS需要先知道有哪些App(PackageManagerService),才能管理它们的Activity。
四、Binder机制:Android的通信命脉
4.1 为什么需要Binder
Android是基于Linux的系统,进程之间通信(IPC)有几种方式:管道、Socket、共享内存、Messenger、AIDL等。
为什么偏偏选Binder?
性能高:Binder使用内存映射(mmap),数据不需要在用户态和内核态之间来回拷贝,效率远高于Socket。
安全:Binder有UID和PID的概念,每个进程有唯一标识,通信双方可以验证对方身份。
简单:Binder把通信的复杂性封装在内核驱动里,开发者只需关注接口定义。
4.2 Binder的架构
Binder分为四部分:
┌─────────────────────────────────┐
│ 用户态 │
│ ┌──────────┐ ┌──────────┐ │
│ │ Client │ │ Server │ │
│ │ (应用) │ │ (服务) │ │
│ └────┬─────┘ └────┬─────┘ │
│ │ │ │
│ ┌────▼──────────────▼────┐ │
│ │ Binder驱动框架 │ │
│ └─────────────────────────┘ │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 内核态 │
│ ┌──────────────────────────┐ │
│ │ Binder驱动 │ │
│ │ (binder.c / binder.c) │ │
│ └──────────────────────────┘ │
└─────────────────────────────────┘
内核部分:/drivers/android/binder.c,负责内存映射、事务传递、权限检查。
用户态部分:
- Binder驱动框架:
libbinder、libutils等 - Client:调用方,通过代理对象发起调用
- Server:服务方,实现具体逻辑
4.3 Binder的核心概念
1)IBinder接口
所有Binder通信的基础:
// android/os/IBinder.java
public interface IBinder {
boolean transact(int code, Parcel data, Parcel reply, int flags);
IInterface queryLocalInterface(String descriptor);
void linkToDeath(DeathRecipient recipient, int flags);
boolean unlinkToDeath(DeathRecipient recipient, int flags);
}
2)IInterface
定义服务契约的接口:
// IActivityManager.java
public interface IActivityManager extends IInterface {
ActivityInfo startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int flags,
ProfilerInfo profilerInfo, Bundle options) throws RemoteException;
}
3)Stub和Proxy
这是Binder通信的核心模式,以AMS为例:
// 服务端Stub
public abstract class ActivityManagerNative extends Binder implements IActivityManager {
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
switch (code) {
case START_ACTIVITY_TRANSACTION:
// 从data中读取参数
Intent intent = Intent.CREATOR.createFromParcel(data);
// 调用具体实现
ActivityInfo info = startActivity(...);
// 写入返回结果
info.writeToParcel(reply, 0);
return true;
}
return super.onTransact(code, data, reply, flags);
}
}
// 实际服务端实现
public class ActivityManagerService extends ActivityManagerNative {
@Override
public ActivityInfo startActivity(...) {
// 真实逻辑
return info;
}
}
// 客户端Proxy
public class ActivityManagerProxy implements IActivityManager {
private IBinder mRemote;
public ActivityManagerProxy(IBinder remote) {
mRemote = remote;
}
@Override
public ActivityInfo startActivity(...) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
// 写入参数
intent.writeToParcel(data, 0);
// 发起跨进程调用
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
// 读取返回结果
return ActivityInfo.CREATOR.createFromParcel(reply);
} finally {
data.recycle();
reply.recycle();
}
}
}
4)transact方法
// Binder.java
public boolean transact(int code, Parcel data, Parcel reply, int flags) {
// 把Parcel对象序列化,通过ioctl系统调用写入Binder设备
return transactNative(code, data, reply, flags);
}
transact是整个机制的精髓 —— 它把数据打包成Parcel,然后通过Linux内核的ioctl调用,直接把数据写入Binder驱动,驱动负责找到对端进程并把数据传递过去。
4.4 Binder驱动工作流程
用一个具体例子说明:App A调用AMS的startActivity
App A (Client) Binder驱动 AMS (Server)
│ │ │
│ transact() │ │
│ ──────────────────────────────────>│ │
│ (写入Parcel到内存映射区) │ │
│ │ (内核态处理) │
│ │ ──────────────────────────>│
│ │ (唤醒AMS所在进程的Binder线程)│
│ │ │ onTransact()
│ │ │ (执行逻辑)
│ │ <─────────────────────────│
│ <─────────────────────────────────│ │
│ (从Binder线程池拿到返回结果) │ │
│ │ │
4.5 Binder线程池
AMS处理请求用的是线程池,不是主线程,因为调用量可能很大:
// ActivityManagerService中的线程池
private final Thread mServiceThread = new Thread(
"ActivityManagerService", Process.THREAD_PRIORITY_FOREGROUND) {
@Override
public void run() {
// 创建Looper
Looper.prepareMainLooper();
mUiThreadHandler = new Handler(Looper.myLooper());
// 进入循环
Looper.loop();
}
};
同时还有一个后台Binder线程池:
// Process.java 中定义的Binder线程数
public static final int THREAD_PRIORITY_FOREGROUND = -2;
// 系统默认Binder线程数 = 16
当AMS收到跨进程调用时,Binder驱动会从线程池里找一个空闲线程来处理,这样就能并发处理多个请求。
五、AMS详解:Android的管家
5.1 AMS是什么
AMS全称ActivityManagerService,是Android系统中最核心的服务之一。它管理着四大组件的生命周期、进程调度、内存管理、任务栈等。
5.2 AMS在SystemServer中的启动
// SystemServer.java
private void startBootstrapServices() {
// 1. 启动ActivityManagerService
mActivityManagerService = ActivityManagerService.Lifecycle.startService(
mSystemServiceManager);
// 2. 启动PackageManagerService
mPackageManagerService = PackageManagerService.main();
// 3. 启动PackageManagerService后,AMS需要引用它
mActivityManagerService.setSystemProcess();
}
private void startOtherServices() {
// 更多服务启动
mActivityManagerService.installSystemProviders();
mActivityManagerService.startObservingNativeCrashes();
// 启动Launcher
mPackageManagerService.systemReady();
}
5.3 AMS的核心职责
1)Activity生命周期管理
当你调用startActivity()时,最终走到AMS:
// Activity.java
public void startActivity(Intent intent) {
// ...
startActivity(intent, null);
}
public void startActivity(Intent intent, @Nullable Bundle options) {
// 通过Instrumentation转给AMS
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(),
mToken, this, intent, requestCode, options);
// ...
}
mMainThread.getApplicationThread() 返回的是一个 ApplicationThread 对象,它是IApplicationThread.Stub的实现,本质就是一个Binder代理,把请求转发给AMS。
2)进程管理
AMS维护着系统中所有进程的状态:
// ProcessRecord.java 描述了进程信息
final class ProcessRecord {
String processName; // 进程名
int pid; // 进程ID
int uid; // 用户ID
int oomAdj; // 内存回收优先级
int lastPss; // 上次 PSS 大小
List<ActivityRecord> activities; // 该进程中的Activity
IApplicationThread app; // 进程的ApplicationThread
}
AMS维护了一个进程列表:
// ActivityManagerService.java
final ArrayMap<String, ProcessRecord> mProcessNames = new ArrayMap<>();
final HashMap<String, ArrayList<ProcessRecord>> mProcessMap = new HashMap<>();
final ArrayList<ProcessRecord> mProcessesReady = new ArrayList<>();
3)内存管理
AMS会根据进程的oomAdj值来决定内存回收优先级:
// 进程状态与oomAdj映射
// FOREGROUND_APP = 0 前台进程
// VISIBLE_PROCESS = 1 可见进程
// PERCEPTIBLE_PROCESS = 2 可感知进程
// SERVICE_PROCESS = 3 服务进程
// BACKUP_APP = 4 备份进程
// HOME_APP = 5 Home进程
// LAST_ACTIVE_RECENT = 6 最近使用
// RECENT_TASK = 7 最近任务
// HIDDEN_APP = 8 隐藏进程
// CONTENT_PROVIDER = 9 内容提供者
// EMPTY_APP = 10 空进程
// CACHED_APP_MAX = 13 缓存进程上限
// CACHED_APP_MIN = 15 缓存进程下限
当系统内存不足时,AMS从oomAdj值最大的进程开始回收。
5.4 AMS处理startActivity的完整流程
这是一个高频考点,我们来走一遍:
用户点击按钮
│
▼
Activity.startActivity()
│
▼
Instrumentation.execStartActivity()
│
▼
IApplicationThread.scheduleLaunchActivity()
│ (通过Binder跨进程调用)
▼
ApplicationThread (AMS内部)
│
▼
ActivityManagerService.startActivity()
│
▼
ActivityStackSupervisor.startActivityLocked()
│
▼
ActivityStack.startActivityLocked()
│
▼
ActivityStack.resumeTopActivityLocked()
│
▼
如果目标进程未启动:
mProcessStartThread.start() → Zygote fork新进程
如果目标进程已启动:
mAllThread.scheduleLaunchActivity()
│
▼
新进程中的ApplicationThread.scheduleLaunchActivity()
│
▼
ActivityThread.handleLaunchActivity()
│
▼
ActivityThread.performLaunchActivity() → 创建Activity
│
▼
ActivityThread.handleResumeActivity() → onResume()
六、面试高频问题
6.1 Android启动流程完整链路
问:请描述Android系统的完整启动流程。
这是一个非常经典的问题,很多求职者只能说出Zygote和SystemServer,但完整答案应该包括:
1. 开机按下电源键
└─ 引导加载程序(Bootloader)启动
2. 内核(Kernel)加载
└─ 挂载根文件系统
└─ 启动init进程(PID=1)
3. init进程
└─ 解析init.rc配置
└─ 启动Zygote进程
└─ 创建/dev/socket/zygote Unix域套接字
└─ 启动SurfaceFlinger、Radio等守护进程
└─ 触发system_ready广播
4. Zygote进程
└─ 初始化Java虚拟机
└─ 注册Zygote Socket
└─ 预加载类和资源
└─ Fork SystemServer进程
5. SystemServer进程
└─ 启动核心服务(AMS、PMS、WMS等)
└─ 完成所有服务启动后发出SYSTEM_READY广播
6. Launcher启动
└─ 接收SYSTEM_READY广播
└─ 启动桌面
└─ 展示桌面图标
追问:SystemServer是如何被Zygote启动的?
SystemServer是通过fork()创建的子进程。Zygote在启动时通过forkSystemServer()方法fork出一个子进程,子进程中通过exec()加载SystemServer的Java类。子进程执行完毕后退出,父进程(Zygote)继续等待新的fork请求。
6.2 Binder机制
问:为什么Android选择Binder作为主要的IPC机制?
标准答案应该包含以下几个维度:
1. 性能优势
- 内存映射(mmap):数据只需一次拷贝
- 相比Socket:避免了用户态↔内核态的多次拷贝
- 相比共享内存:需要额外的同步机制,Binder内核自动处理
2. 安全性
- 基于UID/PID的权限校验
- 调用方身份可被接收方验证
- 防止进程冒充
3. 设计简洁
- 统一的接口:IBinder
- AIDL自动生成Stub/Proxy代码
- 客户端无需关心服务端实现细节
4. 高效的事务模型
- transact()一次性传递完整请求
- 支持同步和异步调用
- 支持回调(onTransact)
问:Binder的C/S架构是怎样的?
C/S架构:
Client进程 Server进程
┌──────────────┐ ┌──────────────┐
│ │ │ │
│ Proxy │◄────Binder──────►│ Stub │
│ (客户端代理) │ 驱动 │ (服务端骨架) │
│ │ │ │
│ IInterface │ │ IInterface │
│ (接口定义) │ │ (接口定义) │
│ │ │ │
└──────┬───────┘ └──────┬───────┘
│ │
└─────────── Binder驱动 ───────────┘
(内核态)
Client通过Proxy对象发起调用,Proxy把参数打包成Parcel,调用transact(),Binder驱动负责把请求转发给Server端的Stub,Stub调用onTransact()执行实际逻辑,把结果写回Parcel,最终Client从reply中读取结果。
问:Binder内存映射的原理是什么?
// binder驱动的核心数据结构
struct binder_transaction {
struct binder_proc *sender; // 发送方进程
struct binder_proc *target; // 接收方进程
void *buffer; // 映射的内存缓冲区
size_t data_size; // 数据大小
size_t offsets_size; // 偏移量大小
struct binder_object objects[]; // 对象数组
};
关键点是:buffer指向的内存是内核空间映射到两个进程的虚拟地址空间的同一块物理内存。当Client调用transact()时,数据被拷贝到内核的buffer中,然后映射到Server进程的地址空间。Server读取时不需要额外的系统调用,直接访问映射的内存即可。
这就是为什么Binder比Socket快 —— Socket需要在用户态和内核态之间来回拷贝数据,而Binder直接在内核态完成映射,一次拷贝就能让两个进程共享数据。
6.3 AMS相关
问:AMS是什么?它管理哪些内容?
AMS(ActivityManagerService)是Android系统中最核心的系统服务之一,
它继承自ActivityManagerNative(Binder的Stub实现),
运行在SystemServer进程中。
AMS主要管理:
├── Activity生命周期(启动、暂停、恢复、销毁)
├── Task(任务栈)管理
├── 进程状态管理(前台、后台、缓存等)
├── 进程OOM_adj值管理
├── ContentProvider的创建和销毁
├── Broadcast(广播)的发送和接收
├── 服务(Service)的生命周期管理
└── 内存回收策略
问:Activity启动的完整流程?
这个问题需要把前面第五节的流程用更详细的代码方式表达出来:
// 1. Activity.startActivity()
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// 注意:这里仍然调用startActivityForResult
startActivityForResult(intent, -1);
}
}
// 2. Instrumentation.execStartActivity()
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
// 获取AMS的Binder引用
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
return null;
}
// 3. AMS.startActivity() → ActivityStackSupervisor.startActivityLocked()
final int startActivityLocked(IApplicationThread caller, ...) {
// 解析Intent
// 检查权限
// 创建或复用Task
// 创建ActivityRecord
// 如果需要启动新进程
if (app == null || app.thread == null) {
// 通过Process.start()启动进程
Process.start(processName, ...);
}
// 调用目标进程的ApplicationThread
app.thread.scheduleLaunchActivity(...);
}
// 4. ApplicationThread.scheduleLaunchActivity()
public final void scheduleLaunchActivity(Intent intent, ...) {
// 发送消息给主线程Handler
sendMessage(H.LAUNCH_ACTIVITY, r);
}
// 5. ActivityThread.handleLaunchActivity()
private void handleLaunchActivity(ActivityClientRecord r, ...) {
// 创建Activity
Activity activity = performLaunchActivity(r, customIntent);
if (activity != null) {
// 触发onResume
handleResumeActivity(r.token, false, r.isForward, ...);
}
}
问:AMS是如何保证Activity在主线程中创建和回调的?
这是Binder IPC机制的关键设计 —— ApplicationThread是AMS在App进程中的Binder对象,当AMS通过Binder调用scheduleLaunchActivity()时,这个调用是同步的。但ActivityThread内部维护了一个Handler,它在收到消息后切换到主线程执行:
// ActivityThread内部的主线程Handler
final H mH = new H() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
// 此时已经在主线程
ActivityClientRecord r = (ActivityClientRecord) msg.obj;
Activity activity = performLaunchActivity(r, null);
handleResumeActivity(r.token, ...);
break;
}
case RESUME_ACTIVITY: {
// 执行onResume
performResumeActivity(...);
break;
}
}
}
};
七、真实开发案例分析
7.1 案例一:为什么我的Service启动这么慢?
背景:某APP的后台Service经常启动超时,被系统kill掉。
排查思路:
# 1. 查看进程状态
adb shell ps -A | grep com.example.app
# 2. 查看AMS日志
adb logcat -b all | grep ActivityManager
# 3. 查看Service启动耗时
adb shell am startservice -p com.example.app \
-n com.example.app/.MyService
问题根源:Service的onCreate()里做了大量同步网络请求,阻塞了Binder线程池。
// 错误的写法
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
// 直接在这里做网络请求!
String data = networkRequest(); // 阻塞Binder线程
saveData(data);
}
}
修复方案:
public class MyService extends Service {
private HandlerThread mWorkerThread;
private Handler mWorkerHandler;
@Override
public void onCreate() {
super.onCreate();
// 使用独立的Worker线程
mWorkerThread = new HandlerThread("MyServiceWorker");
mWorkerThread.start();
mWorkerHandler = new Handler(mWorkerThread.getLooper());
mWorkerHandler.post(() -> {
String data = networkRequest();
saveData(data);
});
}
}
深入分析:Service运行在Binder线程池,而AMS的Binder线程池大小固定(默认16个)。如果大量Service在onCreate()里做耗时操作,会阻塞线程池,导致其他进程调用AMS超时。
7.2 案例二:App进程被回收后如何恢复?
背景:用户返回桌面后,App进程被系统回收,再次进入时发现数据丢失。
问题诊断:
# 查看进程OOM_adj值
adb shell cat /proc/<pid>/oom_adj
# 查看回收记录
adb logcat | grep "ActivityManager" | grep "Kill"
解决方案:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 在进程重启时恢复状态
restoreState();
}
// 关键:在onSaveInstanceState中保存状态
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable("key", importantData);
}
// 关键:在onRestoreInstanceState中恢复状态
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
importantData = savedInstanceState.getParcelable("key");
}
}
更深入的理解:
进程被回收后重新启动,onCreate()会被调用,但onSaveInstanceState可能没有被调用(进程直接被kill)。因此正确的做法是:
// 使用ContentProvider来保存状态(进程级别共享)
public class StateProvider extends ContentProvider {
private static Bundle sState;
@Override
public boolean onCreate() {
// 进程启动时恢复
if (sState == null) {
sState = loadFromDisk();
}
return true;
}
@Override
public Cursor query(...) {
return null;
}
@Override
public String getType(...) {
return null;
}
@Override
public Uri insert(...) {
sState = bundle;
saveToDisk(); // 持久化到磁盘
return null;
}
@Override
public int delete(...) { return 0; }
@Override
public int update(...) { return 0; }
}
7.3 案例三:Binder事务BinderTransactionTooLargeException
背景:在Android 5.0+设备上,传递大数据时经常崩溃。
错误原因:
// 错误:Intent中携带过大的数据
Intent intent = new Intent();
intent.putExtra("data", largeByteArray); // 超过1MB可能崩溃
Binder事务有大小限制(Binder驱动中的binder_thread缓冲区大小),在Android 5.0之前是200KB,之后扩大到1MB左右。
解决方案:
// 方案1:使用ContentProvider传递大数据
public class DataProvider extends ContentProvider {
private byte[] mData;
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, ...) {
// 返回数据
return null;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
mData = values.getAsByteArray("data");
return uri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
// 方案2:使用Application级别的静态引用
public class MyApplication extends Application {
private static byte[] sLargeData;
public static void setLargeData(byte[] data) {
sLargeData = data;
}
public static byte[] getLargeData() {
return sLargeData;
}
}
// 方案3:使用文件传递
public class FileHelper {
public static Uri saveToFile(Context context, byte[] data) {
File file = new File(context.getCacheDir(), "temp_data");
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(data);
} catch (IOException e) {
e.printStackTrace();
}
return Uri.fromFile(file);
}
}
八、总结与记忆技巧
8.1 启动流程记忆口诀
内核启动init忙,init fork Zygote强。 Zygote preload完,fork SystemServer忙。 SystemServer开服务,AMS管理万象更新。
8.2 Binder核心要点
- 一次拷贝:mmap映射,数据只拷贝一次
- C/S架构:Stub/Proxy模式
- UID校验:安全的IPC基础
- 线程池:Binder线程池管理并发
8.3 AMS关键职责
- Activity生命周期管理
- 进程状态管理(oom_adj)
- ContentProvider管理
- Broadcast管理
- 服务(Service)管理
最后说一句,Android的系统架构确实复杂,但理清了”启动流程 → Binder机制 → AMS管理”这条主线,你就已经超越了大部分求职者。面试的时候如果能结合自己的实际开发经验,讲一讲遇到的坑和解决方案,比单纯背诵理论要加分得多。
有什么具体问题或者想深入聊的部分,随时说。
