第二章:开发环境搭建与源码编译
开发SystemUI,环境是第一步。很多人觉得这步就是个机械操作,照着文档敲命令就行。但我个人的经验是——环境搭不好,后面八成的坑都从这里来。今天我们就聊聊怎么高效配置AOSP、按需编译SystemUI模块,还有怎么把代码塞进IDE里看得舒服。
2.1 AOSP源码获取与配置
先说说repo。我知道很多人一上来就用repo init -u ... --depth=1想省空间,但这个参数会砍掉所有历史commit,后面你想git log看谁改了什么就抓瞎了。我建议还是拉完整分支,至少保留最近一年的提交记录。
# 初始化AOSP主分支(如果你在搞Android 13/14)
mkdir ~/aosp && cd ~/aosp
repo init -u https://android.googlesource.com/platform/manifest -b android-14.0.0_r30
repo sync -c -j8 # -c只同步当前分支,省带宽
repo sync -c -j8 --force-sync --no-clone-bundle 2>&1 | tee sync.log,这样能在日志里快速定位哪些仓库失败了。记得留至少150GB磁盘空间,我遇到过编译到一半磁盘满的惨案。
为什么我用-j8?因为8个并行repo同步线程对普通网络比较友好,太多会被GitHub限速。如果你内网有镜像,可以调到16。
2.2 模块编译命令精讲
全量编译AOSP动辄一两个小时,我们搞SystemUI的只需要编译目标模块就行。记住这套心法:分步骤、只编改过的东西。
2.2.1 编译框架层(framework/base)
SystemUI在frameworks/base/packages/SystemUI,但它依赖framework.jar。每次改完SystemUI代码,我一般先只编译framework-res和SystemUI两个目标:
source build/envsetup.sh
lunch aosp_husky-userdebug # 选你的设备
# 只编译framework资源(避免全量编framework.jar)
make framework-res -j16
# 编译SystemUI模块
make SystemUI -j16
# 或者更精细:单独编译systemui的静态jar
make services -j16 && make SystemUI -j16
res/文件后执行make SystemUI,结果资源id没对齐,导致运行时崩溃。现在我的标准流程:改资源 → make framework-res → 改代码 → make SystemUI。
2.2.2 增量编译与热替换
如果只改了Kotlin或Java源文件,可以用mm命令在模块目录下直接编译:
cd frameworks/base/packages/SystemUI
mm -j16
# 输出文件在 out/target/product/xxx/system_ext/priv-app/SystemUI/SystemUI.apk
你想想看,mm只编译当前目录及其依赖,比make快3倍不止。我一般开着两个终端:一个终端做mm,另一个终端用adb sync推送。
# 推送到设备
adb root && adb remount
adb sync system_ext/priv-app/SystemUI
adb reboot # 或者重启SystemUI: adb shell pkill -f com.android.systemui
记住,adb sync system_ext/priv-app/SystemUI只推送SystemUI的apk和oat,秒级完成。
2.2.3 编译产物的常见问题
有时候改了代码后mm还是会报错,说某些类找不到。别慌,可能是之前的obj文件没清除干净。我习惯先运行make clean-SystemUI清理专门模块,再重新 mm。
| 场景 | 推荐命令 | 耗时(参考) |
|---|---|---|
| 第一次全量编译 | make -j$(nproc) | 40~120分钟 |
| 只改Java/Kotlin | mm -j16 | 30秒~3分钟 |
| 改资源文件 | make framework-res && make SystemUI | 1~5分钟 |
| 改了framework API | make -j16 framework SystemUI | 5~15分钟 |
2.3 IDE导入技巧:IntelliJ与Android Studio
AOSP代码结构特殊,直接打开根目录会卡死。我摸索出一套比较顺手的导入方式。
2.3.1 生成IDE项目文件
AOSP自带了一个工具development/tools/idegen,能生成IntelliJ的项目配置文件:
cd ~/aosp
source build/envsetup.sh
lunch aosp_husky-userdebug
make idegen # 编译idegen工具
development/tools/idegen/idegen.sh # 生成android.ipr和android.iml
生成后,用IntelliJ直接打开android.ipr。第一次加载会扫描所有源码,大概5~10分钟。嗯,这里要注意:生成的iml默认会把所有模块都include,我一般在iml里把不相关的模块exclude掉,只保留frameworks/base、packages、libcore。
2.3.2 手动配置依赖与源码路径
如果用Android Studio直接打开frameworks/base目录,能更快加载。我常用这个方式:
- File → New → Project from Existing Sources → 选择
frameworks/base/packages/SystemUI - 选择Gradle构建系统(如果提示没有build.gradle,选None)
- 打开工程后,手动添加依赖:File → Project Structure → Modules → 添加
frameworks/base/core/java、frameworks/base/packages等源码根目录 - 注意把
out/soong/.intermediates/frameworks/base/framework/android_common/gen路径加进去,否则找不到R.java和AIDL生成的类
mm后自动把最新生成的R.java和AIDL拷贝到IDE的对应目录,这样代码不报红。脚本大概20行,有需要的可以抄我的:cp -r $OUT/../gen/R.java $PROJECT_ROOT/gen/
2.3.3 调试环境:直接Attach到SystemUI进程
IDE配置好之后,调试就简单了:启动模拟器或真机,执行adb root && adb shell setprop debug.debuggable 1(或直接在userdebug版本上),然后在Android Studio里选择Attach Debugger to Android Process,选com.android.systemui。断点就可以打在任何SystemUI代码里。
为什么我推荐Attach而不是Run?因为SystemUI是系统核心进程,直接重启会导致系统重绘,而Attach调试不会中断SystemUI的正常运行。
2.4 环境搭建的整体流程
下面这张图总结了开发环境搭建的核心流程和关键节点,方便你把前面的知识点串起来。
核心要点回顾
- 拉源码时别偷懒省depth,否则查commit历史时会哭
- 编译SystemUI首选
mm,配合adb sync实现秒级推送 - 改资源必须先编
framework-res,否则运行时资源ID错乱 - IDE里把
out/.../gen加进去就不报红 - Attach调试比Run调试更方便,不会重启SystemUI
环境搭好了,后面写代码才能跑得起来。你想想看,如果每次改一行都要全量编译40分钟,谁还有心思优化卡顿?所以这一章就是你的“加速器”。