Android 窗口结构(二) 添加Home Task

  Android层次结构构造完成之后,需要添加第一个任务HomeTask。HomeTask是Launcher应用所在的任务,现在接着上一篇文章Android 窗口结构(一),继续分析HomeTask的添加。
  在RootWindowContainer类中,DisplayContent初始化完成之后,会调用默认TaskDisplayArea的getOrCreateRootHomeTask(ON_TOP)方法,来生成HomeTask,我们从这里开始看:

    @Nullable
    Task getOrCreateRootHomeTask(boolean onTop) {
        Task homeTask = getRootHomeTask();
        // Take into account if this TaskDisplayArea can have a home task before trying to
        // create the root task
        if (homeTask == null && canHostHomeTask()) {
            homeTask = createRootTask(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, onTop);
        }
        return homeTask;
    }
    Task getRootHomeTask() {
        return mRootHomeTask;
    }    
    Task createRootTask(int windowingMode, int activityType, boolean onTop) {
        return createRootTask(windowingMode, activityType, onTop, null /* activityOptions */);
    }
    Task createRootTask(int windowingMode, int activityType, boolean onTop, ActivityOptions opts) {
        return new Task.Builder(mAtmService)
                .setWindowingMode(windowingMode)
                .setActivityType(activityType)
                .setParent(this)
                .setOnTop(onTop)
                .setActivityOptions(opts)
                .build();
    }    

  如果TaskDisplayArea类的mRootHomeTask为null,则说明还没有为它生成过,所以需要调用createRootTask()行数来生成。并且这里windowingMode=WINDOWING_MODE_UNDEFINED,activityType=ACTIVITY_TYPE_HOME。
  生成Task也是使用建造者模式,设置了windowingMode为WINDOWING_MODE_UNDEFINED、activityType为ACTIVITY_TYPE_HOME、父容器为TaskDisplayArea类对象,是否在顶上为true,还有ActivityOptions,这里是null。最后调用build()方法,生成Task对象。
  分段看看Task的建造类Build的build(),代码分段一:

        Task build() {
            if (mParent != null && mParent instanceof TaskDisplayArea) {
                validateRootTask((TaskDisplayArea) mParent);
            }

  build()在它的父容器是TaskDisplayArea对象时,调用validateRootTask((TaskDisplayArea) mParent)进行一些根任务验证。

根任务验证

  根任务的验证在方法validateRootTask(TaskDisplayArea tda)中,看一下:

        private void validateRootTask(TaskDisplayArea tda) {
            if (mActivityType == ACTIVITY_TYPE_UNDEFINED && !mCreatedByOrganizer) {
                // Can't have an undefined root task type yet...so re-map to standard. Anyone
                // that wants anything else should be passing it in anyways...except for the task
                // organizer.
                mActivityType = ACTIVITY_TYPE_STANDARD;
            }

            if (mActivityType != ACTIVITY_TYPE_STANDARD
                    && mActivityType != ACTIVITY_TYPE_UNDEFINED) {
                // For now there can be only one root task of a particular non-standard activity
                // type on a display. So, get that ignoring whatever windowing mode it is
                // currently in.
                Task rootTask = tda.getRootTask(WINDOWING_MODE_UNDEFINED, mActivityType);
                if (rootTask != null) {
                    throw new IllegalArgumentException("Root task=" + rootTask + " of activityType="
                            + mActivityType + " already on display=" + tda
                            + ". Can't have multiple.");
                }
            }

            if (!TaskDisplayArea.isWindowingModeSupported(mWindowingMode,
                    mAtmService.mSupportsMultiWindow,
                    mAtmService.mSupportsSplitScreenMultiWindow,
                    mAtmService.mSupportsFreeformWindowManagement,
                    mAtmService.mSupportsPictureInPicture, mActivityType)) {
                throw new IllegalArgumentException("Can't create root task for unsupported "
                        + "windowingMode=" + mWindowingMode);
            }

            if (mWindowingMode == WINDOWING_MODE_PINNED
                    && mActivityType != ACTIVITY_TYPE_STANDARD) {
                throw new IllegalArgumentException(
                        "Root task with pinned windowing mode cannot with "
                                + "non-standard activity type.");
            }

            if (mWindowingMode == WINDOWING_MODE_PINNED && tda.getRootPinnedTask() != null) {
                // Only 1 root task can be PINNED at a time, so dismiss the existing one
                tda.getRootPinnedTask().dismissPip();
            }

            if (mIntent != null) {
                mLaunchFlags |= mIntent.getFlags();
            }

            // Task created by organizer are added as root.
            final Task launchRootTask = mCreatedByOrganizer
                    ? null : tda.getLaunchRootTask(mWindowingMode, mActivityType, mActivityOptions,
                    mSourceTask, mLaunchFlags);
            if (launchRootTask != null) {
                // Since this task will be put into a root task, its windowingMode will be
                // inherited.
                mWindowingMode = WINDOWING_MODE_UNDEFINED;
                mParent = launchRootTask;
            }

            mTaskId = tda.getNextRootTaskId();
        }

  mCreatedByOrganizer变量代表任务是由任务组织者创建,如果不是,并且没有指定任务的mActivityType,则将它设置为ACTIVITY_TYPE_STANDARD。
  任务的mActivityType类型有ACTIVITY_TYPE_UNDEFINED、ACTIVITY_TYPE_STANDARD、ACTIVITY_TYPE_HOME、ACTIVITY_TYPE_RECENTS、ACTIVITY_TYPE_ASSISTANT、ACTIVITY_TYPE_DREAM。其中ACTIVITY_TYPE_UNDEFINED是未指明ActivityType时的值。ACTIVITY_TYPE_STANDARD是标准类型,剩下的几个都叫特殊类型。
  在除ACTIVITY_TYPE_STANDARD、ACTIVITY_TYPE_UNDEFINED这俩类型之外,如果在TaskDisplayArea对象里面已经存在特殊activity类型的根Task对象,是不允许新建的,会报IllegalArgumentException异常。
  接着会通过TaskDisplayArea.isWindowingModeSupported()方法来判断WindowingMode及activity类型是否支持,如果不支持,会报IllegalArgumentException异常。
  在WindowingMode是WINDOWING_MODE_PINNED时,activity类型必须为标准类型,不然,也会报错。
  如果WindowingMode是WINDOWING_MODE_PINNED时,并且现在TaskDisplayArea对象里面已经存在一个根任务PINNED,因为同一时间只允许一个存在,所以丢弃它。
  如果mIntent不为null,则将建造者的mLaunchFlags设置为mIntent.getFlags()。
  TaskDisplayArea对象的getLaunchRootTask()主要是处理参数mActivityOptions里指定的Task和TaskDisplayArea对象里面缓存的一些任务的处理,在生成HomeTask时,该方法返回null。如果不返回null时,会将建造者对象的mParent设置为它。
  最后会生成任务Id。它和用户id相关,比如用户id为0,则任务id范围为0-99999。每次新建的任务Id会加1。

设置一些属性

  接着就是设置一些属性信息。build()代码分段二:

            if (mActivityInfo == null) {
                mActivityInfo = new ActivityInfo();
                mActivityInfo.applicationInfo = new ApplicationInfo();
            }

            mUserId = UserHandle.getUserId(mActivityInfo.applicationInfo.uid);
            mTaskAffiliation = mTaskId;
            mLastTimeMoved = System.currentTimeMillis();
            mNeverRelinquishIdentity = true;
            mCallingUid = mActivityInfo.applicationInfo.uid;
            mCallingPackage = mActivityInfo.packageName;
            mResizeMode = mActivityInfo.resizeMode;
            mSupportsPictureInPicture = mActivityInfo.supportsPictureInPicture();
            if (mActivityOptions != null) {
                mRemoveWithTaskOrganizer = mActivityOptions.getRemoveWithTaskOranizer();
            }

  如果建造者没指定mActivityInfo ,直接新建mActivityInfo 对象和ApplicationInfo对象。
  得到mUserId,这里是通过uid得到的用户Id。

    /**
     * Returns the user id for a given uid.
     * @hide
     */
    @UnsupportedAppUsage
    @TestApi
    public static @UserIdInt int getUserId(int uid) {
        if (MU_ENABLED) {
            return uid / PER_USER_RANGE;
        } else {
            return UserHandle.USER_SYSTEM;
        }
    }

  在支持多用户时,用uid直接除以PER_USER_RANGE(100000)即得到user id。在这里得到0。
  接着将建造者对象的mTaskAffiliation设置为刚才新生成的任务Id。
  mNeverRelinquishIdentity 设置为true,它代表是否不丢弃原来的标识信息。
  mCallingUid设置为刚才生成的ApplicationInfo对象的默认uid,为0。
  mCallingPackage设置为刚才生成的ActivityInfo对象的默认packageName,为null。
  mResizeMode 设置为刚才生成的ActivityInfo对象的默认resizeMode,为RESIZE_MODE_RESIZEABLE,是可以重新设置大小。
  mSupportsPictureInPicture设置为刚才生成的ActivityInfo对象的默认为false。

创建Task对象

  再下来就是调用buildInner()对象进行创建Task对象。build()代码分段三:

            final Task task = buildInner();
            task.mHasBeenVisible = mHasBeenVisible;

  再看下buildInner()的代码:

        /** Don't use {@link Builder#buildInner()} directly. This is only used by XML parser. */
        @VisibleForTesting
        Task buildInner() {
            return new Task(mAtmService, mTaskId, mIntent, mAffinityIntent, mAffinity,
                    mRootAffinity, mRealActivity, mOrigActivity, mRootWasReset, mAutoRemoveRecents,
                    mAskedCompatMode, mUserId, mEffectiveUid, mLastDescription, mLastTimeMoved,
                    mNeverRelinquishIdentity, mLastTaskDescription, mLastSnapshotData,
                    mTaskAffiliation, mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid,
                    mCallingPackage, mCallingFeatureId, mResizeMode, mSupportsPictureInPicture,
                    mRealActivitySuspended, mUserSetupComplete, mMinWidth, mMinHeight,
                    mActivityInfo, mVoiceSession, mVoiceInteractor, mCreatedByOrganizer,
                    mLaunchCookie, mDeferTaskAppear, mRemoveWithTaskOrganizer);
        }

  直接调用Task的构造函数。

将Task对象添加进父容器之前,设置ActivityType

  build()代码分段四:

            // Set activity type before adding the root task to TaskDisplayArea, so home task can
            // be cached, see TaskDisplayArea#addRootTaskReferenceIfNeeded().
            if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
                task.setActivityType(mActivityType);
            }

  mActivityType目前是ACTIVITY_TYPE_HOME,所以调用Task对象的setActivityType(mActivityType)方法。

    /** Sets the activity type to associate with the configuration container. */
    public void setActivityType(/*@WindowConfiguration.ActivityType*/ int activityType) {
        int currentActivityType = getActivityType();
        if (currentActivityType == activityType) {
            return;
        }
        if (currentActivityType != ACTIVITY_TYPE_UNDEFINED) {
            throw new IllegalStateException("Can't change activity type once set: " + this
                    + " activityType=" + activityTypeToString(activityType));
        }
        mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
        mRequestsTmpConfig.windowConfiguration.setActivityType(activityType);
        onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
    }

  设置任务的ActivityType,这块和窗口容器类的配置有关,还是有些复杂的。看下它的逻辑
  如果目前的ActivityType和设置的ActivityType相同,就不用重新设置了。如果之前已经设置过ActivityType,现在也是不允许再次设置成其他类型。接着任务的当前的配置放到mRequestsTmpConfig中,这时将mRequestsTmpConfig对应的ActivityType修改成设置的值,最后调用onRequestedOverrideConfigurationChanged(mRequestsTmpConfig)方法。
  看下Task类的ActivityType的获取方法:

    @Override
    public int getActivityType() {
        final int applicationType = super.getActivityType();
        if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
            return applicationType;
        }
        return getTopChild().getActivityType();
    }

  首先调用父类的getActivityType()方法,如果类型已经指定,或者它没有孩子,这个时候,就将类型返回。如果父类返回的类型为ACTIVITY_TYPE_UNDEFINED (未指定),并且存在孩子,这个时候就取最上层的孩子的ActivityType。
  父类是WindowContainer,它没实现该方法,再往上看ConfigurationContainer,看下它的getActivityType():

    /** Returns the activity type associated with the the configuration container. */
    /*@WindowConfiguration.ActivityType*/
    public int getActivityType() {
        return mFullConfiguration.windowConfiguration.getActivityType();
    }

  mFullConfiguration是Configuration类对象。ConfigurationContainer类除了有这个,还有几个Configuration类对象,分别是mRequestedOverrideConfiguration、mResolvedOverrideConfiguration、mFullConfiguration、mMergedOverrideConfiguration变量。
  mRequestedOverrideConfiguration是请求更改的配置,mResolvedOverrideConfiguration是更改之后的配置。最后它俩是一致的,只是在更改过程中的它俩变化的时间点是不一样的,等会儿下面分析的时候就能看出来。
  mFullConfiguration是它的父容器和它自身的配置的结合。每次变化的时候,它会作为参数向孩子容器向下传递。最后将它的变化配置内容,传递到孩子的mFullConfiguration里。
  继续看setActivityType(/@WindowConfiguration.ActivityType/ int activityType)函数里的onRequestedOverrideConfigurationChanged(mRequestsTmpConfig)方法,这里为了先说清楚mRequestedOverrideConfiguration、mResolvedOverrideConfiguration、mFullConfiguration、mMergedOverrideConfiguration这几个成员变量的使用方式,先看Task类继承的ConfigurationContainer类里的方法。

