Android13深化了解 Android 小窗口办法和窗口类型

Android13深入了解 Android 小窗口模式和窗口类型

小窗办法,作为一种在移动设备上的多使命处理办法,为用户带来了便捷和高效的体会,尤其在一些特定场景下,其价值愈发凸显。以下是为什么需求小窗办法的一些重要原因:

  1. 内容仿制和跨运用操作: 小窗办法答运用户轻松从一个运用(A运用)仿制内容到另一个运用(B运用),而无需频频切换运用。这在迁移微信聊天记载或在不同运用之间同享信息时十分有用。用户能够在小窗口中查看和编辑信息,然后将其粘贴到方针运用,这大大进步了功率。

  2. 无缝切换和即用即走: 在某些状况下,用户或许需求暂时运用一个运用程序(B运用),而不想完全脱离当时运用程序(A运用)。小窗办法答运用户在不中断A运用的状况下快速拜访B运用,然后无缝回来A运用。这关于查看实时信息或快速履行使命十分有用,如叫滴滴后查看司机方位。

  3. 多使命处理和注意力分配: 当用户需求在一个运用程序(A运用)中坚持重视,而且还需求时不时地查看另一个运用程序(B运用)的更新时,小窗办法十分有用。用户能够将B运用以小窗口的办法起浮在A运用上方,无需频频切换,然后更好地分配注意力,减轻焦虑心情。

  4. 快速回复和记载: 在看网课、玩游戏或履行其他使命时,用户或许会收到音讯或需求记载笔记。小窗办法答运用户在小窗口中轻松回复音讯或记笔记,而不用退出当时运用程序。这进步了多使命处理的功率。

  5. 心思负担减轻: 频频切换运用程序或许会导致用户分散注意力,增加心思负担,乃至产生焦虑心情。小窗办法的引入能够减轻这种心思负担,运用户更轻松地处理多个使命。

总的来说,小窗办法为用户供应了更灵敏、高效和愉悦的运用程序办理和多使命处理办法。在日常生活和工作中,小窗办法能够极大地进步用户的生产力和用户体会,成为了现代移动设备不可或缺的功用之一。不仅如此,众多Android操作体系制造商也在不断迭代和优化小窗办法,以满足用户不断改变的需求。

本文首要是对原生的自在窗口办法进行一个代码分析,详细各家的小窗作用,能够参阅这篇文章:
小米、华为、OPPO……五大 Android 体系横向比照,谁的「小窗办法」最好用?

基于 SourceCodeTrace 项目推崇的原则,本文代码块引证均有来历,SourceCodeTrace Project 帮助您在博客、文章记载的进程中,引入对应项目以及版本,行号等信息,让后续的读者,经过引证来历,能够进行愈加深化的学习,在博客或文章中引入代码块时,尽量供应代码的来历信息。

小窗敞开办法

开发者办法下敞开小窗功用

开发者办法下打开如下两个开关,然后重启即可。

Android13深入了解 Android 小窗口模式和窗口类型

adb 手动敞开

adb shell settings put global enable_freeform_support  1
adb shell settings put global force_resizable_activities  1

源码装备

  • copy file
# add for freedom
PRODUCT_COPY_FILES += \
   frameworks/native/data/etc/android.software.freeform_window_management.xml:$(TARGET_COPY_OUT_SYSTEM)/etc/permissions/android.software.freeform_window_management.xml
  • overlay
    <!-- add for freeform -->
    <bool name="config_freeformWindowManagement">true</bool>

小窗的发动办法

首要的发动办法,一个是多使命里边,点击运用图标,选择小窗办法,另一个是经过三方运用发动,比方侧边栏,通知栏等候。

三方运用经过 ActivityOptions 发动

    public void startFreeFormActivity(View view) {
        Intent intent = new Intent(this, FreeFormActivity.class);
        ActivityOptions options = ActivityOptions.makeBasic();
        options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
        startActivity(intent, options.toBundle());
    }

多使命发动

