Android 系统 Activity Embedding 架构解析与工程实践

张开发
2026/4/15 6:49:18 15 分钟阅读

分享文章

Android 系统 Activity Embedding 架构解析与工程实践
我们是由枫哥组建的IT技术团队成立于2017年致力于帮助IT从业者提供实力成功入职理想企业我们提供一对一学习辅导由知名大厂导师指导分享Java技术、参与项目实战等服务并为学员定制职业规划全面提升竞争力过去8年我们已成功帮助数千名求职者拿到满意的OfferIT枫斗者、IT枫斗者-Java面试突击。Android 系统 Activity Embedding 架构解析与工程实践技术定位Activity Embedding 并非简单的 UI 适配方案而是 Android 窗口管理系统Window Manager在大屏设备上的任务窗格Task Fragmentation重组机制。本文基于 AOSP 13 与 Jetpack WindowManager 1.1剖析系统设置Settings应用的嵌入式 Activity 实现架构。一、架构基础Activity Embedding 的窗口管理模型1.1 窗口层级与任务分割在传统 Android 窗口模型中一个 Task 包含一栈 ActivitiesBack Stack。Activity Embedding 通过SplitController在 WindowManager 层引入虚拟容器SplitContainer将单一 Task 的水平空间划分为 Primary 与 Secondary 两个逻辑区域传统模型手机Task └── Activity A → Activity B → Activity C全栈覆盖 Embedding 模型平板/折叠屏 Task └── SplitContainer根容器 ├── Primary Container左侧/上层 │ └── SettingsHomepageActivity导航层 └── Secondary Container右侧/下层 └── NetworkDashboardActivity详情层核心机制非侵入式重组Activities 仍保留独立生命周期与上下文但窗口布局由系统EmbeddingSplitActivityStack管理配置驱动通过SplitRule定义空间分配策略Ratio、Alignment、ClearTop 行为响应式折叠设备姿态Posture变化时SplitController自动触发容器重组展开→并排折叠→堆叠1.2 系统设置Settings的架构适配价值在 AOSP Settings 中引入 Embedding 的架构意义维度传统方案PhoneEmbedding 方案Large Screen导航模式全屏跳转Intent FLAG_ACTIVITY_NEW_TASK并排呈现保留导航上下文状态保持依赖onSaveInstanceState序列化实例保活Dual-Instance 共存配置变更Activity 重建Recreate容器重布局SplitAttributes 更新代码侵入需维护多分支values-large/layout单一代码库运行时规则决策二、工程化集成AOSP 编译配置与初始化2.1 构建系统配置Android.bp在 AOSP 环境下Settings 应用需显式依赖 Jetpack WindowManager 扩展库。注意区分androidx.windowJava 库与androidx.window.extensions系统扩展服务// packages/apps/settings/Android.bp android_library { name: Settings-core, srcs: [src/**/*.java], static_libs: [ // WindowManager 核心库包含 SplitController androidx.window_window-java, // 系统扩展提供 AOSP 特有的 Split 行为 androidx.window.extensions, // 依赖注入用于 RuleController 单例 jsr330, ], sdk_version: system_current, min_sdk_version: 32, // Android 12L 最低要求 } android_app { name: Settings, static_libs: [Settings-core], privileged: true, // Settings 为特权应用需访问系统级 Window 扩展 required: [ androidx.window.extensions, // 确保系统镜像包含扩展服务 ], }关键配置解析min_sdk_version: 32Embedding API 要求 Android 12LAPI 32以上privileged: trueSettings 应用需访问SplitController的系统级实现非 SDK 接口限制2.2 应用级初始化与规则注入在SettingsApplication中完成RuleController初始化需注意进程级单例与主线程执行要求// packages/apps/settings/src/com/android/settings/SettingsApplication.javapublicclassSettingsApplicationextendsApplication{privatestaticfinalStringTAGSettingsEmbedding;OverridepublicvoidonCreate(){super.onCreate();// 延迟初始化至主线程 Looper 就绪避免 WindowToken 未绑定newHandler(Looper.getMainLooper()).post(()-{if(ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this)){Log.i(TAG,Initializing Activity Embedding rules...);newActivityEmbeddingRulesController(this).initRules();}});}}// RuleController 封装线程安全单例publicclassActivityEmbeddingRulesController{privatefinalContextmContext;privatefinalRuleControllermRuleController;privatefinalSplitControllermSplitController;publicActivityEmbeddingRulesController(Contextcontext){mContextcontext.getApplicationContext();// RuleController 为单例管理所有 SplitRule 生命周期mRuleControllerRuleController.getInstance(mContext);// SplitController 负责与系统 WindowManager 服务通信mSplitControllerSplitController.getInstance(mContext);}publicvoidinitRules(){// 原子化注册规则顺序敏感Placeholder 需在 Pair 之前registerHomepagePlaceholderRule();registerSplitPairRules();registerAlwaysExpandRules();// 注册设备姿态监听折叠屏适配registerPostureChangeCallback();}}三、核心规则配置SplitRule 的精细化工程3.1 主页占位规则SplitPlaceholderRule场景大屏设备首次启动 Settings 时左侧显示导航页Homepage右侧需自动填充默认详情页如 NetworkDashboard避免右侧空白。privatevoidregisterHomepagePlaceholderRule(){// 1. 定义触发范围哪些 Activity 激活此规则SetActivityFilterprimaryFiltersnewHashSet();primaryFilters.add(newActivityFilter(newComponentName(mContext,SettingsHomepageActivity.class),null// 允许任意 Intent Action));primaryFilters.add(newActivityFilter(newComponentName(mContext,Settings.class),// 别名入口null));// 2. 定义占位 ActivitySecondary 容器默认展示IntentplaceholderIntentnewIntent(mContext,NetworkDashboardActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TOP).putExtra(SettingsActivity.EXTRA_IS_SECOND_LAYER_PAGE,true);// 3. 构建分屏属性SplitAttributesSplitAttributessplitAttributesnewSplitAttributes.Builder().setSplitType(SplitAttributes.SplitType.ratio(0.3636f))// 黄金比例近似值.setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT).setAnimationParams(newSplitAttributes.AnimationParams.Builder().setAnimationBackgroundColor(Color.TRANSPARENT).build()).build();// 4. 构造并注册规则SplitPlaceholderRuleplaceholderRulenewSplitPlaceholderRule.Builder(primaryFilters,placeholderIntent).setMinWidthDp(ActivityEmbeddingUtils.MIN_SPLIT_WIDTH_DP)// 600dp.setMinSmallestWidthDp(ActivityEmbeddingUtils.MIN_SMALLEST_WIDTH_DP)// 600dp.setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)// 允许竖屏分屏.setSticky(false)// Primary 跳转时是否保留 Placeholderfalse跟随替换.setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ADJACENT)// 占位符关闭时连带关闭 Primary.setDefaultSplitAttributes(splitAttributes).build();mRuleController.addRule(placeholderRule);}关键参数工程决策setSticky(false)当用户从 Homepage 进入深层设置如 Apps右侧占位符应自动替换为具体页面而非保留 NetworkDashboardsetFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ADJACENT)确保用户关闭右侧详情时左侧导航页同步关闭避免空壳 Activity 残留3.2 Activity 对规则SplitPairRule场景定义特定 Activity 对之间的分屏行为如从 Homepage 点击 Wi-Fi 进入 NetworkDashboard。privatevoidregisterSplitPairRules(){// Homepage → Any Dashboard通用规则SetActivityFilterprimaryFiltersnewHashSet();primaryFilters.add(newActivityFilter(newComponentName(mContext,SettingsHomepageActivity.class),null));SetActivityFiltersecondaryFiltersnewHashSet();// 通配所有 DashboardActivity通过 Base 类或 Intent FiltersecondaryFilters.add(newActivityFilter(newComponentName(mContext,DashboardFragment.class),// 实际应为具体 Activityandroid.settings.SETTINGS_EMBED_DEEP_LINK// 使用特定 Action 标记可嵌入页面));SplitPairRulepairRulenewSplitPairRule.Builder(primaryFilters,secondaryFilters).setMinWidthDp(600).setClearTop(false)// 保留 Secondary 栈历史支持多层导航.setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)// 关闭详情不退出导航.setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)// 退出导航连带关闭详情.setDefaultSplitAttributes(newSplitAttributes.Builder().setSplitType(SplitAttributes.SplitType.ratio(0.5f))// 1:1 均分.build()).build();mRuleController.addRule(pairRule);}3.3 全屏展开规则ActivityRule场景某些设置页面如全局搜索、全屏向导必须占据整个 Task 窗口即使在大屏设备上。privatevoidregisterAlwaysExpandRules(){// 搜索页面全屏避免分屏导致搜索体验割裂if(FeatureFlagUtils.isEnabled(mContext,FeatureFlags.SETTINGS_SEARCH_ALWAYS_EXPAND)){SetActivityFilterexpandFiltersnewHashSet();expandFilters.add(newActivityFilter(newComponentName(mContext,SearchActivity.class),null));ActivityRuleexpandRulenewActivityRule.Builder(expandFilters).setAlwaysExpand(true)// 强制全屏忽略 Split 规则.build();mRuleController.addRule(expandRule);}}四、运行时适配配置变更与折叠屏处理4.1 动态 SplitAttributes 更新当设备旋转或折叠时需动态调整分屏比例。通过SplitAttributesCalculator实现运行时布局策略privatevoidregisterDynamicSplitConfig(){mSplitController.setSplitAttributesCalculator(params-{// 获取当前窗口指标WindowMetricsparentMetricsparams.getParentWindowMetrics();intwidthparentMetrics.getBounds().width();intheightparentMetrics.getBounds().height();booleanisPortraitheightwidth;// 折叠屏状态检测通过 FoldingFeatureListFoldingFeaturefoldingFeaturesparams.getFoldingFeatures();booleanisHalfOpenedfoldingFeatures.stream().anyMatch(f-f.getState()FoldingFeature.State.HALF_OPENED);if(isHalfOpenedfoldingFeatures.get(0).getOrientation()FoldingFeature.Orientation.HORIZONTAL){// 桌面模式TableTop上下分屏导航在上详情在下returnnewSplitAttributes.Builder().setSplitType(SplitAttributes.SplitType.ratio(0.4f)).setLayoutDirection(SplitAttributes.LayoutDirection.TOP_TO_BOTTOM).build();}elseif(isPortrait){// 竖屏手机模式堆叠实际为 Activity 覆盖保持 Back Stack 逻辑returnnewSplitAttributes.Builder().setSplitType(SplitAttributes.SplitType.EXPAND_CONTAINER).build();}else{// 横屏平板左右分屏3:7 比例给详情更多空间returnnewSplitAttributes.Builder().setSplitType(SplitAttributes.SplitType.ratio(0.3f)).build();}});}4.2 生命周期与状态同步Embedding 模式下Primary 与 Secondary Activities 处于同一 Task但具有独立生命周期。需特别注意// Secondary Activity如 NetworkDashboard中检测嵌入模式publicclassNetworkDashboardActivityextendsSettingsActivity{OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);// 检测当前是否处于嵌入分屏状态SplitController.getInstance(this).isActivityEmbedded(this).addListener(Runnable::run,isEmbedded-{if(isEmbedded){// 嵌入模式禁用独立任务导航如最近任务列表分离setTaskDescription(newActivityManager.TaskDescription(null,null,Color.TRANSPARENT// 透明在最近任务中显示为整体));// 调整 UI隐藏返回键因 Primary 已提供导航if(getActionBar()!null){getActionBar().setDisplayHomeAsUpEnabled(false);}}});}OverridepublicvoidonConfigurationChanged(ConfigurationnewConfig){super.onConfigurationChanged(newConfig);// 分屏比例变化时动态调整 RecyclerView 列数intscreenWidthDpnewConfig.screenWidthDp;if(screenWidthDp600){mRecyclerView.setLayoutManager(newLinearLayoutManager(this));}else{mRecyclerView.setLayoutManager(newGridLayoutManager(this,2));}}}五、调试与诊断系统级故障排查5.1 Shell 命令调试AOSP 提供wmWindow ManagerShell 命令用于诊断 Embedding 状态# 查看当前 Task 的分屏容器结构adb shell dumpsys activity containers|grep-A20Task ## 强制启用/禁用 Embedding 功能覆盖 FeatureFlagadb shell settings put global settings_support_large_screen1# 查看 SplitController 规则注册情况adb shell dumpsys window policy|grep-iembedding# 模拟大屏尺寸无需物理设备adb shell wm size 2200x1600# 模拟平板adb shell wm density2805.2 常见问题与根因分析现象根因诊断方法分屏未触发Activity 全屏覆盖minWidthDp限制未满足当前屏幕宽度 600dp检查SplitController.isSplitSupported()返回值与当前屏幕Configuration.screenWidthDp占位符不显示右侧空白PlaceholderRule 中sticky与finishPrimaryWithPlaceholder冲突检查 Placeholder Activity 是否被其他 Intent Filter 意外触发折叠/展开时 Activity 重建未正确处理android:configChanges中的screenSize与smallestScreenSize在 AndroidManifest 中声明configChanges并在onConfigurationChanged中更新布局Secondary Activity 返回键退出整个应用SplitPairRule中finishSecondaryWithPrimary设置为ALWAYS改为NEVER或ADJACENT并确保 Secondary Activity 正确处理 Back 事件5.3 内存与性能考量双重实例开销Embedding 模式下同一 Activity 类可能同时存在两个实例Primary/Secondary需确保静态资源无内存泄漏过渡动画性能分屏切换涉及SurfaceControl的 Layer 重组在低端设备上可能掉帧。建议禁用复杂转场动画overridePendingTransition(0,0);// 在嵌入切换时禁用默认动画六、安全与权限考量由于 Settings 为系统特权应用需注意Intent 拦截风险PlaceholderRule 中的 Intent 若包含敏感 Extra需确保目标 Activity 导出权限控制android:exportedfalse 特定权限保护多用户支持Embedding 规则按用户进程隔离需在ActivityEmbeddingRulesController中监听用户切换广播重新初始化规则画中画冲突若 Secondary Activity 支持 PiPPicture-in-Picture需处理从分屏到 PiP 的窗口模式切换冲突七、总结Activity Embedding 在 AOSP Settings 中的实施本质上是将单栈导航模型重构为主-从Master-Detail并行模型。其工程价值不仅在于 UI 适配更在于架构解耦通过声明式规则SplitRule替代条件分支代码实现单一源码兼容多形态设备状态保活避免传统replace()或startActivity()带来的实例重建提升导航性能系统级整合深度集成 WindowManager 的 FoldingFeature 与 Resize 行为为折叠屏提供原生级体验⭐️推荐:Offer训练营介绍Java 面试 后端通用面试八股文Java后端企业级实战面试Java后端校招算法学习

更多文章