02 · 应用启动与首页架构

一、App 入口

角色路径
Applicationcom.android.car.settings.miauto.SettingsApplicationapp/src/main/java/com/android/car/settings/miauto/SettingsApplication.java
Launcher 入口CarSettingActivities$HomepageActivityHomePageActivityRealapp/.../miauto/HomePageActivityReal.java
Launcher 别名Settings_Launcher_Homepage(响应 MAIN+LAUNCHER)manifest
备用 HOMEFallbackHome(priority -900)app/.../FallbackHome.java
语音中转DeeplinkActivityapp/.../miauto/common/DeeplinkActivity.kt

进程身份:系统特权应用 —— sharedUserId="android.uid.system" + coreApp="true" + platform.keystore 平台签名。这意味着它能访问系统 API、Car Service、隐藏 API。

二、启动初始化链路

继承:SettingsApplicationBaseApplication(base/settingsBaseLib) → Application

SettingsApplication 头部有路由合并注解(驱动 RouterHelper 生成):

@Module(SettingsConstant.TAG)
@Modules({ TAG, TAG_AUTOPILOT, TAG_Light, TAG_VEHICLECONTROL, TAG_LOCK,
           TAG_DRIVING, TAG_CHARGE, TAG_IOT, TAG_XIAOAI, TAG_DISPLAY,
           TAG_SAFETY_SERVICE, TAG_CONNECTION, TAG_VOLUME, TAG_SYSTEM })
public class SettingsApplication extends BaseApplication { ... }

onCreate 执行顺序

SettingsApplication.onCreate()
  │
  ├─ DfxManager.init()                      // DFX 监控
  ├─ checkAppMatchRom("application#onCreate") // ROM 与构建一致性校验
  ├─ registerActivityLifecycleCallbacks(AppLifecycleTracker)  // UI 模式追踪
  ├─ AutoDensityConfig.init()               // miuix 自动密度
  ├─ initLogging()                          // MLog (mi.car.logger)
  │
  └─ AppStartTaskDispatcher.start()  ← 核心:主/异步双任务并行
        │
        ├─ MainThreadStartTask (主线程, 注释 ~71ms / 实测 ~247ms)
        │     ├─ GlobalCarPropertyManager.init() + VehiclePermanentListener.init()  // 车辆配置加载
        │     ├─ MiCarSettings.setConfigManager(CarConfigManager)                   // 配置字
        │     ├─ LicenseControlMgr / MiCarLcsControlHelper                          // 许可证
        │     ├─ PreferenceVisibleStatusHelperWrapper 注册 4 个可见性 helper
        │     ├─ initConnection()  // 蓝牙 + 热点 + WiFi(必须主线程,实测蓝牙 ~188ms)
        │     ├─ Router.getInstance().init(mApplication)  // 路由表合并
        │     └─ initInAsync() → ASYNC.execute(...)  // 二次派发:License/MisDevice/儿童座椅
        │
        ├─ AsyncThreadStartTask (ASYNC 线程池, 注释 ~61ms)
        │     ├─ SettingsTrack.init()                // 埋点
        │     ├─ VoiceAssistProvider.initialize()    // 语音
        │     ├─ initCustomSyncHandler()             // 11 个账号同步 handler
        │     ├─ registerAccountsUpdateListener()
        │     ├─ VoiceSearchManager.SearchInitBuilder // 语音搜索
        │     ├─ initHudConfig() + initLinkManager() + MQTTManagerHelper.initMQTTManager()
        │     ├─ GlobalCarAudioControlManager.init()
        │     └─ TrackTrigger.init() → countDown()
        │
        └─ CountDownLatch.await(1000ms)  // 阻塞直到异步完成或超时

⚠️ 启动风险点(详见 docs/启动慢白屏分析)

  1. 白屏元凶CarPropertyCacheManager.java:192 —— 主线程同步 registerCallback,Activity 阶段 44 个 callback 占 893ms。优化方案:扔单线程 executor。
  2. 蓝牙同步初始化 ~188ms(主线程大头)。
  3. 二次派发盲区MainThreadStartTask.initInAsyncAsyncThreadStartTaskASYNC.execute 派发的任务不受 latch 约束,onCreate 返回后仍可能在跑(竞态)。
  4. CountDownLatch 超时 1000ms:异步卡死时主线程阻塞 1 秒。
  5. ASYNC 线程池无界(0 核心、MAX 最大):高并发可能线程爆炸。