ConfigurationContainer类里的onRequestedOverrideConfigurationChanged()

    /**
     * Update override configuration and recalculate full config.
     * @see #mRequestedOverrideConfiguration
     * @see #mFullConfiguration
     */
    public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
        // Pre-compute this here, so we don't need to go through the entire Configuration when
        // writing to proto (which has significant cost if we write a lot of empty configurations).
        mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
        mRequestedOverrideConfiguration.setTo(overrideConfiguration);
        final Rect newBounds = mRequestedOverrideConfiguration.windowConfiguration.getBounds();
        if (mHasOverrideConfiguration && providesMaxBounds()
                && diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) {
            mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds);
        }
        // Update full configuration of this container and all its children.
        final ConfigurationContainer parent = getParent();
        onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
    }

  参数overrideConfiguration是mRequestedOverrideConfiguration修改了ActivityType之后的值,如果overrideConfiguration不等于Configuration.EMPTY,则将mHasOverrideConfiguration 为true,代表存在修改的内容。接着将mRequestedOverrideConfiguration里的值都设置为参数overrideConfiguration里的值。注意这个时候mRequestedOverrideConfiguration的值反生了变化。
  再接着就是检查是否需要将mRequestedOverrideConfiguration的maxBounds设置为bounds。条件为mHasOverrideConfiguration 为true,providesMaxBounds()为true,diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE。
  providesMaxBounds()看下实现:

    protected boolean providesMaxBounds() {
        return false;
    }

  该方法代表是否为它的孩子提供了最大的边框,true即提供了,false为没提供。默认是不提供的,子类可以实现该方法。
  diffRequestedOverrideMaxBounds(newBounds)是将参数newBounds与mRequestedOverrideConfiguration配置的maxBounds进行比较,看是否发生了变化。
  onRequestedOverrideConfigurationChanged(mRequestsTmpConfig)方法最后会调用onConfigurationChanged(Configuration newParentConfig)执行配置发生变化之后的操作。注意一下它的参数,如果父容器parent存在的情况下,会将它设置为父容器的mFullConfiguration。如果不存在,则设置为Configuration.EMPTY(空配置)。
  父容器是在什么时候存在呢,是在父容器addChild()的时候。一会就会讲到该方法。所以目前是父容器还不存在,该参数为空配置。
  接着看ConfigurationContainer类的onConfigurationChanged(Configuration newParentConfig):

    /**
     * Notify that parent config changed and we need to update full configuration.
     * @see #mFullConfiguration
     */
    public void onConfigurationChanged(Configuration newParentConfig) {
        mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration);
        resolveOverrideConfiguration(newParentConfig);
        mFullConfiguration.setTo(newParentConfig);
        mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
        onMergedOverrideConfigurationChanged();
        if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
            // This depends on the assumption that change-listeners don't do
            // their own override resolution. This way, dependent hierarchies
            // can stay properly synced-up with a primary hierarchy's constraints.
            // Since the hierarchies will be merged, this whole thing will go away
            // before the assumption will be broken.
            // Inform listeners of the change.
            for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
                mChangeListeners.get(i).onRequestedOverrideConfigurationChanged(
                        mResolvedOverrideConfiguration);
            }
        }
        for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
            mChangeListeners.get(i).onMergedOverrideConfigurationChanged(
                    mMergedOverrideConfiguration);
        }
        for (int i = getChildCount() - 1; i >= 0; --i) {
            dispatchConfigurationToChild(getChildAt(i), mFullConfiguration);
        }
    }

  开始先把resolveOverrideConfiguration的值存在mResolvedTmpConfig里,resolveOverrideConfiguration这时的值还是没有更改的值,接着调用resolveOverrideConfiguration(newParentConfig),它在ConfigurationContainer类的实现如下:

    /**
     * Resolves the current requested override configuration into
     * {@link #mResolvedOverrideConfiguration}
     *
     * @param newParentConfig The new parent configuration to resolve overrides against.
     */
    void resolveOverrideConfiguration(Configuration newParentConfig) {
        mResolvedOverrideConfiguration.setTo(mRequestedOverrideConfiguration);
    }

  只是将mResolvedOverrideConfiguration的值设置为mRequestedOverrideConfiguration的值,而mRequestedOverrideConfiguration里面是发生变更的值。现在mResolvedOverrideConfiguration和mRequestedOverrideConfiguration数据保持一致了。这个方法里的参数怎么没用到呢,这时为了让子类进行重写该方法使用的。
  onConfigurationChanged()接着设置mFullConfiguration的值,先设置成参数newParentConfig的值,我们知道如果父容器存在,它为父容器的配置值,这种情况下mFullConfiguration先设置成父容器的配置值。如果父容器不存在,则为空配置。接着调用updateFrom()从mResolvedOverrideConfiguration里面更新数据。看见了吧,这样mFullConfiguration就将父容器和自己变更之后的数据结合在一起了。如果父容器里的值和mResolvedOverrideConfiguration里对应属性的值(不算默认配置值)不一样,会采用mResolvedOverrideConfiguration里面的值。
  接着调用onMergedOverrideConfigurationChanged()处理mMergedOverrideConfiguration成员变量。看下代码:

    void onMergedOverrideConfigurationChanged() {
        final ConfigurationContainer parent = getParent();
        if (parent != null) {
            mMergedOverrideConfiguration.setTo(parent.getMergedOverrideConfiguration());
            mMergedOverrideConfiguration.updateFrom(mResolvedOverrideConfiguration);
        } else {
            mMergedOverrideConfiguration.setTo(mResolvedOverrideConfiguration);
        }
        for (int i = getChildCount() - 1; i >= 0; --i) {
            final ConfigurationContainer child = getChildAt(i);
            child.onMergedOverrideConfigurationChanged();
        }
    }

  如果父容器存在,则先将mMergedOverrideConfiguration设置为父容器的mMergedOverrideConfiguration。然后,再取当前对象的配置变量mResolvedOverrideConfiguration值。如果父容器不存在,则将mMergedOverrideConfiguration设置为当前对象的配置值。
  继续调用孩子的onMergedOverrideConfigurationChanged()方法,将该容器的mMergedOverrideConfiguration传递给它的孩子。
  onConfigurationChanged()接下来会判断!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration,因为mResolvedTmpConfig是变化之前的配置值,和现在的相比不同,意思即发生了属性值变化。会调用注册的onRequestedOverrideConfigurationChanged()接口函数进行回调通知。这些注册的回调都是在变量mChangeListeners集合中。
  onConfigurationChanged()再接着调用回调接口的onMergedOverrideConfigurationChanged(),因为mMergedOverrideConfiguration发生了变化。
  onConfigurationChanged()最后就是调用孩子的onConfigurationChanged(Configuration newParentConfig)方法,将当前对象的mFullConfiguration传递给孩子的mFullConfiguration。
  我们发现mFullConfiguration和mMergedOverrideConfiguration都是通过父容器向子容器传递的。它俩开始赋值是在什么时候呢?是在添加到父容器中时,调用的onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent)方法里面会调用onConfigurationChanged(newParent.mFullConfiguration)。
  我们通过Android 窗口结构(一),这篇文章知道窗口层次结构的根是RootWindowContainer对象,如果一直就这样照着树结构添加的时候,mFullConfiguration和mMergedOverrideConfiguration应该是相同的。但是看注释里明确指出,这俩是不同的。肯定是在子类中的处理,导致的不同,这个问题留待其他文章探究。

