九、系统服务开发:SystemServer 启动流程、添加自定义系统服务
系统服务,说白了就是 Android 系统的“内脏”。没有它们,你的手机连亮屏都做不到。我刚开始接触 AOSP 时,总觉得 SystemServer 启动流程特别神秘——一个 Java 进程怎么就把整个 Android 世界给撑起来了?后来啃了几个月源码,才慢慢摸清门道。
今天咱们就把它拆开揉碎了讲。你不仅能看懂 SystemServer 怎么启动的,还能亲手往里面塞一个自定义服务。
9.1 SystemServer 启动流程:从 init 到服务管家
先看整体脉络。我画了一张图,帮你快速建立全局认知:
嗯,这张图你得存着。每次看源码时对照着它,思路会清晰很多。
9.2 启动流程源码级拆解
咱们从 SystemServer.main() 开始看。这是整个服务的入口:
// frameworks/base/services/java/com/android/server/SystemServer.java
public static void main(String[] args) {
new SystemServer().run();
}
代码就这么两行。但 run() 方法里,藏着整个 Android 系统的启动秘密。
9.2.1 run() 方法做了什么?
我把它拆成三个阶段:
- 准备阶段:设置系统属性、加载库、创建主线程 Looper
- 启动引导服务:Installer、DeviceIdentifiers 等
- 启动核心服务:ActivityManagerService、PackageManagerService 等
看一段关键代码:
private void run() {
// 1. 准备阶段
Looper.prepareMainLooper();
System.loadLibrary("android_servers");
// 2. 创建系统上下文
createSystemContext();
// 3. 启动引导服务
startBootstrapServices();
// 4. 启动核心服务
startCoreServices();
// 5. 启动其他服务
startOtherServices();
// 6. 进入消息循环
Looper.loop();
}
关键点:Looper.loop() 是最后一步。一旦调用,主线程就卡在这里了。所有后续工作都通过消息驱动。这也是为什么系统服务不会“退出”的原因。
9.3 添加自定义系统服务:手把手教程
好,理论讲完了。咱们来点实战。我带你写一个 DemoService,功能很简单——返回当前系统运行时间。
9.3.1 第一步:定义 AIDL 接口
在 frameworks/base/core/java/android/os/ 下创建 IDemoService.aidl:
// frameworks/base/core/java/android/os/IDemoService.aidl
package android.os;
interface IDemoService {
long getUptimeMillis();
}
我的习惯:AIDL 文件命名一定要加 "I" 前缀。这是 Android 源码的约定,别打破它。我曾经见过有人用 "DemoService.aidl",结果编译时各种报错,排查了半天。
9.3.2 第二步:实现服务端
在 frameworks/base/services/core/java/com/android/server/ 下创建 DemoService.java:
// frameworks/base/services/core/java/com/android/server/DemoService.java
package com.android.server;
import android.content.Context;
import android.os.IDemoService;
import android.util.Slog;
public class DemoService extends IDemoService.Stub {
private static final String TAG = "DemoService";
private final Context mContext;
public DemoService(Context context) {
mContext = context;
Slog.i(TAG, "DemoService created");
}
@Override
public long getUptimeMillis() {
return SystemClock.uptimeMillis();
}
}
9.3.3 第三步:注册到 SystemServer
打开 SystemServer.java,在 startOtherServices() 方法中添加:
private void startOtherServices() {
// ... 原有代码 ...
try {
Slog.i(TAG, "Starting DemoService");
DemoService demo = new DemoService(mSystemContext);
ServiceManager.addService("demo", demo);
} catch (Throwable e) {
Slog.e(TAG, "Failed to start DemoService", e);
}
// ... 原有代码 ...
}
注意:添加位置有讲究。我建议放在 startOtherServices() 的靠前位置,但要在 WindowManagerService 之后。因为你的服务可能依赖窗口管理。嗯,这个坑我踩过——服务启动太早,拿不到 Display 信息。
9.3.4 第四步:客户端调用
在 App 中调用你的服务:
IDemoService demoService = IDemoService.Stub.asInterface(
ServiceManager.getService("demo"));
if (demoService != null) {
long uptime = demoService.getUptimeMillis();
Log.d(TAG, "System uptime: " + uptime);
}
9.4 避坑指南:我踩过的三个坑
| 坑 | 现象 | 原因 | 解决方案 |
|---|---|---|---|
| 服务启动时序 | 服务注册成功但调用时崩溃 | 依赖的服务还没启动 | 使用 waitForService() 或调整启动顺序 |
| 权限问题 | 客户端调用时抛 SecurityException | 未声明系统权限 | 在 AndroidManifest 中添加 android.permission.CONFIGURE_INPUT_DISPLAY |
| 编译失败 | 找不到 AIDL 生成的 Stub 类 | AIDL 文件未加入编译 | 检查 Android.bp 中是否包含 aidl 文件路径 |
9.5 服务注册的两种方式
我个人更推荐第二种,但第一种更常见:
- 直接注册:
ServiceManager.addService("demo", service)—— 简单粗暴,适合快速验证 - 通过 SystemService 基类:继承
SystemService,实现生命周期回调 —— 更规范,适合生产环境
第二种方式的示例:
public class DemoService extends SystemService {
private final DemoServiceImpl mImpl;
public DemoService(Context context) {
super(context);
mImpl = new DemoServiceImpl(context);
}
@Override
public void onStart() {
publishBinderService("demo", mImpl);
}
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
// 系统服务就绪,可以初始化了
}
}
}
为什么推荐第二种? 因为 SystemService 提供了 onBootPhase() 回调。你可以精确控制服务在不同启动阶段的行为。比如,有些初始化必须在 PHASE_ACTIVITY_MANAGER_READY 之后才能做。
9.6 验证你的服务
编译刷机后,用 adb 验证:
# 查看服务是否注册成功
adb shell service list | grep demo
# 直接调用服务方法
adb shell service call demo 1
如果看到输出 Result: Parcel(00000000 00000000 ...),说明服务跑起来了。那个 1 是方法编号——AIDL 中第一个方法对应编号 1。
嗯,到这里,你已经掌握了 SystemServer 的启动流程,也亲手添加了一个自定义系统服务。剩下的就是多练、多踩坑。我曾经为了一个服务注册失败的问题折腾了两天,最后发现是 AIDL 文件路径写错了。这种经历,你迟早也会有的。