Android 系统多窗口模式实现
参考链接
https://blog.csdn.net/qq_34211365/article/details/121465211
https://www.cnblogs.com/wzjhoutai/p/6873790.html
https://blog.csdn.net/m0_37602827/category_10620827.html
https://www.jianshu.com/p/cdbbeb105a66
1 窗口概念
Android 系统中的窗体是屏幕上的一块用于绘制各种UI元素,并能够响应用户输入的一块矩形区域,如 Activity 的界面、壁纸、状态栏导航栏以及 Toast、Dialog 等都是窗体。
在 Android 中,窗体是相当于占有一个 Surface 实例的显示区域,framework 层通过 WMS 负责为全部窗体分配 Surface,管理 Surface 的显示顺序(Z-order)、位置尺寸,控制窗体的动画,同时还是输入系统的重要的中转站。
以下是 frameworks/base/services/core/java/com/android/server/wm 包下和窗口容器相关的类结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dPEcjvdA-1646268866796)(WindowContainer.png)]
Android 中,Activity 窗口通过容器的概念来进行划分,可以大概理解分为 AMS 端和 WMS 端,AMS 端负责管理 Activity 实例, 其顶层容器类是 ConfigurationContainer,WMS 端负责管理窗口,其顶层容器类是 WindowContainer, 而 ConfigurationContainer 是所有容器的顶级父类容器,根据不同的类型容器可抽象出特定的容器类。
AMS 端容器结构: ActivityDisplay -> ActivityStack -> TaskRecord -> ActivityRecord;
WMS 端容器结构:WindowContainer -> TaskStack -> Task -> AppWindowToken;
在AMS、WMS端的容器结构中,ActivityStack 与 TaskStack、TaskRecord 与 Task、ActivityRecord 与 AppWindowToken 对应;
ActivityDisplay: 对应系统中的显示器,可持有多个附加的 ActivityStack;
ActivityStack:管理 Activity 的堆栈, 父级容器是 ActivityDisplay,子级容器是 TaskRecord,可包含多个 TaskRecord;
TaskRecord: 相当于包含一组多个Activity 的任务, 包含多个 ActivityRecord;
ActivityRecord: 对应单个Activity实例;
WindowContainer: 以层次结构形式保存和管理窗口的配置类, 是 TaskStack、Task、AppWindowToken 的基类。
而在 Android 多窗口中,主要分为分屏、画中画、自由窗口三种模式,多窗口的核心主要是分栈和划分栈边界。Android 10 通过划分窗口的启动模式来对 ActivityStack 堆栈来进行划分:
// frameworks/base/core/java/android/app/WindowConfiguration.java
public static final int WINDOWING_MODE_UNDEFINED = 0; //未定义
public static final int WINDOWING_MODE_FULLSCREEN = 1; //普通全屏窗口
public static final int WINDOWING_MODE_PINNED = 2; //画中画
public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY = 3; //分屏主窗口
public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY = 4; //分屏副窗口
public static final int WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
public static final int WINDOWING_MODE_FREEFORM = 5; // 自由窗口
2 自由窗口启动
自由窗口没有完全适配应用的控件布局显示,导致应用内无法正常显示。我们需要对系统原生自由窗口的进行修改,以适配应用在自由窗口内的布局显示,这里通分析自由窗口的相关流程来分析如何实现布局适配。
// 可通过以下代码启动一个自由窗口
public static void startFreeForm(Context context, Intent intent) {
intent.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK);
ActivityOptions options = ActivityOptions.makeBasic();
setLaunchWindowingMode(options, WindowConfiguration.WINDOWING_MODE_FREEFORM);
options.setLaunchBounds(new Rect(0, 0, 1080, 1920));
context.startActivity(intent, options.toBundle());java
}
跳过一些流程,直接来到 ActivityStarter.startActivityUnchecked() 方法
// frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
// Note: This method should only be called from {@link startActivity}.
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
ActivityRecord[] outActivity, boolean restrictedBgActivity) {
// // 省略部分
if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
newTask = true;
// 第一步:创建 ActivityStack、TaskRecord、ActivityRecord 相关实例,并维护容器之间的关系
result = setTaskFromReuseOrCreateNewTask(taskToAffiliate);
} else if (mSourceRecord != null) {
result = setTaskFromSourceRecord();
} else if (mInTask != null) {
result = setTaskFromInTask();
} else {
result = setTaskToCurrentTopOrCreateNewTask();
}
// 第二步: 在ActivityStack顶部添加新Activity, 创建对应activity的AppWindowToken容器。
mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
mOptions);
if (mDoResume) {
final ActivityRecord topTaskActivity =
mStartActivity.getTaskRecord().topRunningActivityLocked();
if (!mTargetStack.isFocusable()
|| (topTaskActivity != null && topTaskActivity.mTaskOverlay
&& mStartActivity != topTaskActivity)) {
mTargetStack.ensureActivitiesVisibleLocked(mStartActivity, 0, !PRESERVE_WINDOWS);
mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
} else {
if (mTargetStack.isFocusable()
&& !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) {
mTargetStack.moveToFront("startActivityUnchecked");
}
// 第三步:恢复ActivityStack顶部的activity,使其可见可交互(resumed 触发添加根DecorView过程)
mRootActivityContainer.resumeFocusedStacksTopActivities(
mTargetStack, mStartActivity, mOptions);
}
}
// 省略部分
}
2.1 第一步分析
第一步:创建 ActivityStack、TaskRecord、ActivityRecord 相关实例,并维护容器之间的关系
ActivityStarter.setTaskFromReuseOrCreateNewTask()
1.1#-> ActivityStarter.computeStackFocus() # return ActivityStack
-> ActivityStarter.getLaunchStack()
-> RootActivityContainer.getLaunchStack()
-> RootActivityContainer.getActivityDisplayOrCreate() # return ActivityDisplay
-> RootActivityContainer.getActivityDisplay(displayId)
-> new ActivityDisplay() # return ActivityDisplay
-> RootActivityContainer.addChild(ActivityDisplay) #RootActivityContainer 包含 ActivityDisplay
-> RootActivityContainer.positionChildAt()
-> RootWindowContainer.positionChildAt()
-> ActivityDisplay.getOrCreateStack # return ActivityStack
-> ActivityDisplay.createStackUnchecked()
-> new ActivityStack()
-> ActivityStack.setActivityType()
-> ActivityStack.createTaskStack() # return TaskStack
-> DisplayContent.setStackOnDisplay(mTaskStack)
-> TaskStackContainers.addStackToDisplay(TaskStack) # TaskStackContainers包含与应用程序(活动)相关的所有窗口容器
-> ActivityStack.setWindowingMode()
-> ActivityDisplay.addChild(ActivityStack) # ActivityDisplay 包含 ActivityStack
1.2#-> ActivityStack.createTaskRecord()
-> TaskRecord.createTask()
-> TaskStack.addTask()
-> TaskStack.addChild(Task) # TaskStack 包含 Task
-> Task.setParent(WindowContainer);
-> WindowContainer.onParentChanged()
-> SurfaceControl.build()
-> SurfaceControl$Transaction.show(mSurfaceControl)
-> WindowContainer.updateSurfacePosition()
-> SurfaceControl$Transaction.setPosition()
-> SurfaceControl$Transaction.setMatrix()
1.3#-> ActivityStarter.addOrReparentStartingActivity()
-> TaskRecord.addActivityToTop(ActivityRecord) # return ActivityRecord
-> ActivityRecord.setTask(TaskRecord)
1.4#-> ActivityStarter.updateBounds()
-> TaskRecord.updateOverrideConfiguration(bounds)
-> TaskRecord.setDisplayedBounds(steadyBounds)
-> Task.setOverrideDisplayedBounds()
-> WindowContainer.updateSurfacePosition()
-> SurfaceControl$Transaction.setPosition()
-> SurfaceControl$Transaction.setMatrix()
1.5#-> ActivityStack.moveToFront(“reuseOrNewTask”)
2.2 第二步分析
第二步: 在ActivityStack顶部添加新Activity, 创建对应activity的AppWindowToken容器。
ActivityStack.startActivityLocked
-> ActivityRecord.createAppWindowToken
2.1#-> TaskRecord.updateOverrideConfigurationFromLaunchBounds()
-> TaskRecord.updateOverrideConfiguration(bounds)
-> Task.setOverrideDisplayedBounds()
-> WindowContainer.updateSurfacePosition()
-> SurfaceControl$Transaction.setPosition()
-> SurfaceControl$Transaction.setMatrix()
2.2#-> ActivityRecord.createAppWindow() # return mAppWindowToken
-> new AppWindowToken() ->