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,共 147msJIT 在启动期间紧急编译,来不及
解释器采样244 次 / 2265 总采样(10.8%)大量方法在以解释模式运行
bindApplication 空白区273ms48.6% 的时间花在未被 atrace 标记的类加载+解释执行

2.2 JIT 编译热点 Top 10

启动期间 JIT 线程紧急编译的方法(说明这些方法正在以解释模式运行,影响启动):

JIT 编译耗时方法
15.18msConstraintLayout.applyConstraintsFromLayoutParams()
10.38msConstraintWidget.applyConstraints()
9.60msReflectiveTypeAdapterFactory.getBoundFields() (Gson)
5.67msConstraintWidget.addToSolver()
5.26msConstraintWidgetContainer.layout()
5.12msTypeToken.<init>() (Gson)
4.09msBasicMeasure.solverMeasure()
4.02msBasicPreference.initPreference() (Settings 自有)
3.60msConstraintLayout$Measurer.measure()
3.41msConstraintLayout$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,默认走 verifyquicken 模式——只验证类不编译方法。

三、适配方案

方案一:系统构建指定 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=speed

3.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(后台线程)~0msJIT 不再需要紧急编译
冷启动总时间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-profile

3.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 前置条件

项目当前值要求说明
AGP7.2.0≥ 7.0已满足。AGP 7.0+ 原生支持 baseline-prof.txt
compileSdkVersion33≥ 30已满足
Kotlin1.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 处理流程

  1. AGP 在打包时将 baseline-prof.txt 编译为二进制格式,嵌入 APK 的 assets/dexopt/baseline.prof
  2. profileinstaller 在首次启动时读取该二进制 profile,写入 ART reference profile 目录
  3. 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\$HomepageActivity

4.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 installDcdDebugdebug 包也会包含 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
整机 makefilePRODUCT_DEX_PREOPT_MODULE_CONFIGS += MiCarSettings=speed-profile系统构建时 dex2oat 读取 APK 内嵌的 profile 编译
设备上 installprofileinstaller 写入 + 后台 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 行 makefilebaseline-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(仅验证类,不编译方法)。需要显式指定 speedspeed-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 的内容就被完全覆盖了,不需要同时配置。两者取其一即可。