三、首页:双面板架构

承载布局 car_setting_activity.xml

RelativeLayout#car_settings_activity_wrapper
├── CarModelSwitcher                (3D 车辆模型, 右侧)
├── FrameLayout#content_menu_wrapper (可选第三面板, 默认隐藏)
├── <fragment>#top_level_menu       ← 左栏: TopLevelMenuFragment
├── View#top_level_divider
└── LinearLayout#content_ll
    ├── Toolbar
    └── FrameLayout#fragment_container  ← 右栏: 当前选中 Fragment

BaseCarSettingsActivity.onCreate 加载,启动时:

  • 左栏挂载 TopLevelMenuFragment
  • 右栏挂载初始 Fragment = VehicleControlSettingsFragment(车辆控制,默认选中第一项)

左栏:TopLevelMenuFragment

  • 继承 SettingsFragmentBaseXmlParserSettingsFragment(基于 micarx.preference.PreferenceFragmentCompat
  • 加载 R.xml.miauto_top_level_menu_fragment(region/cn 或 region/global 两份)
  • CustomFocusLayoutManager 拦截车机 DPAD 方向键
  • MenuHighlightableAdapter 处理左↔右联动高亮
  • performMenuClick(prefKey) 驱动右栏切换

菜单项声明(XML 驱动)

菜单项通过 PreferenceScreen XML 声明式定义,每项是一个 VoiceAssistMenuPreference

<!-- app/src/region/cn/res/xml/miauto_top_level_menu_fragment.xml -->
<com.android.car.settings.miauto.preferences.VoiceAssistMenuPreference
    android:fragment="com.android.micar.settings.energy.EnergyManagerFragment"
    android:key="@string/psk_settings_energy_entry"
    android:title="@string/top_level_menu_energy_management"
    settings:controller="com.android.car.settings.common.DefaultRestrictionsPreferenceController"
    settings:menuIcon="@drawable/micarx_icon_charge_circle"
    settings:hiddenFeatures="@string/micar_common_settings_lemans_ece" />
  • android:fragment = 点击后在右栏实例化的 Fragment
  • android:key = @string/psk_*,用于高亮/路由
  • settings:controller = 控制可见性的 Controller(多为 DefaultRestrictionsPreferenceController
  • settings:hiddenFeatures = ECE 合规隐藏标记

国内菜单(15 项):车辆控制 → 灯光 → 门窗锁 → 驾驶偏好 → 辅助驾驶 → 安全 → 充电&能量 → 连接 → 显示 → 声音 → 智能设备 → 智能语音 → 服务 → 系统 → 个人中心。

海外菜单(17 项):额外增加 位置(LocationSettingsFragment)和 隐私(PrivacySettingsFragment),辅助驾驶换 GlobalAutopilotSettingsFragment

四、外部入口 → 首页的三条路径

路径 A:桌面/Launcher 启动

Settings_Launcher_Homepage 别名(MAIN+LAUNCHER)→ HomepageActivity → 默认选中车辆控制。

路径 B:深链接(外部 App / adb)

Intent(action=android.settings.SETTINGS, data="carsettings://homepage/?subPage=volume")BaseCarSettingsActivity.onNewIntenthandlePageSwitchIntentPageRouterConfig.getPskByPageId(subPage) 把 alias 转成 preference key → TopLevelMenuFragment.performMenuClick(prefKey) 高亮 + 切右栏

路径 C:语音(小爱)

小爱返回 widgetId/subPage Bundle → 启动 DeeplinkActivity(透明中转,action android.settings.VEHICLE_PROPERTY_DELIVER)→ 转发到 HomepageActivity → 走路径 B。

内部下钻子页(Fragment 内部点击进二级页)不走 Router,而是 SubSettingsActivity.newInstance(ctx, fragment) 在新 Activity 打开。