Task类里的onConfigurationChanged(Configuration newParentConfig)

  这是我们分析的ConfigurationContainer类里的onRequestedOverrideConfigurationChanged(),其实在Task里面对onConfigurationChanged(Configuration newParentConfig)方法进行了重写,其实它这个逻辑还是挺复杂的,涉及到各种模式的变化,这里就选择最简单的一个角度来解释下,因为Task对象还没有添加到父容器里,所以参数newParentConfig是空配置,我摘其中的相关代码:

@Override
    public void onConfigurationChanged(Configuration newParentConfig) {
    		……
    		onConfigurationChangedInner(newParentConfig);
    		……
    } 
    private void onConfigurationChangedInner(Configuration newParentConfig) {
    	……
		super.onConfigurationChanged(newParentConfig);
		……
	}       		

  没错这种情况,其实就是将配置的ActivityType设置成了ACTIVITY_TYPE_HOME。

将Home Task对象添加进父容器

  build()代码分段五:

            if (mParent != null) {
                if (mParent instanceof Task) {
                    final Task parentTask = (Task) mParent;
                    parentTask.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM,
                            (mActivityInfo.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
                } else {
                    mParent.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM);
                }
            }

  在这里mParent是TaskDisplayArea对象,所以这里看下TaskDisplayArea的addChild(WindowContainer child, int position):

    @Override
    void addChild(WindowContainer child, int position) {
        if (child.asTaskDisplayArea() != null) {
            if (DEBUG_ROOT_TASK) {
                Slog.d(TAG_WM, "Set TaskDisplayArea=" + child + " on taskDisplayArea=" + this);
            }
            super.addChild(child, position);
        } else if (child.asTask() != null) {
            addChildTask(child.asTask(), position);
        } else {
            throw new IllegalArgumentException(
                    "TaskDisplayArea can only add Task and TaskDisplayArea, but found "
                            + child);
        }
    }

  可见TaskDisplayArea对象的孩子可以是TaskDisplayArea对象,也可以是Task对象。在这里child是Home Task,所以它是Task对象。调用addChildTask():

    private void addChildTask(Task task, int position) {
        if (DEBUG_ROOT_TASK) Slog.d(TAG_WM, "Set task=" + task + " on taskDisplayArea=" + this);

        addRootTaskReferenceIfNeeded(task);
        position = findPositionForRootTask(position, task, true /* adding */);

        super.addChild(task, position);
        if (mPreferredTopFocusableRootTask != null
                && task.isFocusable()
                && mPreferredTopFocusableRootTask.compareTo(task) < 0) {
            // Clear preferred top because the adding focusable task has a higher z-order.
            mPreferredTopFocusableRootTask = null;
        }
        mAtmService.updateSleepIfNeededLocked();
        onRootTaskOrderChanged(task);
    }

  1、调用addRootTaskReferenceIfNeeded(task),设置对应的类型的成员变量
  2、得到任务的位置
  3、调用父类的addChild(task, position)方法,添加任务
  4、如果新添加的任务是focusable ,并且有更高的z-order,修改mPreferredTopFocusableRootTask = null 。
  5、根据睡眠状态,进行一些设置
  6、回调根任务次序改变的接口
  这里主要说一下前三个步,

