Android 系统多窗口模式实现

本文详细探讨了Android系统中多窗口模式的实现,尤其是自由窗口的启动过程,包括窗口概念、自由窗口启动的步骤分析以及相关布局适配和修改。在自由窗口启动时,分析了从创建ActivityStack、TaskRecord到添加ActivityRecord的流程,并讨论了如何处理自由窗口在分屏模式下的显示问题,包括窗口置顶显示和UI修改。
摘要由CSDN通过智能技术生成

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() ->

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值