3. 开发环境搭建

Native Service 开发与普通 App 开发最大的区别在于:你写的 C/C++ 代码最终要跑在 Android 系统镜像里,而不是安装成一个 APK。因此开发环境不只是“装个 IDE”,而是要同时具备系统源码编译能力、NDK 交叉编译能力和设备调试能力三条链路。本节按这三条链路展开,并给出可复用的命令与配置。

一、核心原理:三条链路各司其职

AOSP 源码编译负责把 Native Service 编进 system 分区,产出可刷写的镜像;NDK 负责在 Android Studio 中快速验证 C/C++ 逻辑与 JNI 接口;adb/fastboot 负责把产物送到设备并观察运行状态。三者关系可以用下面的结构图表示。

Native Service 开发环境三条链路 ① AOSP 源码链路 repo init / repo sync source build/envsetup.sh lunch aosp_arm64-userdebug m / mm / mmm 编译 产出:system.img、boot.img ② NDK 验证链路 Android Studio + CMake ndkVersion / abiFilters JNI 接口快速验证 ndk-build / clang 交叉编译 产出:.so、可执行测试程序 ③ 设备调试链路 adb push / pull / shell adb logcat 过滤 TAG fastboot flash system fastboot reboot 产出:运行日志、验证结果 Native Service 可运行产物 system/bin/xxx_service + .rc 启动脚本 常用工具链 repo · soong/ninja · clang · adb · fastboot · logcat · gdb/lldb · strace · atrace

二、AOSP 源码下载与编译流程

AOSP 体积大、依赖多,建议使用 Ubuntu 20.04/22.04,至少 16 核 CPU、32GB 内存、400GB 以上 SSD。首次同步耗时较长,务必先配置好 Git 身份与代理。

# 1. 安装 repo
mkdir ~/bin && curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo && export PATH=~/bin:$PATH

# 2. 初始化并同步(以 Android 13 为例)
mkdir aosp && cd aosp
repo init -u https://android.googlesource.com/platform/manifest -b android-13.0.0_r1
repo sync -c -j8 --no-tags

# 3. 编译环境
source build/envsetup.sh
lunch aosp_arm64-userdebug
m -j16            # 全量编译
mm                # 编译当前目录模块
mmm frameworks/base/services/core/jni   # 编译指定路径模块
只改一个 Native Service 时,优先使用 mm 或 mmm,再 adb push 对应产物到设备,比全量 m 快一个数量级。全量编译仅在首次或改动系统公共库时使用。

三、Android Studio NDK 配置

NDK 链路用于快速验证 C/C++ 逻辑、JNI 接口和第三方库移植,不必每次都刷机。在 local.properties 或 build.gradle 中固定 NDK 版本,避免团队间版本漂移。

android {
    ndkVersion "25.2.9519653"
    defaultConfig {
        externalNativeBuild {
            cmake { cppFlags "-std=c++17 -fno-exceptions" }
        }
        ndk { abiFilters "arm64-v8a", "x86_64" }
    }
    externalNativeBuild {
        cmake { path file("src/main/cpp/CMakeLists.txt") }
    }
}

若需脱离 Gradle 单独交叉编译,可直接调用 NDK 工具链:

$NDK/build/tools/make_standalone_toolchain.py \
  --arch arm64 --api 33 --install-dir /opt/ndk-arm64

/opt/ndk-arm64/bin/aarch64-linux-android33-clang++ \
  -shared -fPIC -o libdemo.so demo.cpp -llog

四、adb 与 fastboot 调试工具

adb 面向运行中的系统,fastboot 面向未启动的引导阶段,两者配合完成“编译—推送—验证—刷写”闭环。

工具典型命令使用场景
adbadb devices -l确认设备连接与序列号
adbadb push out/.../xxx_service /system/bin/推送单个 Native 可执行文件
adbadb shell setenforce 0临时关闭 SELinux 排查权限问题
adbadb logcat -s xxx_service:V按 TAG 过滤服务日志
fastbootfastboot flash system system.img刷写 system 分区
fastbootfastboot reboot刷写后重启进入系统
修改 /system 分区前需执行 adb remount 或 adb disable-verity 后重启,否则会因 dm-verity 校验失败导致写入无效。生产设备还需解锁 bootloader,注意数据会被清空。

五、案例场景:新增一个 Native Service 的完整闭环

  1. 在 frameworks/base/services/ 下新建模块目录,编写 Android.bp,声明 cc_binary 与依赖的 libbinder、libutils。
  2. 编写 xxx_service.rc,定义 service、class core、user system 与 oneshot 等属性。
  3. 执行 mmm frameworks/base/services/xxx 编译,确认 out/target/product/.../system/bin/xxx_service 生成。
  4. adb push 可执行文件与 rc 文件,adb shell start xxx_service 启动。
  5. 用 adb logcat 与 adb shell dumpsys 验证服务注册与接口调用。
  6. 验证通过后,将模块加入系统编译配置,全量 m 并 fastboot flash system 固化。

六、误区与复盘

  • 只配 NDK 不配 AOSP:NDK 编出的 .so 无法直接作为系统服务常驻,最终仍需 AOSP 编译进镜像。
  • 忽略 SELinux:服务启动失败常因 avc: denied,应先用 setenforce 0 定位,再补 te 规则,而不是长期关闭。
  • NDK 版本漂移:不同 NDK 的 libc++ ABI 可能不兼容,务必在构建脚本中锁定版本。
  • 直接改 system 分区不刷机:临时 push 只适合调试,正式验证必须走镜像刷写,否则环境不可复现。
环境搭建的验收标准:能独立完成一次 AOSP 全量编译、能用 NDK 编出一个可执行程序、能通过 adb 推送并看到 Native Service 日志、能用 fastboot 刷写 system 镜像并正常启动。四条全部打通,才具备后续 Native Service 开发的基础条件。