1、addRootTaskReferenceIfNeeded(task)

    void addRootTaskReferenceIfNeeded(Task rootTask) {
        if (rootTask.isActivityTypeHome()) {
            if (mRootHomeTask != null) {
                if (!rootTask.isDescendantOf(mRootHomeTask)) {
                    throw new IllegalArgumentException("addRootTaskReferenceIfNeeded: root home"
                            + " task=" + mRootHomeTask + " already exist on display=" + this
                            + " rootTask=" + rootTask);
                }
            } else {
                mRootHomeTask = rootTask;
            }
        } else if (rootTask.isActivityTypeRecents()) {
            if (mRootRecentsTask != null) {
                if (!rootTask.isDescendantOf(mRootRecentsTask)) {
                    throw new IllegalArgumentException("addRootTaskReferenceIfNeeded: root recents"
                            + " task=" + mRootRecentsTask + " already exist on display=" + this
                            + " rootTask=" + rootTask);
                }
            } else {
                mRootRecentsTask = rootTask;
            }
        }

        if (!rootTask.isRootTask()) {
            return;
        }
        final int windowingMode = rootTask.getWindowingMode();
        if (windowingMode == WINDOWING_MODE_PINNED) {
            if (mRootPinnedTask != null) {
                throw new IllegalArgumentException(
                        "addRootTaskReferenceIfNeeded: root pinned task=" + mRootPinnedTask
                                + " already exist on display=" + this + " rootTask=" + rootTask);
            }
            mRootPinnedTask = rootTask;
        } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
            if (mRootSplitScreenPrimaryTask != null) {
                throw new IllegalArgumentException(
                        "addRootTaskReferenceIfNeeded: root split screen primary task="
                                + mRootSplitScreenPrimaryTask
                                + " already exist on display=" + this + " rootTask=" + rootTask);
            }
            mRootSplitScreenPrimaryTask = rootTask;
        }
    }

  可见,该方法是为了设置对应TaskDisplayArea对象的成员变量mRootHomeTask、mRootRecentsTask、mRootPinnedTask、mRootSplitScreenPrimaryTask。
  mRootHomeTask和mRootRecentsTask是根据任务的ActivityType来判断的。并且如果已经设置过mRootHomeTask或mRootRecentsTask的情况下,再添加同类型的Task时,它如果不是已经存在的mRootHomeTask或mRootRecentsTask的孩子后代的情况下,会报异常。
  mRootPinnedTask、mRootSplitScreenPrimaryTask是根据windowingMode来判断的,并且已经设置过之后,再添加也会报错。
  对于目前的Home Task,这一步之后,就将mRootHomeTask 设置为对应的任务值了。

