Android 系统多窗口模式实现

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-lF5hx91l-1646992663420)(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.update

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 11 的多窗口系统实现流程如下: 1. 在 AndroidManifest.xml 文件中设置应用支持多窗口模式,可以通过添加以下代码实现: ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" ...> <application ... android:resizeableActivity="true" android:supportsPictureInPicture="true" android:allowTaskReparenting="true"> ... </application> </manifest> ``` 其中,`android:resizeableActivity` 属性指定应用的 Activity 是否支持调整大小,`android:supportsPictureInPicture` 属性指定应用是否支持画中画模式,`android:allowTaskReparenting` 属性指定是否允许任务重归。 2. 在 Activity 中处理多窗口状态变化的事件,可以通过重写以下方法实现: ```java @Override public void onMultiWindowModeChanged(boolean isInMultiWindowMode) { super.onMultiWindowModeChanged(isInMultiWindowMode); if (isInMultiWindowMode) { // 进入多窗口模式 } else { // 离开多窗口模式 } } ``` 3. 在 Activity 中设置多窗口模式的显示方式和位置,可以通过以下代码实现: ```java if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { final WindowInsetsController insetsController = getWindow().getInsetsController(); if (insetsController != null) { insetsController.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); insetsController.show(WindowInsets.Type.systemBars()); insetsController.setWindowInsetsAnimationCallback(new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { @Override public void onProgress(@NonNull WindowInsetsAnimation animation, @NonNull List<WindowInsets> insets) { super.onProgress(animation, insets); // 处理多窗口模式的显示位置 } }); } } ``` 其中,`setSystemBarsBehavior` 方法指定系统栏的行为,`show` 方法显示窗口,`setWindowInsetsAnimationCallback` 方法设置窗口插入动画的回调函数。 4. 在 Activity 中处理多窗口模式下的画中画事件,可以通过以下代码实现: ```java @Override public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { super.onPictureInPictureModeChanged(isInPictureInPictureMode); if (isInPictureInPictureMode) { // 进入画中画模式 } else { // 离开画中画模式 } } ``` 以上就是 Android 11 的多窗口系统实现流程,希望对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值