第2章 自定义系统Service的实现与注册:生命周期、AIDL与Manifest
系统级Service是Android Framework中后台长期运行、跨进程提供服务的基础组件。自定义系统Service需要完整经历接口定义(AIDL)、生命周期重写、清单注册三个环节。本章深入每个环节的原理、实现步骤及常见陷阱。
目标:掌握从AIDL接口声明到Service运行的全链路操作,理解系统Service与普通应用Service在注册、权限、调用上的差异。
2.1 核心原理
Android Service的生命周期由系统AMS(Activity Manager Service)调度。自定义系统Service通常被其他系统组件或特权应用通过绑定方式调用,因此必须同时支持启动和绑定两种模式。
| 生命周期方法 | 触发时机 | 典型用途 |
|---|---|---|
| onCreate() | Service第一次创建时 | 初始化资源、线程池、AIDL回调注册 |
| onStartCommand(Intent, flags, startId) | 每次调用 startService() | 处理前台启动命令,返回 START_STICKY 等 |
| onBind(Intent) | 首次有客户端调用 bindService() | 返回AIDL接口的Binder实现 |
| onUnbind(Intent) | 所有客户端解除绑定时 | 清理绑定相关资源 |
| onDestroy() | Service被销毁前 | 释放所有资源,停止线程 |
AIDL(Android Interface Definition Language)生成跨进程Binder接口桩代码。系统Service通常声明为android:exported="true",并设置android:process属性运行在独立进程中以避免内存泄漏。
注意:系统Service如果运行在非系统进程,必须在Manifest中声明
android:protectionLevel="signature"或使用系统权限,否则客户端无法绑定。
2.2 实现流程
- 定义AIDL接口:创建一个
.aidl文件,声明需要暴露的方法。 - 实现Service类:继承
Service,重写onCreate、onBind、onDestroy等方法。 - 生成Binder桩:在
onBind中返回AIDL接口的Stub实例。 - Manifest注册:在
AndroidManifest.xml中添加<service>标签,设置权限、进程、导出属性。 - 编译与部署:将Service编译为系统应用或预置到system分区。
2.3 代码实现
以下为一个完整的系统Service示例:提供远程加法计算。
AIDL接口文件(IMathService.aidl)
// IMathService.aidl
package com.example.systemserver;
interface IMathService {
int add(int a, int b);
int subtract(int a, int b);
}
Service实现(MathService.java)
package com.example.systemserver;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class MathService extends Service {
private static final String TAG = "MathService";
private final IMathService.Stub binder = new IMathService.Stub() {
@Override
public int add(int a, int b) {
Log.d(TAG, "add: " + a + " + " + b);
return a + b;
}
@Override
public int subtract(int a, int b) {
Log.d(TAG, "subtract: " + a + " - " + b);
return a - b;
}
};
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Service created");
// 初始化线程池或其他资源
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "Service bound");
return binder;
}
@Override
public void onDestroy() {
Log.i(TAG, "Service destroyed");
super.onDestroy();
// 释放资源
}
}
Manifest注册(AndroidManifest.xml片段)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.systemserver">
<application
android:persistent="true"
android:process=":systemserver">
<service android:name=".MathService"
android:exported="true"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="com.example.systemserver.IMathService" />
</intent-filter>
</service>
</application>
</manifest>
2.4 调试与验证
- 使用
adb shell dumpsys activity services查看Service是否注册成功。 - 使用
adb logcat -s MathService过滤日志,确认生命周期回调。 - 编写测试客户端调用
bindService(),检查返回的Binder是否正常。
调试命令:
adb shell am startservice -n com.example.systemserver/.MathService 可触发onStartCommand。
2.5 常见错误
| 错误现象 | 原因 | 解决方案 |
|---|---|---|
| 客户端无法绑定,返回null | Service未在Manifest中导出,或AIDL接口包名不一致 | 检查exported="true",确保AIDL接口路径与清单匹配 |
java.lang.SecurityException | 客户端缺少系统权限 | 在Manifest中添加android:permission并使用系统自定义权限 |
| Service重启后状态丢失 | onStartCommand返回错误标记 | 使用START_REDELIVER_INTENT确保重新传递Intent |
| AIDL编译错误(找不到类) | AIDL文件中使用了非基本类型但未import | 在AIDL文件顶部添加import语句 |
2.6 性能与稳定性优化
- 避免主线程阻塞:AIDL方法若可能耗时,应使用
android:process单独进程,并在内部使用HandlerThread或线程池。 - 合理返回生命周期标志:经常被启动的Service应返回
START_STICKY,绑定型Service注意onUnbind中返回false以避免重建。 - 资源泄漏防范:在
onDestroy中释放所有注册的监听器、回调、线程。 - 系统Service的持久化:Manifest中设置
android:persistent="true"使系统启动时自动拉起Service。
2.7 组件关系与生命周期流程
下图展示了自定义系统Service从创建到销毁的完整流程,以及AIDL接口与客户端的交互关系。
关键要点:系统Service的
onBind返回的Binder对象必须与AIDL生成的Stub类型一致,否则客户端强制转换时会抛出ClassCastException。推荐在onCreate中预先创建Binder实例,避免绑定时的创建延迟。
2.8 部署至系统镜像
将Service编译为系统应用(放在packages/apps/目录下)或作为预置APK(system/priv-app),并确保清单中包含android:sharedUserId="android.uid.system"以获得系统级权限。使用mm或make编译后,重新打包system.img并刷机。
对于非系统源码环境的部署,可以将APK签名为平台签名,复制到/system/priv-app/并设置权限644,重启后Service随系统自动启动。