2、得到任务的位置

    private int findPositionForRootTask(int requestedPosition, Task rootTask, boolean adding) {
        // The max possible position we can insert the root task at.
        int maxPosition = findMaxPositionForRootTask(rootTask);
        // The min possible position we can insert the root task at.
        int minPosition = findMinPositionForRootTask(rootTask);

        // Cap the requested position to something reasonable for the previous position check
        // below.
        if (requestedPosition == POSITION_TOP) {
            requestedPosition = mChildren.size();
        } else if (requestedPosition == POSITION_BOTTOM) {
            requestedPosition = 0;
        }

        int targetPosition = requestedPosition;
        targetPosition = Math.min(targetPosition, maxPosition);
        targetPosition = Math.max(targetPosition, minPosition);

        int prevPosition = mChildren.indexOf(rootTask);
        // The positions we calculated above (maxPosition, minPosition) do not take into
        // consideration the following edge cases.
        // 1) We need to adjust the position depending on the value "adding".
        // 2) When we are moving a root task to another position, we also need to adjust the
        //    position depending on whether the root task is moving to a higher or lower position.
        if ((targetPosition != requestedPosition) && (adding || targetPosition < prevPosition)) {
            targetPosition++;
        }

        return targetPosition;
    }

findMaxPositionForRootTask(Task rootTask)

  再看下findMaxPositionForRootTask(Task rootTask)最大位置的取得方法:

    private int findMaxPositionForRootTask(Task rootTask) {
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final WindowContainer curr = mChildren.get(i);
            // Since a root task could be repositioned while still being one of the children, we
            // check if 'curr' is the same root task and skip it if so
            final boolean sameRootTask = curr == rootTask;
            if (getPriority(curr) <= getPriority(rootTask) && !sameRootTask) {
                return i;
            }
        }
        return 0;
    }
    private int getPriority(WindowContainer child) {
        final TaskDisplayArea tda = child.asTaskDisplayArea();
        if (tda != null) {
            // Use the top child priority as the TaskDisplayArea priority.
            return tda.getPriority(tda.getTopChild());
        }
        final Task rootTask = child.asTask();
        if (mWmService.mAssistantOnTopOfDream && rootTask.isActivityTypeAssistant()) return 4;
        if (rootTask.isActivityTypeDream()) return 3;
        if (rootTask.inPinnedWindowingMode()) return 2;
        if (rootTask.isAlwaysOnTop()) return 1;
        return 0;
    }    

  findMaxPositionForRootTask()是从上向下寻找子类,如果找到一个比它优先级低或者等于的,并且子类和当前添加Task对象不同,即找到了位置。
  再看下任务的优先级方法getPriority(),如果mWmService.mAssistantOnTopOfDream为true,并且是isActivityTypeAssistant(),它的优先级最高为4;如果是isActivityTypeDream(),优先级为3;如果WindowMode是WINDOWING_MODE_PINNED,优先级为2;如果isAlwaysOnTop()优先级为1,其他的优先级为0。
  还需要看下Task类的isAlwaysOnTop():

    @Override
    public boolean isAlwaysOnTop() {
        return !isForceHidden() && super.isAlwaysOnTop();
    }

  !isForceHidden()是指没有设置mForceHiddenFlags标志;super.isAlwaysOnTop()是父类ConfigurationContainer类的isAlwaysOnTop(),它主要是看其成员变量mFullConfiguration的配置,在WindowConfiguration类的isAlwaysOnTop():

    public boolean isAlwaysOnTop() {
        if (mWindowingMode == WINDOWING_MODE_PINNED) return true;
        if (mActivityType == ACTIVITY_TYPE_DREAM) return true;
        if (mAlwaysOnTop != ALWAYS_ON_TOP_ON) return false;
        return mWindowingMode == WINDOWING_MODE_FREEFORM
                    || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW;
    }

  可见如果mWindowingMode 为WINDOWING_MODE_PINNED,mActivityType 为ACTIVITY_TYPE_DREAM都设置为true。模式为WINDOWING_MODE_PINNED,是Android的一种锁定模式,开启之后,用户是只能在当前APP里面操作的,所以是isAlwaysOnTop()。ACTIVITY_TYPE_DREAM是壁纸相关,它肯定也是在上层的。
  另外,其他情况,如果没设置mAlwaysOnTop 为ALWAYS_ON_TOP_ON,就直接返回false。如果设置了它,再看mWindowingMode 是否为WINDOWING_MODE_FREEFORM或WINDOWING_MODE_MULTI_WINDOW,如果是这两种,也为true。