经过长按运用图标,选择小窗办法,ActivityOptions 会设置 ActivityOptions#setLaunchWindowingModeWINDOWING_MODE_FREEFORM,然后经过 ActivityManager#startActivity 发动 Activity。

    int startActivityFromRecents(int callingPid, int callingUid, int taskId,
            SafeActivityOptions options) {
        final Task task;
        final int taskCallingUid;
        final String callingPackage;
        final String callingFeatureId;
        final Intent intent;
        final int userId;
        final ActivityOptions activityOptions = options != null
                ? options.getOptions(this)
                : null;
        boolean moveHomeTaskForward = true;
        synchronized (mService.mGlobalLock) {
            int activityType = ACTIVITY_TYPE_UNDEFINED;
            if (activityOptions != null) {
                activityType = activityOptions.getLaunchActivityType();
                final int windowingMode = activityOptions.getLaunchWindowingMode();

/dev/src/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java?#L2529-L2545

运用兼容

运用需求设置 android:resizeableActivity="true",运用安装进程中会解析AndroidManifest.xml,并设置 PackageParser.ActivityInfoprivateFlags,在发动运用的时分,会根据 privateFlags 的值来判别是否支撑小窗。

        if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) {
            if (sa.getBoolean(R.styleable.AndroidManifestApplication_resizeableActivity, true)) {
                ai.privateFlags |= PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
            } else {
                ai.privateFlags |= PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
            }
        } else if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N) {
            ai.privateFlags |= PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
        }

/dev/src/frameworks/base/core/java/android/content/pm/PackageParser.java?#L3623-L3631

根底的窗口信息

在Android 体系中,窗口是运用程序界面的根本单元,用于承载和显现运用的视图内容。每个 Activity 都有一个主窗口,但也能够有其他窗口,如对话框、悬浮窗等。

在运用上层的一些组件的体现上,改变不是很大,可是 Framework 中的关于窗口的办理,迭代改变一直都比较大,或许之前有的类,新的架构下就被精简了,所以首要掌握窗口的一些概念,这样比较简略在新的架构之下找到对应的完结。

这儿先回顾一下根底的窗口和界面相关的概念:

  1. Window(窗口): 窗口是运用程序界面的根本单元,用于承载和显现运用的视图内容。每个 Activity 都有一个主窗口,但也能够有其他窗口,如对话框、悬浮窗等。
  2. WindowManagerService: 是 Android 体系中的窗口办理服务,担任办理窗口的创立、显现、移动、调整巨细、层级关系等。它是 Android 窗口体系的中心组件。
  3. View(视图): View 是 Android 中用户界面的根本构建块,用于在窗口中制作和显现内容。它是窗口中可见元素的根底。
  4. ViewGroup(视图组): ViewGroup 是一种特别的 View,它能够包括其他视图(包括 View 和其他 ViewGroup)来构成杂乱的用户界面。
  5. Surface(外表): Surface 是用于制作图形内容的区域,窗口和视图内容都能够在 Surface 上制作。每个窗口一般对应一个 Surface
  6. SurfaceFlinger: 是 Android 体系中的一个组件,担任办理和合成窗口中的 Surface,以及在屏幕上制作这些 Surface
  7. LayoutParams(布局参数): LayoutParams 是窗口或视图的布局参数,用于指定视图在其父视图中的方位、巨细和外观等。
  8. Window Token(窗口令牌): Window Token 是一个用于标识窗口所归于的运用程序或使命的方针。它在窗口的显现和交互中起着重要作用。
  9. Window Decor(窗口装修): Window Decor 是窗口的装修元素,如标题栏、状况栏等,能够影响窗口的外观和交互。
  10. Dialog(对话框): 对话框是一种特别的窗口,用于在当时活动之上显现暂时的提示、选择或输入内容。
  11. Activity(活动): 在 Android 运用程序中,每个 Activity 都与一个 PhoneWindow 相关联。PhoneWindow 用于办理 Activity 的界面制作和交互。
  12. Window Callback(窗口回调)PhoneWindow 完结了 Window.Callback 接口,该接口用于处理窗口事情和交互。经过完结这个接口,您能够监听和呼应窗口的状况改变、输入事情等。
  13. DecorView(装修视图): PhoneWindow 中的内容一般由 DecorView 承载。DecorView 是一个特别的 ViewGroup,用于包括运用程序的用户界面内容和窗口装修元素,如标题栏、状况栏等。
  14. PhoneWindow(运用程序窗口)PhoneWindowandroid.view.Window 类的完结之一,用于表明一个运用程序窗口。它供应了窗口的根本功用,如制作、布局、装修、焦点办理等。

窗口类型

    /** Can be freely resized within its parent container. */ /** 能够在其父容器中自在调整巨细。 */
    // TODO: Remove once freeform is migrated to wm-shell.
    public static final int WINDOWING_MODE_FREEFORM = 5;
    /** Generic multi-window with no presentation attribution from the window manager. */
    public static final int WINDOWING_MODE_MULTI_WINDOW = 6;
    /** @hide */
    @IntDef(prefix = { "WINDOWING_MODE_" }, value = {
            WINDOWING_MODE_UNDEFINED,
            WINDOWING_MODE_FULLSCREEN,
            WINDOWING_MODE_MULTI_WINDOW,
            WINDOWING_MODE_PINNED,
            WINDOWING_MODE_FREEFORM,
    })
    public @interface WindowingMode {}

/dev/src/frameworks/base/core/java/android/app/WindowConfiguration.java?#L107-L121

  1. WINDOWING_MODE_FULLSCREEN(全屏窗口办法):

    • 全屏窗口办法是最常见的窗口办法之一。在这种办法下,运用程序的窗口占据整个屏幕,而且一般用于运转单个运用程序,以全屏办法呈现内容。这是最常见的办法,特别是用于媒体播映和游戏运用。
  2. WINDOWING_MODE_MULTI_WINDOW(多窗口办法):

    • 多窗口办法答应多个运用程序在屏幕上并排显现,用户能够一起查看和操作多个运用程序。这种办法一般在平板电脑和大屏幕设备上运用,以进步多使命处理才干。在多窗口办法下,运用程序能够以切割屏幕或起浮窗口的办法并存,运用户能够轻松切换和互操作不同的运用程序。
  3. WINDOWING_MODE_PINNED(固定窗口办法):

    • 固定窗口办法是一种特别的办法,答运用户将一个运用程序窗口锁定在屏幕上,使其悬浮在其他运用程序之上。这关于创立“固定”运用程序,如屏幕时间约束或导航运用程序,以及在特定使命期间坚持一个运用程序窗口可见十分有用。
  4. WINDOWING_MODE_FREEFORM(自在窗口办法):

    • 自在窗口办法是一种灵敏的窗口办法,它答应运用程序窗口自在调整巨细和定位,就像在桌面操作体系中相同。这种办法运用户能够在屏幕上创立自定义布局,以满足其特定需求。自在窗口办法一般在大屏幕设备上运用,如平板电脑,以供应更多的窗口办理自在度。

这些窗口办法供应了不同的用户体会和多使命处理办法,使Android设备习惯了各种不同的运用情境和设备类型。

小窗

Android13深入了解 Android 小窗口模式和窗口类型

原生的小窗归于 WINDOWING_MODE_FREEFORM,关于目前大部分国内厂商而言,在WindowConfiguration 的窗口类型的根底上,自定义一个 WINDOWING_MODE_XXX,详细逻辑也是参阅小窗的窗口类型,用于完结自己的小窗的功用。 比较原生的小窗,国内厂商完结的小窗支撑的功用和动画作用愈加丰富,可是原理大致是相同的,中心的区别便是窗口的鸿沟,自定义的窗口一般是经过矩阵改变中的缩放完结将运用从大屏幕到小屏,保存了原始的长宽比,这样就不会出现变形的状况而且能够兼容一切的运用。 而原生的完结,首要是对运用鸿沟的操控,长宽比不固定,而且将鸿沟切到很小的时分,这样会让运用显现不完全,而且窗口类型需求运用提前定义为 resizeableActivity 可支撑缩放。

经过dumpsys window能够看到小窗的Task状况,bounds 表明窗口的鸿沟, 一个是mode字段描绘窗口类型.

  Task display areas in top down Z order:
    TaskDisplayArea DefaultTaskDisplayArea
      mPreferredTopFocusableRootTask=Task{1595f4c #23 type=standard A=10130:com.youku.phone U=0 visible=true visibleRequested=true mode=freeform translucent=false sz=1}
      mLastFocusedRootTask=Task{1595f4c #23 type=standard A=10130:com.youku.phone U=0 visible=true visibleRequested=true mode=freeform translucent=false sz=1}
      Application tokens in top down Z order:
      * Task{1595f4c #23 type=standard A=10130:com.youku.phone U=0 visible=true visibleRequested=true mode=freeform translucent=false sz=1}
        bounds=[50,50][553,991]
        * ActivityRecord{aec2841 u0 com.youku.phone/com.youku.v2.HomePageEntry} t23 d0}

小窗生成

小窗首要在 DecorCaptionView 中处理, Activity -> PhoneWindow -> DecorView -> DecorCaptionView,比较 DecorView 多了一个 Caption(标题栏)

树立的条件有两种:
一个在 PhoneWindow 树立的时分,创立 DecorViewDecorView 会判别是否要创立一个 DecorCaptionView
第二个便是在 DecorView 中动态改变中,有参数变量,经过onWindowSystemUiVisibilityChanged(View的可见性改变) 以及 onConfigurationChanged(各种触发装备改变的条件) 的回调,完结对DecorView 进行是否要新建一个小窗的视图。

在 DecorView 中首要处理了小窗的参数改变,以及标题栏的显现与隐藏。

    private void updateDecorCaptionStatus(Configuration config) {
        final boolean displayWindowDecor = config.windowConfiguration.hasWindowDecorCaption()
                && !isFillingScreen(config); // 假如定义窗口类型为小窗,且不是全屏办法, 则创立一个 DecorCaptionView 在 DecorView 内部。
        if (mDecorCaptionView == null && displayWindowDecor) {
            // Configuration now requires a caption.
            final LayoutInflater inflater = mWindow.getLayoutInflater();
            mDecorCaptionView = createDecorCaptionView(inflater);
            if (mDecorCaptionView != null) {
                if (mDecorCaptionView.getParent() == null) {
                    addView(mDecorCaptionView, 0,
                            new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
                }
                removeView(mContentRoot);
                mDecorCaptionView.addView(mContentRoot,
                        new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
            }
        } else if (mDecorCaptionView != null) { // 假如现已创立了 DecorCaptionView, 则更新装备信息,比方窗口巨细,窗口方位等。
            // We might have to change the kind of surface before we do anything else.
            mDecorCaptionView.onConfigurationChanged(displayWindowDecor);
            enableCaption(displayWindowDecor); // 是否显现小窗的标题栏
        }
    }

/dev/src/frameworks/base/core/java/com/android/internal/policy/DecorView.java?#L2179-L2200

小窗的标题栏

  • 小窗的标题栏
    private View mCaption;  // 标题栏
    private View mContent; // 小窗View之下,运用的根View
    private View mMaximize; // 最大化按钮
    private View mClose; // 封闭按钮

/dev/src/frameworks/base/core/java/com/android/internal/widget/DecorCaptionView.java?#L81-L84

假如在运用的根View外部点击的话,就阻拦事情往下传递,这样就不会触发运用的点击事情。

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        int action = event.getAction();
        if (mHasCaption && isShowingCaption()) { // 是否显现标题栏
            // Don't dispatch ACTION_DOWN to the captionr if the window is resizable and the event
            // was (starting) outside the window. Window resizing events should be handled by
            // WindowManager.  // 假如窗口可调整巨细,而且事情是(开端)在窗口外部,则不要将 ACTION_DOWN 事情进行传递。窗口调整巨细事情应由 WindowManager 处理。
            // TODO: Investigate how to handle the outside touch in window manager
            //       without generating these events.
            //       Currently we receive these because we need to enlarge the window's
            //       touch region so that the monitor channel receives the events
            //       in the outside touch area. // TODO: 了解如何在窗口办理器中处理外部接触,而不会生成这些事情。当时,咱们收到这些事情,由于咱们需求扩大窗口的接触区域,以便监视通道接收外部接触区域中的事情。
            if (action == MotionEvent.ACTION_DOWN) {
                final int x = (int) event.getX();
                final int y = (int) event.getY();
                if (isOutOfInnerBounds(x, y)) {
                    return true;
                }
            }
        }

/dev/src/frameworks/base/core/java/com/android/internal/policy/DecorView.java?#L525-L544

在小窗的View中,运用就不能经过View的事情来直接处理,DecorCaptionView 需求经过核算View所在的矩形区域,然后核算点击的区域是否处于该矩形区域范围内判别为点击,便是一个点和面的问题,原生小窗上的问题便是这个触控面太小,手指的点击区域或许不简略触发。 (在我开发的ChatDev游戏中,也有面和面磕碰的问题,玩家的方位和磕碰方位的核算)

Android13深入了解 Android 小窗口模式和窗口类型

事情分发的流程是这样的: ViewGroup::dispatchTouchEvent -> ViewGroup::onInterceptTouchEvent -> ViewGroup::onTouchEvent -> View::dispatchTouchEvent -> View::onTouchEvent

这儿 DecorCaptionView 经过onInterceptTouchEvent 阻拦了事情用来完结触控。

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // If the user starts touch on the maximize/close buttons, we immediately intercept, so
        // that these buttons are always clickable.  // 假如用户点击最大化或许封闭按钮,就阻拦事情,这样就不会触发运用的点击事情
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            final int x = (int) ev.getX();
            final int y = (int) ev.getY();
            // Only offset y for containment tests because the actual views are already translated.
            if (mMaximizeRect.contains(x, y - mRootScrollY)) { // 是否点击到最大化按钮的区域
                mClickTarget = mMaximize;
            }
            if (mCloseRect.contains(x, y - mRootScrollY)) { // 是否点击到封闭按钮的区域
                mClickTarget = mClose;
            }
        }
        return mClickTarget != null;
    }

/dev/src/frameworks/base/core/java/com/android/internal/widget/DecorCaptionView.java?#L142-L158

小窗的鸿沟

目前国内的厂商的小窗规划,都是经过在DecorView里边仿照DecorCaptionView自定义一个View,用来作为小窗内部运用的容器。

  • 鸿沟圆角 一般会对这个小窗容器进行一个UI上的美化,首要的一个便是鸿沟的圆角概括制作。

惯例的,圆角的完结首要是经过 ViewsetOutlineProvider, 然后在对自定义的 DecorCaptionView 上面制作一个圆角的布景,这样就能够完结圆角的作用。

在 Android 中,setOutlineProvider() 是一个 View 的办法,能够用来为 View 设置一个 OutlineProviderOutlineProvider 是一个抽象类,它供应了获取 View 概括的办法。经过设置 OutlineProvider,能够使 View 在制作时具有特定的概括形状。 创立一个类承继自 OutlineProvider,并完结其 getOutline() 办法。在该办法中,能够经过调用 setRoundRect()、setOval() 等办法来设置不同的概括形状。

经过 View.setOutlineProvider() 办法,将上一步创立的 OutlineProvider 实例设置给相应的 View。 在 ViewonDraw() 办法中,能够利用 View.getOutlineProvider() 获取当时设置的 OutlineProvider 实例,然后获取 View 的概括,并将其制作出来。

可是这样的完结办法,关于在小窗内部的 View 的制作仍是不可控,所以内部的View在可制作的区域继续制作矩形仍是会出现显现问题。

  • 导航栏堆叠的问题

DisplayPolicy 作为体系里边操控显现 dock栏、状况栏、导航栏的款式的首要类, 在每次制作布局之后,都会走到如下applyPostLayoutPolicyLw 函数,进行显现规矩的条件,当判别堆叠之后,在导航栏更新透明度规矩的时分,将其标记中不透明的纯深色布景和淡色远景清空。


        // Check if the freeform window overlaps with the navigation bar area. // 查看自在窗口是否与导航栏区域堆叠。
        if (!mIsFreeformWindowOverlappingWithNavBar && win.inFreeformWindowingMode()
                && win.mActivityRecord != null && isOverlappingWithNavBar(win)) {  // 假如窗口是自在窗口,而且窗口和导航栏堆叠
            mIsFreeformWindowOverlappingWithNavBar = true;
        }

/dev/src/frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java?#L1585-L1591

    /**
     * @return the current visibility flags with the nav-bar opacity related flags toggled based
     *         on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}.
     */
    private int configureNavBarOpacity(int appearance, boolean multiWindowTaskVisible,
            boolean freeformRootTaskVisible) {
        final boolean drawBackground = drawsBarBackground(mNavBarBackgroundWindow);
        if (mNavBarOpacityMode == NAV_BAR_FORCE_TRANSPARENT) {
            if (drawBackground) {
                appearance = clearNavBarOpaqueFlag(appearance);
            }
        } else if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) {
            if (multiWindowTaskVisible || freeformRootTaskVisible) {
                if (mIsFreeformWindowOverlappingWithNavBar) { // 假如自在窗口和导航栏堆叠
                    appearance = clearNavBarOpaqueFlag(appearance); // 铲除使导航栏变成不透明的纯深色布景和淡色远景。
                }

/dev/src/frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java?#L2530-L2546

  • 高度暗影问题
    private void updateElevation() {
        final int windowingMode =
                getResources().getConfiguration().windowConfiguration.getWindowingMode();
        final boolean renderShadowsInCompositor = mWindow.mRenderShadowsInCompositor;
        // If rendering shadows in the compositor, don't set an elevation on the view // 假如在合成器中烘托暗影,请不要在视图上设置高度
        if (renderShadowsInCompositor) {
            return;
        }
        float elevation = 0;
        final boolean wasAdjustedForStack = mElevationAdjustedForStack;
        // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
        // since the shadow is bound to the content size and not the target size. // 当咱们处于调整巨细办法(mBackdropFrameRenderer不为空)时不要运用暗影,由于暗影绑定到内容巨细而不是方针巨细。
        if ((windowingMode == WINDOWING_MODE_FREEFORM) && !isResizing()) {
            elevation = hasWindowFocus() ?
                    DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP; // 假如有焦点,则为DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP=20,否则为DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP=5
            // Add a maximum shadow height value to the top level view.
            // Note that pinned stack doesn't have focus
            // so maximum shadow height adjustment isn't needed. // 为顶级视图增加最大暗影高度值。注意,固定仓库没有焦点,因而不需求最大暗影高度调整。
            // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
            if (!mAllowUpdateElevation) {
                elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
            }
            // Convert the DP elevation into physical pixels.
            elevation = dipToPx(elevation);
            mElevationAdjustedForStack = true;
        }

/dev/src/frameworks/base/core/java/com/android/internal/policy/DecorView.java?#L2530-L2555

小窗的布景

小窗的布景中,包括运用对View设置的布景资源 还有一个窗口容器外部存在一个调暗层(DimLayer)的布景,用于完结调暗的作用。

Dimmer 类的作用是为完结窗口容器(WindowContainer增加“调暗层”(DimLayer)支撑,经过在不同的 Z 层级上创立具有不同透明度的黑色层,然后完结调暗的作用。该类首要用于在窗口容器中办理和运用调暗作用。

该类的首要办法和功用如下:

  • dimAbovedimBelow 办法
    这些办法用于在指定的窗口容器上方或下方增加调暗层。能够设置调暗层的透明度(alpha)和相关于指定容器的 Z 层级。

  • resetDimStates 该办法标记一切的调暗状况为等候下一次调用 updateDims 时完结。在调用子容器的 prepareSurfaces 之前调用此办法,以答应子容器继续恳求坚持调暗。

  • updateDims 办法 在调用子容器的 prepareSurfaces 后,经过此办法来更新调暗层的方位和尺寸。根据容器的更新,它能够设置调暗层的方位、巨细以及调整动画。

  • stopDim 办法 用于中止调暗作用,能够在不再需求调暗时调用。

  • 内部类 DimState 表明调暗状况的内部类,包括调暗层的 SurfaceControl、调暗状况等信息。

Dimmer 类用于办理和运用调暗作用,使窗口容器在不同 Z 层级上增加透明度改变的调暗层,以到达调暗的作用。 在 Android 体系中用于处理窗口切换、过渡动画以及调整显现作用时很有用, 假如要对小窗进行圆角的处理,这一层也是要处理的, 不然会出现黑色矩形边的问题。

运用的发动

startSpecificActivity 办法的作用是发动指定的活动(Activity)实例。 这个办法一般用于特定的状况,例如当需求在特定使命中发动某个活动时,或许在特定的使命仓库中发动活动。

详细来说,startSpecificActivity 办法的作用包括:

发动指定活动: 该办法答应开发者或体系经过供应特定的活动组件信息(如包名、类名)来发动特定的活动。

指定使命和仓库: 该办法能够答应开发者或体系指定在哪个使命和仓库中发动活动。这有助于将活动放置在预期的上下文中,如在多窗口办法中。

使命切换和前台切换: 在发动指定活动时,体系或许需求调整使命仓库和前台使命。这能够用于在用户点击通知或从其他运用程序发动时,保证正确的使命切换和前台切换。

    void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        final WindowProcessController wpc =
                mService.getProcessController(r.processName, r.info.applicationInfo.uid);
        boolean knownToBeDead = false;
        if (wpc != null && wpc.hasThread()) {
            try {
                realStartActivityLocked(r, wpc, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }
            // If a dead object exception was thrown -- fall through to
            // restart the application.
            knownToBeDead = true;
            // Remove the process record so it won't be considered as alive.
            mService.mProcessNames.remove(wpc.mName, wpc.mUid);
            mService.mProcessMap.remove(wpc.getPid());
        }
        r.notifyUnknownVisibilityLaunchedForKeyguardTransition();
        final boolean isTop = andResume && r.isTopRunningActivity();
        mService.startProcessAsync(r, knownToBeDead, isTop,
                isTop ? HostingRecord.HOSTING_TYPE_TOP_ACTIVITY
                        : HostingRecord.HOSTING_TYPE_ACTIVITY);
    }

/dev/src/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java?#L1039-L1068

Task 的康复

resumeTopActivityUncheckedLocked 是 Android 体系中 ActivityStack 类中的一个重要函数,它用于在仓库中康复坐落顶部的活动(Activity)。这个函数首要用于活动的切换和前台使命的办理。

详细来说,resumeTopActivityUncheckedLocked 的作用包括:

活动康复: 当一个活动需求从后台切换到前台时,这个函数担任履行康复操作。它会履行活动的生命周期办法,如 onResume,以保证活动处于可见和活跃状况。

前台使命切换: 当用户切换到另一个使命时,或许当一个使命从后台切换到前台时,这个函数担任在不同的使命仓库之间切换活动。它保证方针活动所在的使命仓库处于前台,以便用户能够与其交互。

使命切换的康复: 当时台使命从另一个使命切换回来时,体系需求保证前台使命的活动被正确康复。这个函数担任在这种状况下的活动切换和康复。

多窗口办法支撑: 假如设备支撑多窗口办法,这个函数也会在多窗口切换时被调用,以保证活动在不同窗口办法之间正确切换和康复。

鸿沟

内部鸿沟的核算

在Android Framework中,WindowState 是用于表明窗口状况的一个类。 窗口状况是指运用程序窗口在屏幕上的显现状况,包括方位、巨细、可见性等。

computeFrame办法是WindowState类中的一个重要办法,用于核算窗口的方位和巨细。详细来说,它担任核算窗口的制作区域,即窗口的内容在屏幕上实践显现的方位和巨细。这个核算触及到考虑窗口的方位、巨细、布局参数以及或许的鸿沟约束,保证窗口内容不会超出屏幕鸿沟或被其他窗口遮挡。

在窗口办理器中,computeFrame办法一般会在以下状况被调用:

当窗口第一次被创立时,需求核算其初始方位和巨细。 当窗口的布局参数或内容产生改变时,需求重新核算窗口的方位和巨细。 当屏幕旋转或巨细改变等体系事情产生时,需求调整一切窗口的方位和巨细。 总归,computeFrame办法在Android窗口办理体系中起到了十分重要的作用,保证运用程序窗口能够正确地在屏幕上显现,而且习惯不同的设备和体系事情, 为了核算小窗的方位,以及处理小窗内的View 的鸿沟异常状况, 一般咱们需求对 WindowFrames 是一个表明窗口边框巨细和方位的类进行适当的处理。

其中,mFrame、mVisibleFrame、mDecorFrame、mDisplayFrame 是 WindowFrames 中的一些成员变量,用于描绘不同的窗口区域。

  • mFrame 表明窗口在屏幕上的方位和巨细,是窗口办理和界面制作的根底依据。
  • mVisibleFrame 表明窗口可见区域的方位和巨细,即除去状况栏和导航栏等体系 UI 元素后,窗口实践能够显现的区域。
  • mDecorFrame 表明窗口装修区域的方位和巨细,即窗口除去实践内容区域外,包括的标题栏、边框、按钮等 UI 元素所占用的空间。
  • mDisplayFrame 表明整个屏幕的可见区域的方位和巨细,也便是说它包括了状况栏和导航栏等体系 UI 元素。

这些成员变量一起描绘了窗口在屏幕中的方位和巨细,并供应给其他模块运用,比方 WindowManager 和 View 体系。 在 Android Framework 中,WindowManagerService 会在每次窗口巨细产生改变时,调用 WindowFrames 的 setFrames() 办法,更新这些成员变量的值。

窗口鸿沟

Task 类中有一个比较重要的函数,prepareSurfaces, 一般是在窗口或界面元素制作之前被调用的一个办法,用于预备和更新与制作相关的外表(Surface)的状况。 和窗口相关的体系类,这儿梳理一下需求了解的要害承继关系:

- WindowContainer::prepareSurfaces
    - public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
    - class RootWindowContainer extends WindowContainer<DisplayContent>
    - class TaskFragment extends WindowContainer<WindowContainer> {
        - class Task extends TaskFragment {
    - class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
    - class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,
    - class WindowToken extends WindowContainer<WindowState> {
  • prepareSurfaceLocked 履行仓库
prepareSurfaceLocked:472, WindowStateAnimator (com.android.server.wm)
prepareSurfaces:5653, WindowState (com.android.server.wm)
prepareSurfaces:2669, WindowContainer (com.android.server.wm)
prepareSurfaces:7327, ActivityRecord (com.android.server.wm)
prepareSurfaces:2669, WindowContainer (com.android.server.wm)
prepareSurfaces:2639, TaskFragment (com.android.server.wm)
prepareSurfaces:3323, Task (com.android.server.wm)
prepareSurfaces:2669, WindowContainer (com.android.server.wm)
prepareSurfaces:2669, WindowContainer (com.android.server.wm)
prepareSurfaces:2669, WindowContainer (com.android.server.wm)
prepareSurfaces:2669, WindowContainer (com.android.server.wm)
prepareSurfaces:2669, WindowContainer (com.android.server.wm)
prepareSurfaces:662, DisplayArea$Dimmable (com.android.server.wm)
prepareSurfaces:2669, WindowContainer (com.android.server.wm)
prepareSurfaces:662, DisplayArea$Dimmable (com.android.server.wm)
prepareSurfaces:5242, DisplayContent (com.android.server.wm)
applySurfaceChangesTransaction:4717, DisplayContent (com.android.server.wm)
applySurfaceChangesTransaction:1025, RootWindowContainer (com.android.server.wm)
performSurfacePlacementNoTrace:828, RootWindowContainer (com.android.server.wm)
performSurfacePlacement:788, RootWindowContainer (com.android.server.wm)
performSurfacePlacementLoop:177, WindowSurfacePlacer (com.android.server.wm)
performSurfacePlacement:126, WindowSurfacePlacer (com.android.server.wm)
performSurfacePlacement:115, WindowSurfacePlacer (com.android.server.wm)
handleMessage:5680, WindowManagerService$H (com.android.server.wm)
dispatchMessage:106, Handler (android.os)
loopOnce:201, Looper (android.os)
loop:288, Looper (android.os)
run:67, HandlerThread (android.os)
run:44, ServiceThread (com.android.server)
    @Override
    void prepareSurfaces() {
        mDimmer.resetDimStates();
        super.prepareSurfaces();
        getDimBounds(mTmpDimBoundsRect);
        // Bounds need to be relative, as the dim layer is a child. // 鸿沟需求是相对的,由于暗层是子层。
        if (inFreeformWindowingMode()) {
            getBounds(mTmpRect);
            mTmpDimBoundsRect.offsetTo(mTmpDimBoundsRect.left - mTmpRect.left,
                    mTmpDimBoundsRect.top - mTmpRect.top); // 处理调暗层的偏移鸿沟
        } else {
            mTmpDimBoundsRect.offsetTo(0, 0);
        }

/dev/src/frameworks/base/services/core/java/com/android/server/wm/Task.java?#L3306-L3319

prepareSurfaces图形烘托显现进程中发挥着重要作用,保证制作的界面元素能够正确显现和交互。 正常有这些状况下会调用 prepareSurfaces 函数:

  • Surface 的创立和装备: 在界面元素即将制作之前,prepareSurfaces 函数或许会创立、装备或更新相关的 Surface。这或许包括在制作进程中运用的后备 Surface 或用于烘托特定视图的 Surface。

  • 更新界面状况: 该函数一般用于更新界面元素的状况,如方位、巨细、可见性等。这能够保证界面元素在制作之前具有正确的状况。

  • 外表层级设置: prepareSurfaces 一般会设置不同 Surface 之间的层级关系,以保证它们依照正确的次序制作。这关于完结叠加作用、混合作用和窗口层次等十分重要。

  • 调暗作用的办理: 在一些状况下,prepareSurfaces 或许会用于办理调暗作用,如前一个问题中提到的 Dimmer 类。在制作前调整外表的透明度和方位,以到达调暗的作用。

  • 动画和过渡预备: 假如有动画或过渡作用,prepareSurfaces 或许会在制作之前对外表进行适当的预备,以保证动画作用的正确履行。

  • 功能优化: prepareSurfaces 也能够用于功能优化,例如预备制作所需的材料、纹路或缓冲区,以防止在实践制作时出现延迟。

触控区域的优化

DisplayContent 类的一个办法,名为 processTaskForTouchExcludeRegion。 处理使命的接触扫除区域,以保证在处理接触事情时不会影响到特定区域,例如使命之间的间隙或使命的鸿沟。以下是对代码的逐行分析和大致功用的解说:

private void processTaskForTouchExcludeRegion(Task task, Task focusedTask, int delta) {
        final ActivityRecord topVisibleActivity = task.getTopVisibleActivity();
        if (topVisibleActivity == null || !topVisibleActivity.hasContentToDisplay()) { // 查看顶部可见活动是否存在,以及该活动是否有内容需求显现。假如没有,就越过后续处理。
            return;
        }
        // Exclusion region is the region that TapDetector doesn't care about.
        // Here we want to remove all non-focused tasks from the exclusion region.
        // We also remove the outside touch area for resizing for all freeform
        // tasks (including the focused).
        // We save the focused task region once we find it, and add it back at the end.
        // If the task is root home task and it is resizable and visible (top of its root task),
        // we want to exclude the root docked task from touch so we need the entire screen area
        // and not just a small portion which the root home task currently is resized to. // 假如使命是根主页使命,而且它是可调整巨细的而且可见的(在其根使命的顶部),咱们期望从接触中扫除根停靠使命,因而咱们需求整个屏幕区域而不仅仅是根主页使命当时调整巨细的一小部分。
        if (task.isActivityTypeHome() && task.isVisible() && task.isResizeable()) {
            task.getDisplayArea().getBounds(mTmpRect);
        } else {
            task.getDimBounds(mTmpRect); // 获取使命的调整鸿沟(维度鸿沟)
        }
        if (task == focusedTask) {
            // Add the focused task rect back into the exclude region once we are done
            // processing root tasks.
            // NOTE: this *looks* like a no-op, but this usage of mTmpRect2 is expected by
            //       updateTouchExcludeRegion.
            mTmpRect2.set(mTmpRect); // 将当时使命的鸿沟仿制到暂时矩形 mTmpRect2, 用于后续的更新操作。
        }
        final boolean isFreeformed = task.inFreeformWindowingMode();
        if (task != focusedTask || isFreeformed) {
            if (isFreeformed) {
                // If the task is freeformed, enlarge the area to account for outside
                // touch area for resize. // 假如使命是小窗办法,则扩大该区域以考虑调整巨细的外部接触区域。
                mTmpRect.inset(-delta, -delta);
                // Intersect with display content frame. If we have system decor (status bar/
                // navigation bar), we want to exclude that from the tap detection.
                // Otherwise, if the app is partially placed under some system button (eg.
                // Recents, Home), pressing that button would cause a full series of
                // unwanted transfer focus/resume/pause, before we could go home.
                mTmpRect.inset(getInsetsStateController().getRawInsetsState().calculateInsets(
                        mTmpRect, systemBars() | ime(), false /* ignoreVisibility */)); // 调整使命鸿沟,以扫除体系装修(如状况栏、导航栏)和输入法区域的影响。
            }
            mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE); // 将调整后的使命鸿沟与接触扫除区域进行扫除操作,以保证接触事情不会影响到此区域。
        }
    }

/dev/src/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java?#L3167-L3212

拖拽

借助 Android 拖放框架,您能够向运用中增加交互式拖放功用。经过拖放,用户能够在运用中的 View 之间仿制或拖动文本、图片、方针(能够经过 URI 表明的任何内容),也能够运用多窗口办法在运用之间拖动这些内容。

Android13深入了解 Android 小窗口模式和窗口类型

developer.android.google.cn/guide/topic…

DragState 类首要用于办理和盯梢拖拽操作的状况。拖拽操作一般触及用户在屏幕上拖动某个视图或方针,并在特定方位开释它。以下是 DragState 类的首要作用和功用:

  1. 办理拖拽动作的状况DragState 类担任盯梢拖拽操作的各个状况,例如开端拖拽、拖拽中、拖拽结束等。它能够存储和更新与拖拽状况相关的信息。

  2. 处理拖拽手势:这个类或许包括了处理用户拖拽手势的逻辑,例如监测用户手指的移动、核算拖拽物体的方位、响运用户的拖拽操作等。

  3. 和谐拖拽操作DragState 类或许与其他体系组件(如窗口办理器或视图体系)协同工作,以保证拖拽操作在屏幕上正确履行。它或许需求调整被拖拽物体的方位,更新UI,或许触发其他操作。

  4. 供应拖拽状况信息:这个类一般会供应有关拖拽状况的信息,例如拖拽物体的方位、拖拽的类型、拖拽的源方针等。这些信息能够供其他组件运用。

  5. 处理拖拽的开释:当用户开释拖拽物体时,DragState 类或许需求履行特定的操作,例如将拖拽物体放置在新的方位、触发操作、或许完结拖拽操作。

  6. 支撑拖拽的可视化作用:在一些状况下,DragState 类或许需求办理与拖拽相关的可视化作用,例如拖拽物体的影子或许拖拽物体的预览。

DragState
    notifyLocationLocked(float, float)
            WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y);
    notifyDropLocked(float, float)
            final WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y);
WindowManagerService
    updatePointerIcon(IWindow)
                        displayContent.getTouchableWinAtPointLocked(mouseX, mouseY);
    restorePointerIconLocked(DisplayContent, float, float)
                    displayContent.getTouchableWinAtPointLocked(latestX, latestY);

移动

在小窗办法下,能够经过长按小窗的标题栏,然后拖动到屏幕的恣意方位,这个进程中会调用 moveTaskToStack,详细完结如下:

焦点的改变

Task的办理

目前从 Android12 开端,Android Framework逐渐将Stack的概念切换为Task了,比方之前的 ActivityStackSupervisor 变成了 ActivityTaskSupervisor, ActivitStack 也变成了 Task, 在之前的版本上,也两个概念也同步存在一段时间。

ActivityTaskManagerService 是体系中担任办理运用程序的使命(Task)和活动(Activity)。 它在Android体系中起到了调度和办理运用程序的中心角色。 首要作用有:

  • 使命办理: 该服务担任办理多个运用程序使命的生命周期。使命是一组相关活动的调集,一般代表了一个用户在运用程序之间的导航路径。例如,用户在浏览器中打开一个网页,然后从浏览器跳转到电子邮件运用,这两个活动会被归为一个使命。使命办理器保证使命在切换活动或回来到最近运用的使命时得以康复。

  • 活动办理: 该服务担任办理单个活动的生命周期。活动是运用程序用户界面的根本单元,包括用户与运用程序交互的界面元素。ActivityTaskManagerService保证活动在需求时正确创立、暂停、康复、毁掉等。

  • 使命切换和前台运用办理: 当用户切换运用程序或使命时,ActivityTaskManagerService和谐活动之间的切换,以及保证前台运用程序的正确办理。前台运用是当时用户正在与之交互的运用程序,而后台运用则是在后台运转的运用程序。

  • 多窗口支撑: Android体系中的一些设备支撑多窗口办法,答运用户在同一屏幕上一起运转多个运用程序。ActivityTaskManagerService担任办理多窗口办法下的使命和活动排列。

  • 使命仓库办理: ActivityTaskManagerService办理运用程序使命的仓库,以便在不同使命之间进行切换。这保证用户能够流畅地在不同运用程序之间切换,而且体系能够适当地处理回来键等用户导航操作。

它用来保证运用程序使命和活动的正确办理、切换和交互,然后供应了良好的用户体会。

ActivityTaskSupervisorActivityTaskManagerService 一起办理的运用程序使命和活动,它是 ActivityManagerService 的一部分,用于和谐和办理使命的生命周期、使命切换、多窗口办法等方面的功用。

// TODO: This class has become a dumping ground. Let's
// - Move things relating to the hierarchy to RootWindowContainer
// - Move things relating to activity life cycles to maybe a new class called ActivityLifeCycler
// - Move interface things to ActivityTaskManagerService.
// - All other little things to other files.
public class ActivityTaskSupervisor implements RecentTasks.Callbacks {

/dev/src/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java?#L159-L164

在新的结构中,将一些功用从 ActivityTaskSupervisor 中移除,别离放到 ActivityTaskManagerServiceRootWindowContainerActivityLifeCycler 中,使得 ActivityTaskSupervisor 只保存一些中心功用,然后使得代码愈加明晰。

办法切换

小窗右上角点击切换全屏运用,会从小窗的办法变成运用全屏的办法,切换的逻辑从 DecorCaptionView中 调用toggleFreeformWindowingMode,在Android13之前经过 ActivityTaskManagerService 来完结,在 Android12之后新增了一个体系类ActivityClientController(在服务端完结,用于客户端活动与体系交互,防止了之前在运用层调用体系服务的时分,之前直接调用的客户端需求自行处理RemoteException以及其他剩余的逻辑,这儿增加了一层封装,进步了功率),在ActivityClientController中新增了一个办法toggleFreeformWindowingMode,用于切换小窗和全屏的办法,详细完结如下:

/dev/src/frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java?#L96-L101

    @Override
    public void toggleFreeformWindowingMode(IBinder token) { // 
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
                if (r == null) {
                    throw new IllegalArgumentException(
                            "toggleFreeformWindowingMode: No activity record matching token="
                                    + token);
                }
                final Task rootTask = r.getRootTask();
                if (rootTask == null) {
                    throw new IllegalStateException("toggleFreeformWindowingMode: the activity "
                            + "doesn't have a root task");
                }
                if (!rootTask.inFreeformWindowingMode()
                        && rootTask.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
                    throw new IllegalStateException("toggleFreeformWindowingMode: You can only "
                            + "toggle between fullscreen and freeform.");
                }
                if (rootTask.inFreeformWindowingMode()) {
                    rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
                    rootTask.setBounds(null);
                } else if (!r.supportsFreeform()) {

/dev/src/frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java?#L993-L1020

这儿有几个改变,一个是曾经经过setWindowingMode切换窗口处理的方针是ActivityStackActivityStack 承继自Task, 在Android13上, 直接把ActivityStack 这个类干掉了,处理的窗口类型方针为 Task, 在一个是新增了 setBounds(null). setWindowingMode 手动更新窗口类型,首要是为了根节点规划,假如是子节点来调用,需求找到父节点,然后设置它的窗口类型。

    @Override
    public void setWindowingMode(int windowingMode) {
        // Calling Task#setWindowingMode() for leaf task since this is the a specialization of
        // {@link #setWindowingMode(int)} for root task.
        if (!isRootTask()) {
            super.setWindowingMode(windowingMode);
            return;
        }
        setWindowingMode(windowingMode, false /* creating */);
    }

/dev/src/frameworks/base/services/core/java/com/android/server/wm/Task.java?#L4531-L4541

ActivityRecord办理

ActivityRecord 是一个数据结构,用于表明运用程序中的活动(Activity)实例。 每个正在运转或现已发动但没有毁掉的活动都会有一个对应的 ActivityRecord 方针来盯梢其状况和信息。 ActivityRecord一般由 ActivityTaskSupervisor 等组件运用,用于办理和操控活动的生命周期和交互。

  • 状况盯梢: ActivityRecord用于盯梢活动的当时状况,包括运转状况、暂停状况、中止状况等。它记载活动是否现已创立、发动、康复,以及是否现已被毁掉。

  • 使命和仓库信息: 每个ActivityRecord一般都归于某个使命(Task),并包括有关其所在使命的信息,例如使命的ID、仓库的ID等。这有助于在使命和仓库之间正确切换活动。

  • 窗口办理: ActivityRecord供应与窗口办理相关的信息,如窗口的方位、巨细、可见性等。这些信息用于将活动的UI内容正确地显现在屏幕上。

  • 目的和参数: 经过ActivityRecord,能够拜访发动活动时传递的目的(Intent)和附加参数。这关于康复活动状况、处理目的数据等操作很有帮助。

  • 生命周期办理: ActivityRecord记载了活动的生命周期事情,如创立、发动、康复、暂停、中止和毁掉等。这有助于ActivityStackSupervisor等组件正确办理活动的生命周期。

在 ActivityTaskSupervisor 中,记载了一些活动的状况信息,如下所示:

    /** List of activities that are ready to be stopped, but waiting for the next activity to
     * settle down before doing so. */ // 列表中的活动现已预备好中止,可是等候下一个活动安靖下来才干中止。
    final ArrayList<ActivityRecord> mStoppingActivities = new ArrayList<>();
    /** List of activities that are ready to be finished, but waiting for the previous activity to
     * settle down before doing so.  It contains ActivityRecord objects. */ // 列表中的活动现已预备好完结,可是等候上一个活动安稳后才干完结。
    final ArrayList<ActivityRecord> mFinishingActivities = new ArrayList<>();
    /**
     * Activities that specify No History must be removed once the user navigates away from them.
     * If the device goes to sleep with such an activity in the paused state then we save it
     * here and finish it later if another activity replaces it on wakeup. // 指定无历史记载的活动必须在用户导航脱离它们后删去。
     */
    final ArrayList<ActivityRecord> mNoHistoryActivities = new ArrayList<>();
    /** List of activities whose multi-window mode changed that we need to report to the
     * application */ // 列表中的活动的多窗口办法已更改,咱们需求向运用程序陈述
    private final ArrayList<ActivityRecord> mMultiWindowModeChangedActivities = new ArrayList<>();
    /** List of activities whose picture-in-picture mode changed that we need to report to the
     * application */ // 列表中的活动的画中画办法已更改,咱们需求向运用程序陈述
    private final ArrayList<ActivityRecord> mPipModeChangedActivities = new ArrayList<>();
    /**
     * Animations that for the current transition have requested not to
     * be considered for the transition animation.
     */  // 动画,关于当时转化,已恳求不考虑转化动画。
    final ArrayList<ActivityRecord> mNoAnimActivities = new ArrayList<>();
    /**
     * Cached value of the topmost resumed activity in the system. Updated when new activity is
     * resumed.
     */ // 体系中最顶层的康复活动的缓存值。当康复新活动时更新。
    private ActivityRecord mTopResumedActivity;

/dev/src/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java?#L280-L313

mStoppingActivities 为例: ActivityRecord 类的 finishIfPossible 办法一般在以下状况下被调用:

  • 使命切换: 当用户切换到另一个使命或回来到使命仓库的根活动时,体系或许会测验完结一些不再可见或不再需求的活动。这能够开释资源并优化体系功能。
  • 屏幕旋转或装备更改: 当屏幕方向产生改变或装备更改时,体系或许会毁掉偏重新创立某些活动。在这种状况下,体系能够测验完结旧的活动实例,以保证新的实例能够正确地创立和显现。
  • 后台活动整理: 当体系内存不足时,Android 体系会测验整理后台活动以开释内存资源。这时,体系能够调用 finishIfPossible 来完结一些不再活跃或不再需求的活动,以开释内存。
  • 运用程序封闭: 当运用程序被用户或体系封闭时,体系会测验完结该运用程序的一切活动,以保证资源被正确开释。

整理 ActivityRecord 时 会将实例暂存到 mStoppingActivities 然后后续的实例履行安稳后,在实践去整理,这样能够让下一个实例发动时的制作功能更高,进步体会。

矩阵改变

缩放

private static native void nativeSetMatrix(long transactionObj, long nativeObject,
float dsdx, float dtdx,
float dtdy, float dsdy);

transactionObj(类型:long):这是一个 SurfaceControl.Transaction方针的本地表明。它用于指示履行此操作的事务方针。这个参数一般用来保证操作是原子的,以便它们能够一起履行或一起失利。 nativeObject(类型:long):这是 SurfaceControl 方针的本地表明。它是要运用改换矩阵的方针图形外表的本地引证。

  • dsdx(类型:float)
    这是改换矩阵的元素之一,表明 X 轴上的水平缩放因子。假如设置为 1.0,则表明不进行水平缩放。假如大于 1.0,则表明进行扩大;假如小于 1.0,则表明进行缩小。
  • dtdx(类型:float)
    这是改换矩阵的元素之一,表明 X 轴上的水平切变因子。切变能够用于歪斜图形。
  • dtdy(类型:float)
    这是改换矩阵的元素之一,表明 Y 轴上的笔直切变因子。切变能够用于歪斜图形。
  • dsdy(类型:float)
    这是改换矩阵的元素之一,表明 Y 轴上的笔直缩放因子。假如设置为 1.0,则表明不进行笔直缩放。假如大于 1.0,则表明进行扩大;假如小于 1.0,则表明进行缩小。

本文仅仅从软件开发的视点,简略梳理一下Android FreeForm(自在窗口) 触及到的模块以及根本概念,本文也在继续的更新中,假如你需求得到最新的更新或许有一些代码高亮,请拜访: Android13深化了解 Android 小窗口办法和窗口类型