实战项目:从零构建一个定制 ROM

说实话,这个章节是我整个课程里最期待的部分。前面讲了那么多理论、源码、编译流程,现在终于到了动真格的时候。从零构建一个定制 ROM,听起来很唬人,其实拆开来看就是四件事:功能裁剪、UI 定制、性能优化、OTA 发布。我当年第一次做这个项目时,踩的坑比代码行数还多,今天我把这些经验都摊开来跟你聊聊。

一、项目整体架构

先给你看一张图,这是我个人习惯用的项目流程。你照着这个走,基本不会迷路。

定制 ROM 构建流程 1. 源码准备 repo sync AOSP 2. 功能裁剪 删除无用模块 3. UI 定制 主题/启动器/壁纸 4. 性能优化 内核/调度/内存 5. 编译打包 make -jN 6. OTA 发布 差分升级包生成 7. 测试验证

这个流程我反复调整过很多次。一开始我总想着一步到位,结果每次编译到一半就崩。后来学乖了,按这个顺序走,每一步都验证通过再往下。

二、功能裁剪:做减法比做加法难

功能裁剪,说白了就是删东西。但删什么、怎么删,这里头门道很多。

2.1 确定裁剪目标

我建议你先想清楚:这个 ROM 给谁用?用在什么设备上?

  • 低端设备:砍掉动态壁纸、Google 服务、3D 特效、多余输入法
  • 行业专用设备:只保留核心应用,比如 POS 机、扫码器驱动
  • 极客定制:保留 AOSP 基础,去掉厂商预装应用
我的经验: 别一上来就大砍特砍。先编译一个完整版,确认能跑起来,再逐步裁剪。我曾经一次性删了十几个模块,结果编译过了但开机黑屏,排查了三天才发现是删了 SurfaceFlinger 的依赖。

2.2 具体操作

在 AOSP 源码中,裁剪主要通过修改 device/<厂商>/<设备名>/device.mk 文件实现。

# 示例:裁剪 device.mk
# 删除不需要的模块
PRODUCT_PACKAGES := \
    Launcher3 \
    Settings \
    SystemUI \
    # 以下为删除项(注释掉或移除)
    # LiveWallpapers \
    # LiveWallpapersPicker \
    # Email \
    # Calendar \
    # Exchange2 \
    # PrintSpooler \
    # WallpaperPicker \
    # BasicDreams \
    # PhotoTable \
    # Galaxy4 \
    # HoloSpiralWallpaper \
    # NoiseField \
    # PhaseBeam

# 禁用不必要的服务
PRODUCT_PROPERTY_OVERRIDES += \
    ro.config.low_ram=true \
    ro.sys.fw.bg_apps_limit=16

嗯,这里要注意:有些模块之间有依赖关系。比如你删了 WallpaperPicker,但 Launcher3 里还引用了它,那编译就会报错。我的做法是:先 grep 一下这个模块被谁引用了。

# 查找模块依赖
grep -r "WallpaperPicker" vendor/ device/ packages/

三、UI 定制:让 ROM 看起来像你的作品

UI 定制是最有成就感的部分。你想想看,开机第一眼看到的是你自己的设计,那感觉完全不一样。

3.1 修改开机动画

开机动画在 frameworks/base/core/res/assets/images/ 目录下。其实它是一个 ZIP 包,里面包含一系列 PNG 图片和一个 desc.txt 描述文件。

# desc.txt 示例
480 800 15
p 1 0 part0
p 0 0 part1

# 说明:
# 第一行:宽 高 帧率
# p 1 0 part0:播放一次,不循环
# p 0 0 part1:循环播放
注意: 图片尺寸必须和屏幕分辨率一致,否则会拉伸变形。我见过有人把 720p 的图片用在 1080p 的设备上,结果开机动画糊成一团。

3.2 修改系统主题

系统主题主要涉及 frameworks/base/core/res/res/ 下的 values 文件。比如修改主色调:

<!-- frameworks/base/core/res/res/values/colors.xml -->
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 修改为主色调为深蓝色 -->
    <color name="primary_color">#FF1565C0</color>
    <color name="primary_dark_color">#FF0D47A1</color>
    <color name="accent_color">#FF00BCD4</color>
    <!-- 状态栏颜色 -->
    <color name="status_bar_color">#FF0D47A1</color>
</resources>

改完之后,记得重新编译 framework-res.apk:

make framework-res -j8

3.3 替换默认启动器

我个人习惯用 Lawnchair 作为基础,因为它开源且可定制性强。把第三方启动器源码放到 packages/apps/ 目录下,然后在 device.mk 里添加:

PRODUCT_PACKAGES += \
    Lawnchair \
    # 移除原生的 Launcher3
    # Launcher3

四、性能优化:让 ROM 飞起来

性能优化这块,我踩过的坑最多。说白了,优化就是找瓶颈。CPU、内存、IO、GPU,哪个是短板就优化哪个。

4.1 内核优化

内核参数调整是最直接的手段。修改 device/<厂商>/<设备名>/init.rcinit.<设备名>.rc

# 调整 CPU 调度器
on boot
    write /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor "interactive"
    write /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor "interactive"
    # 调整 I/O 调度器
    write /sys/block/mmcblk0/queue/scheduler "cfq"
    # 降低 swappiness 减少内存交换
    write /proc/sys/vm/swappiness 10
    # 启用 zram
    write /sys/block/zram0/disksize 256M
    swapon /dev/block/zram0
关键点: 不同芯片平台的调优参数差异很大。高通平台用 interactive 调度器效果不错,联发科平台我建议用 schedutil。这个没有银弹,得实测。

4.2 内存优化

低内存设备(1GB 以下)必须做内存优化。我常用的手段:

  • 启用 ro.config.low_ram=true,系统会自动减少后台进程
  • 调整 LMK(Low Memory Killer)阈值
  • 禁用动画缩放:ro.min.fling_velocity=4000
# 在 build.prop 中添加
ro.sys.fw.bg_apps_limit=12
ro.config.low_ram=true
persist.sys.force_highendgfx=false

4.3 应用预优化

这个技巧知道的人不多。在编译时对系统应用做 dex2oat 预编译,可以显著提升首次启动速度。

# device.mk 中启用
WITH_DEXPREOPT := true
DONT_DEXPREOPT_PREBUILTS := false

不过要注意,预编译会增加系统分区大小。我算过,大概每个应用多占 5-10MB。

五、OTA 发布:让用户能升级

OTA 是定制 ROM 的最后一公里。没有 OTA,你的 ROM 就是个一次性产品。

5.1 生成 OTA 包

AOSP 自带的工具在 build/tools/releasetools/ 目录下。基本流程:

# 1. 编译完整 ROM
source build/envsetup.sh
lunch aosp_<设备名>-userdebug
make -j8

# 2. 生成 OTA 包(增量更新)
./build/tools/releasetools/ota_from_target_files \
    -i previous_target_files.zip \
    -n \
    -v \
    -p out/host/linux-x86 \
    -k build/target/product/security/testkey \
    out/target/product/<设备名>/obj/PACKAGING/target_files_intermediates/<设备名>-target_files-*.zip \
    update.zip
我的经验: 第一次做 OTA 时,我忘了加 -i 参数指定上一个版本,结果生成了全量包,用户每次都要下载 1GB 多的文件。后来改成增量包,每次只有 50-100MB,体验好太多了。

5.2 OTA 服务器搭建

最简单的方案是用 Nginx 做静态文件服务器:

# nginx 配置
server {
    listen 80;
    server_name ota.example.com;
    root /var/www/ota;
    
    location / {
        autoindex on;
    }
    
    # 限制 OTA 包下载速度,避免占满带宽
    location /updates/ {
        limit_rate 5m;
    }
}

然后在 ROM 中内置 OTA 应用,指向你的服务器地址。我推荐用 OpenDelta 或自己写一个简单的 OTA 检查器。

5.3 版本管理

建议用语义化版本号:主版本.次版本.修订号。比如 v1.2.3。每次发布时在 build.prop 中更新:

ro.build.version.release=12
ro.build.version.incremental=v1.2.3
ro.build.display.id=MyROM-v1.2.3

六、测试验证:别急着发布

我吃过这个亏。有一次编译完觉得没问题,直接发布了,结果用户反馈 WiFi 连不上。排查后发现是裁剪时删了一个 WiFi 相关的 HAL 库。

建议的测试清单:

测试项 测试方法 预期结果
开机启动 冷启动 3 次 每次 < 30 秒
WiFi/蓝牙 连接/断开 10 次 无异常断开
电话/短信 拨打/接收测试 声音清晰
相机 拍照/录像 5 分钟 无闪退
OTA 升级 从 v1.0 升到 v1.1 数据不丢失

嗯,差不多就这些了。从零构建一个定制 ROM,说难不难,说简单也不简单。关键是每一步都要稳,别想着一步到位。我当年第一个 ROM 做了整整两个月,现在回想起来,那些坑都是财富。

如果你在做的过程中遇到问题,欢迎来找我交流。记住:编译失败是常态,别灰心,查日志、问社区、看源码,总能解决的。


公众号:蓝海资料掘金营,微信deep3321