findMinPositionForRootTask(Task rootTask)

    private int findMinPositionForRootTask(Task rootTask) {
        int minPosition = POSITION_BOTTOM;
        for (int i = 0; i < mChildren.size(); ++i) {
            if (getPriority(mChildren.get(i)) < getPriority(rootTask)) {
                minPosition = i;
            } else {
                break;
            }
        }

        if (rootTask.isAlwaysOnTop()) {
            // Since a root task could be repositioned while still being one of the children, we
            // check if this always-on-top root task already exists and if so, set the minPosition
            // to its previous position.
            // Use mChildren.indexOf instead of getTaskIndexOf because we need to place the rootTask
            // as a direct child.
            final int currentIndex = mChildren.indexOf(rootTask);
            if (currentIndex > minPosition) {
                minPosition = currentIndex;
            }
        }
        return minPosition;
    }

  最小位置为孩子中最后一个优先级小于该任务的位置。如果任务是总是在顶上,并且该任务已经是孩子了,最小位置取它目前在的位置。
  findPositionForRootTask()接着根据参数requestedPosition,来设置它的值。然后赋值给targetPosition,接着取它和前面算出来的最大位置maxPosition中的最小值,然后再取它和前面算出来的最小位置的minPosition最大值。
  如果targetPosition != requestedPosition,即targetPosition取了刚才算出来的minPosition或者maxPosition中的一个值。我们知道,刚才计算这两个值的时候,都是它的优先级是小于当前任务的,所以需要将该位置增加1。所以在参数adding为true时,就将对应位置增加1.
  里面还提到一个targetPosition < prevPosition情况,这应该是将孩子从高位置向低位置转移的情况。这种情况下,也增加1.
  这样就得到添加到孩子中的位置。

