第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 实现流程

  1. 定义AIDL接口:创建一个.aidl文件,声明需要暴露的方法。
  2. 实现Service类:继承Service,重写onCreate、onBind、onDestroy等方法。
  3. 生成Binder桩:在onBind中返回AIDL接口的Stub实例。
  4. Manifest注册:在AndroidManifest.xml中添加<service>标签,设置权限、进程、导出属性。
  5. 编译与部署:将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 常见错误

错误现象原因解决方案
客户端无法绑定,返回nullService未在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生命周期与AIDL交互图 系统Service生命周期与AIDL绑定流程 AIDL接口定义 IMathService.aidl Service实现类 MathService.java onCreate() onBind() 返回Binder 客户端 bindService() onDestroy() Manifest注册 <service> 标签 生命周期顺序:onCreate → onBind(绑定) → onUnbind(所有客户端解绑) → onDestroy
关键要点:系统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随系统自动启动。