MiCarSettings Baseline Profile 适配指南
一、什么是 Baseline Profile
1.1 背景
Android 应用的 Java/Kotlin 代码在 ART 虚拟机上有三种执行模式:
| 模式 | 速度 | 场景 |
|---|---|---|
| 解释执行(Nterp) | 最慢(~5-10x 慢) | 首次运行、未编译的方法 |
| JIT 即时编译 | 中等 | 运行中热点方法被后台线程编译 |
| AOT 预编译 | 最快 | 安装时通过 dex2oat 编译为机器码 |
冷启动时,如果方法没有被 AOT 预编译,主线程就以解释模式执行,速度慢 5-10 倍。JIT 后台线程会尝试编译热点方法,但来不及——主线程已经在等待了。
1.2 Baseline Profile 的作用
Baseline Profile 是一个文本文件(baseline-prof.txt),列出应用启动路径上的热点类和方法,告诉 ART:
「这些方法请在安装时就 AOT 编译好,不要等运行时再 JIT」
效果:启动路径上的方法直接执行机器码,不走解释器,典型提速 15-40%。
1.3 和 dex2oat speed 模式的区别
| 方案 | 编译范围 | APK 体积增长 | 适用场景 |
|---|---|---|---|
speed(全量 AOT) | 所有方法 | 大(+50-100%) | 系统应用、体积不敏感 |
speed-profile(Baseline Profile) | 仅热路径方法 | 中(+15-30%) | 三方应用、体积敏感 |
verify(当前状态) | 仅类验证,不编译方法 | 无 | ← MiCarSettings 当前状态 |
二、MiCarSettings 当前状态诊断
2.1 trace 实测数据
从 _xiaomi_watt_sa8295p_20260717210509 trace 中检测到:
| 指标 | 数值 | 含义 |
|---|---|---|
| OAT 文件路径 | /data/dalvik-cache/arm64/system@priv-app@MiCarSettings@... | 有 dalvik-cache,但是 verify 模式 |
| vdex 校验 | DexChecksumUpToDate(vdex) ×3 | 类已验证,但方法体未编译 |
| JIT 编译活动 | 97 次 Compiling baseline,共 147ms | JIT 在启动期间紧急编译,来不及 |
| 解释器采样 | 244 次 / 2265 总采样(10.8%) | 大量方法在以解释模式运行 |
| bindApplication 空白区 | 273ms | 48.6% 的时间花在未被 atrace 标记的类加载+解释执行 |
2.2 JIT 编译热点 Top 10
启动期间 JIT 线程紧急编译的方法(说明这些方法正在以解释模式运行,影响启动):
| JIT 编译耗时 | 方法 |
|---|---|
| 15.18ms | ConstraintLayout.applyConstraintsFromLayoutParams() |
| 10.38ms | ConstraintWidget.applyConstraints() |
| 9.60ms | ReflectiveTypeAdapterFactory.getBoundFields() (Gson) |
| 5.67ms | ConstraintWidget.addToSolver() |
| 5.26ms | ConstraintWidgetContainer.layout() |
| 5.12ms | TypeToken.<init>() (Gson) |
| 4.09ms | BasicMeasure.solverMeasure() |
| 4.02ms | BasicPreference.initPreference() (Settings 自有) |
| 3.60ms | ConstraintLayout$Measurer.measure() |
| 3.41ms | ConstraintLayout$LayoutParams.<init>() |
2.3 整机构建配置
# device/qcom/qssi/BoardConfig.mk
WITH_DEXPREOPT := true
WITH_DEXPREOPT_PIC := true
# 非 user 构建保留 classes.dex
DEX_PREOPT_DEFAULT := nostripping当前整机使用默认 WITH_DEXPREOPT=true,但没有为 MiCarSettings 指定 compiler-filter=speed,默认走 verify 或 quicken 模式——只验证类不编译方法。
三、适配方案
方案一:系统构建指定 speed 编译(推荐,最简单)
MiCarSettings 是 /system/priv-app/ 下的系统应用,体积不是关键约束。直接在系统构建中指定全量 AOT 编译。
3.1 修改方式
在 device/micar/common/ 的 makefile 中添加:
# device/micar/common/dcd/common.mk 或 product_common.mk
# MiCarSettings 全量 AOT 编译,加速冷启动
PRODUCT_DEX_PREOPT_MODULE_CONFIGS += \
MiCarSettings=speed或者,如果 MiCarSettings 有自己的 Android.bp/Android.mk,在其中添加:
// Android.bp 方式
android_app {
name: "MiCarSettings",
// ... 其他配置
dex_preopt: {
profile_guided: false,
// speed 模式:全量 AOT 编译所有方法
},
optimize: {
enabled: true,
},
}# Android.mk 方式
LOCAL_DEX_PREOPT := true
LOCAL_DEX_PREOPT_FLAGS := --compiler-filter=speed3.2 验证命令(不需要刷机,设备上直接验证效果)
# Step 1: 查看当前编译状态
adb shell dumpsys package com.android.car.settings | grep -A10 "Dexopt state"
# Step 2: 手动触发 speed 编译
adb shell cmd package compile -m speed -f com.android.car.settings
# Step 3: 重启后测试冷启动
adb reboot
# 等待开机完成后
adb shell am force-stop com.android.car.settings
adb shell am start-activity -W com.android.car.settings/.common.CarSettingActivities\$HomepageActivity
# Step 4: 对比 TotalTime(编译前后分别测 3 次取平均)3.3 预期效果
| 指标 | 优化前 | 优化后(speed) | 变化 |
|---|---|---|---|
| bindApplication 空白区 | 273ms | ~100-140ms | -130 ~ -170ms |
| JIT 编译开销 | 147ms(后台线程) | ~0ms | JIT 不再需要紧急编译 |
| 冷启动总时间 | 1193ms | ~1000-1060ms | -130 ~ -190ms |
| APK + OAT 体积 | ~15MB | ~25-30MB | +10-15MB |
方案二:Baseline Profile(精细化,适合体积敏感场景)
如果不想全量 speed 编译,可以只编译启动热路径。
3.4 添加 baseline-prof.txt
创建文件 app/src/main/baseline-prof.txt:
# ============================================================
# MiCarSettings Baseline Profile
# 从 Perfetto trace 采样 + JIT 编译记录中提取的启动热路径
# ============================================================
# --- Application 启动 ---
HSPLcom/android/car/settings/miauto/SettingsApplication;->**
HSPLcom/android/car/settings/miauto/MainThreadStartTask;->**
HSPLcom/android/car/settings/miauto/AsyncThreadStartTask;->**
HSPLcom/android/car/settings/miauto/common/BaseApplication;->**
# --- CarService 车辆属性注册(启动最大 binder 开销) ---
HSPLcom/android/car/settings/miauto/VehiclePermanentListener;->**
HSPLcom/android/car/settings/miauto/GlobalCarPropertyManager;->**
HSPLcom/android/car/settings/miauto/vehicle/SettingsCarPropertyManager;->**
HSPLcom/android/car/settings/miauto/vehicle/CarPropertyCacheManager;->**
HSPLcom/android/car/settings/miauto/vehicle/VehicleControlManager;->**
HSPLcom/android/car/settings/miauto/vehicle/NewBaseCarServiceManager;->**
HSPLcom/android/car/settings/miauto/CarConfigManager;->**
HSPLcom/android/car/settings/miauto/VehiclePermanentListener$**;->**
# --- Activity 启动 ---
HSPLcom/android/car/settings/common/BaseCarSettingsActivity;->**
HSPLcom/android/car/settings/miauto/HomePageActivityReal;->**
HSPLcom/android/car/settings/common/CarSettingActivities;->**
HSPLcom/android/car/settings/common/CarSettingActivities$HomepageActivity;->**
HSPLcom/android/car/settings/common/TopLevelMenuFragment;->**
# --- HUD 设置(启动时调用) ---
HSPLcom/mi/car/hud/settings/HudSettingsOTA2;->**
HSPLcom/mi/car/hud/settings/HudSettingsCompat;->**
HSPLcom/mi/car/hud/CarHudManager;->**
HSPLcom/android/car/settings/miauto/display/CarHudControlCompatManager;->**
HSPLcom/android/car/settings/miauto/display/CarHudControlCompatManager$**;->**
# --- 连接模块初始化 ---
HSPLcom/android/settingslib/bluetooth/LocalBluetoothManager;->**
HSPLcom/android/car/settings/miauto/bluetooth/MiBluetoothUtils;->**
HSPLcom/android/car/settings/miauto/hotspot/HotspotUtils;->**
HSPLcom/android/car/settings/miauto/wifi/MiCarWifiUtils;->**
# --- Preference / UI 框架 ---
HSPLcom/android/car/settings/miauto/preferences/BasicPreference;->**
HSPLcom/android/car/settings/common/PreferenceVisibleStatusManager;->**
HSPLcom/android/car/settings/miauto/common/PreferenceVisibleStatusHelperWrapper;->**
HSPLcom/android/car/settings/miauto/common/views/CarModelSwitcher;->**
HSPLcom/android/car/settings/miauto/common/views/MiCarSettings;->**
# --- Router 路由初始化 ---
HSPLcom/android/car/settings/router/manager/Router;->**
# --- License 管理 ---
HSPLcom/android/license/master/LicenseMasterManager;->**
HSPLcom/android/license/master/LicenseControlMgr;->**
# --- ConstraintLayout(JIT 编译热点 Top1,15ms+) ---
HSPLandroidx/constraintlayout/widget/ConstraintLayout;->**
HSPLandroidx/constraintlayout/widget/ConstraintLayout$LayoutParams;->**
HSPLandroidx/constraintlayout/widget/ConstraintLayout$Measurer;->**
HSPLandroidx/constraintlayout/solver/**;->**
# --- Gson(JIT 编译热点,反序列化配置数据) ---
HSPLcom/google/gson/Gson;->**
HSPLcom/google/gson/reflect/TypeToken;->**
HSPLcom/google/gson/internal/bind/ReflectiveTypeAdapterFactory;->**
HSPLcom/google/gson/stream/JsonReader;->**
HSPLcom/google/gson/internal/Excluder;->**
# --- AndroidX 基础(Fragment/Lifecycle/RecyclerView) ---
HSPLandroidx/fragment/app/FragmentActivity;->**
HSPLandroidx/fragment/app/FragmentStateManager;->**
HSPLandroidx/fragment/app/FragmentManager;->**
HSPLandroidx/lifecycle/**;->**
HSPLandroidx/recyclerview/widget/RecyclerView;->**
HSPLandroidx/appcompat/widget/AppCompatTextHelper;->**
HSPLandroidx/appcompat/widget/Toolbar;->**
# --- 日志框架 ---
HSPLcom/mi/car/logger/**;->**
HSPLmi/car/hungtask/blockmonitor/BlockMonitor;->**
HSPLmi/car/hungtask/blockmonitor/BlockMonitor$**;->**
3.5 添加 profileinstaller 依赖
在 app/build.gradle 中添加:
dependencies {
implementation "androidx.profileinstaller:profileinstaller:1.3.1"
}3.6 系统构建配合
确保构建系统使用 speed-profile 模式:
PRODUCT_DEX_PREOPT_MODULE_CONFIGS += \
MiCarSettings=speed-profile3.7 设备上验证 Baseline Profile 是否生效
# 安装后检查 profile 是否被编译
adb shell dumpsys package com.android.car.settings | grep -A10 "Dexopt state"
# 预期看到 [status=speed-profile] 或 [status=speed]
# 如果看到 [status=verify] 说明 profile 未生效
# 手动触发 profile 编译
adb shell cmd package compile -m speed-profile -f com.android.car.settings四、Gradle 本地构建适配(方案二专用)
MiCarSettings 使用 Gradle 构建(AGP 7.2.0 + compileSdkVersion 33),日常开发通过 ./gradlew assembleDcdRelease 出包后 adb install 到设备。以下是本地 Gradle 构建中集成 Baseline Profile 的完整步骤。
方案一(speed 模式)不需要改 Gradle,只改整机 makefile 即可。此章节仅适用于方案二(Baseline Profile)。
4.1 前置条件
| 项目 | 当前值 | 要求 | 说明 |
|---|---|---|---|
| AGP | 7.2.0 | ≥ 7.0 | 已满足。AGP 7.0+ 原生支持 baseline-prof.txt |
| compileSdkVersion | 33 | ≥ 30 | 已满足 |
| Kotlin | 1.8.10 | 任意 | 无要求 |
| profileinstaller | 无 | 需添加 | 见 4.2 |
4.2 Step 1:添加 profileinstaller 依赖
编辑 app/build.gradle,在 dependencies 块中添加:
dependencies {
// ... 现有依赖 ...
// Baseline Profile 安装器
// 负责在 APK 首次安装时将 baseline-prof.txt 写入 ART profile
implementation "androidx.profileinstaller:profileinstaller:1.3.1"
}作用:profileinstaller 库会在 APK 安装后、首次启动时,将 baseline-prof.txt 中声明的类和方法写入 ART 的 reference profile(/data/misc/profiles/ref/<pkg>/primary.prof)。ART 的 bg-dexopt-job(后台优化服务)检测到 profile 变更后,会触发 speed-profile 模式的 dex2oat 编译。
4.3 Step 2:创建 baseline-prof.txt
将 baseline-prof.txt 放在 app/src/main/baseline-prof.txt(AGP 默认扫描路径)。
文件内容已在方案二的 §3.4 中给出。核心是从 Perfetto trace 中提取的 ~200 个启动热路径类。
app/
├── src/
│ └── main/
│ ├── java/
│ ├── res/
│ ├── AndroidManifest.xml
│ └── baseline-prof.txt ← 新增此文件
AGP 7.x 处理流程:
- AGP 在打包时将
baseline-prof.txt编译为二进制格式,嵌入 APK 的assets/dexopt/baseline.prof profileinstaller在首次启动时读取该二进制 profile,写入 ART reference profile 目录- ART 后台服务检测到 profile 后触发
speed-profile编译
4.4 Step 3:构建并安装
# 正常 Gradle 构建
./gradlew assembleDcdRelease
# 安装到设备
adb install -r app/build/outputs/apk/dcd/release/app-dcd-release.apk
# 等待 profileinstaller 写入 profile(首次启动自动触发)
adb shell am start -n com.android.car.settings/.common.CarSettingActivities\$HomepageActivity
# 启动后等待 3-5 秒,profileinstaller 会在后台完成写入4.5 Step 4:触发编译并验证
# 1. 确认 profile 已写入
adb shell ls -la /data/misc/profiles/ref/com.android.car.settings/
# 预期看到 primary.prof 文件(非空)
# 2. 手动触发 speed-profile 编译(不等后台 dexopt)
adb shell cmd package compile -m speed-profile -f com.android.car.settings
# 3. 确认编译状态
adb shell dumpsys package com.android.car.settings | grep -A10 "Dexopt state"
# 预期看到 [status=speed-profile] 而非 [status=verify]
# 4. 测试冷启动
adb shell am force-stop com.android.car.settings
adb shell am start-activity -W com.android.car.settings/.common.CarSettingActivities\$HomepageActivity4.6 多 flavor 适配
MiCarSettings 有 3 个 flavor(dcd/xcd/global),baseline-prof.txt 放在 src/main/ 下会被所有 flavor 共享。
如果不同 flavor 的启动路径有差异(比如 global 版少了某些车辆模块),可以按 flavor 放置:
app/src/main/baseline-prof.txt ← 公共热路径
app/src/dcd/baseline-prof.txt ← dcd 额外热路径(可选)
app/src/xcd/baseline-prof.txt ← xcd 额外热路径(可选)
AGP 会自动合并 main + flavor 的 profile。建议先只放 src/main/ 即可,三个 flavor 启动路径高度一致。
4.7 日常开发中的注意事项
| 场景 | 说明 |
|---|---|
adb install 安装 | profileinstaller 会在首次启动时写入 profile,但不会立即编译。需手动 cmd package compile -m speed-profile -f 或等后台 dexopt |
./gradlew installDcdDebug | debug 包也会包含 baseline-prof.txt,profileinstaller 正常工作 |
| 添加新类/新功能后 | 如果新代码在启动路径上,需更新 baseline-prof.txt 添加对应类。遗漏只是性能退化,不影响功能 |
| 整机刷机后 | 通过整机 makefile 的 speed-profile 配置,dex2oat 在系统构建时已完成,无需等 profileinstaller |
| profile 文件语法错误 | AGP 编译时会报 warning(不会 fail),错误行被忽略 |
4.8 与整机构建的衔接
本地 Gradle 构建 和 整机 AOSP 构建 是两条独立流水线,Baseline Profile 需要两边都配:
| 环节 | 配置 | 作用 |
|---|---|---|
| Gradle 构建 | baseline-prof.txt + profileinstaller | 将 profile 嵌入 APK |
| 整机 makefile | PRODUCT_DEX_PREOPT_MODULE_CONFIGS += MiCarSettings=speed-profile | 系统构建时 dex2oat 读取 APK 内嵌的 profile 编译 |
| 设备上 install | profileinstaller 写入 + 后台 dexopt | 开发调试时手动安装场景 |
如果只改了 Gradle 没改 makefile:整机刷机后 dex2oat 仍用默认的 verify 模式,profile 不生效(需要等 profileinstaller 首次启动 + 后台 dexopt,可能要几小时)。
如果只改了 makefile 用 speed 模式:不需要 Gradle 任何改动,全量 AOT 编译覆盖一切。
4.9 Gradle 构建验证 checklist
# ✅ 确认 baseline-prof.txt 被打入 APK
unzip -l app/build/outputs/apk/dcd/release/app-dcd-release.apk | grep baseline
# 预期看到 assets/dexopt/baseline.prof 或 assets/dexopt/baseline.profm
# ✅ 确认 profileinstaller 被打入 APK
unzip -l app/build/outputs/apk/dcd/release/app-dcd-release.apk | grep profileinstaller
# 预期看到 profileinstaller 相关 class
# ✅ 确认 profile 语法正确(AGP 编译时无 warning)
./gradlew assembleDcdRelease 2>&1 | grep -i "baseline\|profile"
# 不应该看到 ERROR,WARNING 是正常的(部分类可能在当前构建中不存在)五、方案对比与建议
| 维度 | 方案一(speed) | 方案二(Baseline Profile) |
|---|---|---|
| 改动量 | 1 行 makefile | baseline-prof.txt + 1 行依赖 + makefile |
| 编译范围 | 所有方法 | 仅启动热路径(~200 类) |
| 冷启动提速 | ~130-190ms | ~100-150ms |
| OAT 体积增长 | +10-15MB | +3-5MB |
| 维护成本 | 无 | 需定期更新 profile |
| 热启动影响 | 无(已编译的方法不影响) | 无 |
建议:MiCarSettings 是车机系统应用,存储空间充裕,优先使用方案一(speed 模式)。简单、无维护成本、效果最好。
如果整机对 system 分区体积有严格限制,再退回方案二。
六、快速验证脚本
#!/bin/bash
# verify_baseline_profile.sh
# 在设备上快速验证 AOT 编译对冷启动的影响
PKG="com.android.car.settings"
ACT="com.android.car.settings/.common.CarSettingActivities\$HomepageActivity"
echo "=== 1. 当前编译状态 ==="
adb shell dumpsys package $PKG | grep -A10 "Dexopt state"
echo ""
echo "=== 2. 基准测试(当前状态) ==="
for i in 1 2 3; do
adb shell am force-stop $PKG
sleep 1
result=$(adb shell am start-activity -W $ACT 2>&1 | grep TotalTime)
echo " Run $i: $result"
done
echo ""
echo "=== 3. 触发 speed 编译 ==="
adb shell cmd package compile -m speed -f $PKG
echo ""
echo "=== 4. 编译后测试 ==="
adb shell am force-stop $PKG
sleep 2
for i in 1 2 3; do
adb shell am force-stop $PKG
sleep 1
result=$(adb shell am start-activity -W $ACT 2>&1 | grep TotalTime)
echo " Run $i: $result"
done
echo ""
echo "=== 5. 编译后状态 ==="
adb shell dumpsys package $PKG | grep -A10 "Dexopt state"
echo ""
echo "=== 6. 恢复原状(可选) ==="
echo " # adb shell cmd package compile -m verify -f $PKG"七、FAQ
Q: speed 编译会影响 OTA 升级吗? A: 不会。OTA 后系统会重新对所有 app 做 dex2oat,compiler-filter 配置会在新版本构建时生效。
Q: 已经有 WITH_DEXPREOPT=true,为什么还没编译?
A: WITH_DEXPREOPT=true 只是开启了 dex preopt 框架,默认使用的 compiler-filter 是 verify(仅验证类,不编译方法)。需要显式指定 speed 或 speed-profile 才会编译方法体。
Q: 为什么 dalvik-cache 有 OAT 文件但方法没编译?
A: OAT 文件在 verify 模式下只包含 verified dex(vdex),方法体仍是 dex 字节码,运行时走解释器。speed 模式的 OAT 文件才包含编译后的机器码。
Q: Baseline Profile 里的 HSPL 是什么意思?
A: H=Hot(热方法,需 AOT 编译),S=Startup(启动路径),P=Post-startup(启动后仍频繁调用),L=方法签名行。HSPL = Hot + Startup + Post-startup + Line。
Q: 方案一和方案二可以同时用吗?
A: 如果使用 speed 模式,会编译所有方法,Baseline Profile 的内容就被完全覆盖了,不需要同时配置。两者取其一即可。