3、父类的addChild(task, position)方法

  该实现在WindowContainer类的addChild(E child, int index):

    @CallSuper
    void addChild(E child, int index) {
        if (!child.mReparenting && child.getParent() != null) {
            throw new IllegalArgumentException("addChild: container=" + child.getName()
                    + " is already a child of container=" + child.getParent().getName()
                    + " can't add to container=" + getName()
                    + "\n callers=" + Debug.getCallers(15, "\n"));
        }

        if ((index < 0 && index != POSITION_BOTTOM)
                || (index > mChildren.size() && index != POSITION_TOP)) {
            throw new IllegalArgumentException("addChild: invalid position=" + index
                    + ", children number=" + mChildren.size());
        }

        if (index == POSITION_TOP) {
            index = mChildren.size();
        } else if (index == POSITION_BOTTOM) {
            index = 0;
        }

        mChildren.add(index, child);

        // Set the parent after we've actually added a child in case a subclass depends on this.
        child.setParent(this);
    }

  如果index为POSITION_TOP或POSITION_BOTTOM时,将它的值设置为最上或者最下面。然后将参数任务child添加到它的孩子集合mChildren中。
  最后调用孩子的setParent(this)方法。WindowContainer类的setParent(this):

    final protected void setParent(WindowContainer<WindowContainer> parent) {
        final WindowContainer oldParent = mParent;
        mParent = parent;

        if (mParent != null) {
            mParent.onChildAdded(this);
        }
        if (!mReparenting) {
            onSyncReparent(oldParent, mParent);
            if (mParent != null && mParent.mDisplayContent != null
                    && mDisplayContent != mParent.mDisplayContent) {
                onDisplayChanged(mParent.mDisplayContent);
            }
            onParentChanged(mParent, oldParent);
        }
    }

  1、将原来的父容器放到oldParent 变量,然后将新的容器对象设置给mParent 。
  2、设置的父容器不为null,调用它的onChildAdded(this),告诉父容器,它增加孩子了。
  3、mReparenting是在执行reparenting操作,在它的值为false时,也就是不在执行reparenting操作情况下,会执行onSyncReparent(oldParent, mParent)。
  4、不在执行reparenting操作情况下,如果当前对象的屏幕显示对象和父容器的不同,则调用onDisplayChanged(),执行,显示屏幕更换。
  5、不在执行reparenting操作情况下,执行onParentChanged(mParent, oldParent)方法,父容器变化的操作。
  这里主要说一下第2、4、5步的内容,

onChildAdded(this)

  onChildAdded(this)的实现代码在WindowContainer类中:

    private void onChildAdded(WindowContainer child) {
        mTreeWeight += child.mTreeWeight;
        WindowContainer parent = getParent();
        while (parent != null) {
            parent.mTreeWeight += child.mTreeWeight;
            parent = parent.getParent();
        }
        onChildPositionChanged(child);
    }

  它主要是增加数的高度。最后就是调用onChildPositionChanged(child)方法,我们知道当前的父容器是TaskDisplayArea对象,看下TaskDisplayArea类的该方法实现:

    @Override
    void onChildPositionChanged(WindowContainer child) {
        super.onChildPositionChanged(child);
        mRootWindowContainer.invalidateTaskLayers();
    }

  主要是调用父类的onChildPositionChanged(child),父类里也就是检查一下类型,如果类型不一致,会报异常。

Task类的onDisplayChanged()

    @Override
    void onDisplayChanged(DisplayContent dc) {
        final boolean isRootTask = isRootTask();
        if (!isRootTask) {
            adjustBoundsForDisplayChangeIfNeeded(dc);
        }
        super.onDisplayChanged(dc);
        if (isLeafTask()) {
            final int displayId = (dc != null) ? dc.getDisplayId() : INVALID_DISPLAY;
            mWmService.mAtmService.getTaskChangeNotificationController().notifyTaskDisplayChanged(
                    mTaskId, displayId);
        }
        if (isRootTask()) {
            updateSurfaceBounds();
        }
    }

  主要是调用了父类的onDisplayChanged(dc)方法。如果是叶子任务,会执行任务显示屏幕发生改变通知。如果是根任务,会执行更新显示界面边框操作。
  它的父类是WindowContainer,看下它的实现:

    void onDisplayChanged(DisplayContent dc) {
        if (mDisplayContent != null && mDisplayContent.mChangingContainers.remove(this)) {
            // Cancel any change transition queued-up for this container on the old display.
            mSurfaceFreezer.unfreeze(getPendingTransaction());
        }
        mDisplayContent = dc;
        if (dc != null && dc != this) {
            dc.getPendingTransaction().merge(mPendingTransaction);
        }
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final WindowContainer child = mChildren.get(i);
            child.onDisplayChanged(dc);
        }
        for (int i = mListeners.size() - 1; i >= 0; --i) {
            mListeners.get(i).onDisplayChanged(dc);
        }
    }

  这里实现了mDisplayContent 的赋值。并且将该对象的mPendingTransaction合并到显示屏幕对象的mPendingTransaction里。
  如果该对象存在孩子,将会调用孩子的onDisplayChanged(dc)。如果注册回调接口,执行回调。

再设置WindowMode

  build()代码分段六:

            // Set windowing mode after attached to display area or it abort silently.
            if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {
                task.setWindowingMode(mWindowingMode, true /* creating */);
            }
            return task;
        }

  我们在添加Home Task时,传递进来的mWindowingMode 为WINDOWING_MODE_UNDEFINED。所以这一步骤跳过了。

  这样就将Home Task添加到窗口层次结构中了,它作为TaskDisplayArea对象的孩子存在。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值