android framework13-launcher3【02recents】

關(guān)注個人簡介,技術(shù)不迷路~

1.簡介

這里主要介紹下點(diǎn)擊導(dǎo)航欄的recents按鈕瓢对,跳轉(zhuǎn)的頁面。我以前以為這東西是個單獨(dú)的app胰苏,看了代碼才發(fā)現(xiàn)硕蛹,它是launcher的一部分。我們打開源碼/packages/apps/Launcher3 目錄下硕并,發(fā)現(xiàn)除了res法焰,src,清單文件外倔毙,還有2個目錄埃仪,go目錄是給低配置的機(jī)器用的簡化版的代碼,quickstep是我們要看的東西普监。

2.Android.bp

打開Launcher3目錄下的這個文件贵试,搜索下android_app,可以發(fā)現(xiàn)有4個,也就是說這個工程可以編譯4種apk

android_app {
name: "Launcher3",

android_app {
name: "Launcher3Go",

// Build rule for Quickstep app.
android_app {
name: "Launcher3QuickStep",

// Build rule for Launcher3 Go app with quickstep for Android Go devices.
android_app {
name: "Launcher3QuickStepGo",

那么image里用的哪種凯正?這就要看配置了【build/target/product/handheld_system_ext.mk】毙玻,官方源碼默認(rèn)的是這個,額,我這編譯的是phone用的廊散,可以看到下邊用的Launcher3QuickStep

# /system_ext packages
PRODUCT_PACKAGES += \
    Launcher3QuickStep \
    Provision \
    Settings \
    StorageManager \
    SystemUI \
    WallpaperCropper \

3.layout

底層布局還是上一篇里用到launcher.xml,里邊有個這樣的include桑滩,在默認(rèn)的res下是空的,在quickstep里重寫了


        <include
            android:id="@+id/overview_panel"
            layout="@layout/overview_panel" />

    </com.android.launcher3.dragndrop.DragLayer>

image.png

上圖的1和2分別是下邊的3.1和3.2布局

3.1.overview_panel.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android">


    <com.android.quickstep.views.LauncherRecentsView
        android:id="@+id/overview_panel"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:accessibilityPaneTitle="@string/accessibility_recent_apps"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:visibility="invisible" />

    <include
        android:id="@+id/overview_actions_view"
        layout="@layout/overview_actions_container" />

</merge>

3.2.overview_actions_container.xml

<com.android.quickstep.views.OverviewActionsView 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal|bottom">

    <LinearLayout
        android:id="@+id/action_buttons"
        android:layout_width="match_parent"
        android:layout_height="@dimen/overview_actions_height"
        android:layout_gravity="bottom|center_horizontal"
        android:orientation="horizontal">

        <Space
            android:layout_width="0dp"
            android:layout_height="1dp"
            android:layout_weight="1" />

        <Button
            android:id="@+id/action_screenshot"
            style="@style/OverviewActionButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:drawableStart="@drawable/ic_screenshot"
            android:text="@string/action_screenshot"
            android:theme="@style/ThemeControlHighlightWorkspaceColor" />

        <Space
            android:id="@+id/action_split_space"
            android:layout_width="@dimen/overview_actions_button_spacing"
            android:layout_height="1dp"
            android:visibility="gone" />

        <Button
            android:id="@+id/action_split"
            style="@style/OverviewActionButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/action_split"
            android:theme="@style/ThemeControlHighlightWorkspaceColor"
            android:visibility="gone" />

        <Space
            android:layout_width="0dp"
            android:layout_height="1dp"
            android:layout_weight="1" />

        <Space
            android:id="@+id/oav_three_button_space"
            android:layout_width="0dp"
            android:layout_height="1dp"
            android:layout_weight="1"
            android:visibility="gone" />
    </LinearLayout>

</com.android.quickstep.views.OverviewActionsView>

4.LauncherState

顧名思義允睹,就是桌面的狀態(tài)运准,有10種,這里就注釋下我了解的幾種

private static final LauncherState[] sAllStates = new LauncherState[10];

    public static String stateOrdinalToString(int ordinal) {
        switch (ordinal) {
            case NORMAL_STATE_ORDINAL:
                return "Normal";//點(diǎn)擊home鍵回到的頁面就是缭受,默認(rèn)啟動就是這個
            case SPRING_LOADED_STATE_ORDINAL:
                return "SpringLoaded";//拖動圖標(biāo)的時候胁澳,頂部功能按鈕(removed)出現(xiàn)的時候
            case OVERVIEW_STATE_ORDINAL:
                return "Overview";//recents要顯示的
            case OVERVIEW_MODAL_TASK_STATE_ORDINAL:
                return "OverviewModal";
            case QUICK_SWITCH_STATE_ORDINAL:
                return "QuickSwitch";
            case ALL_APPS_STATE_ORDINAL:
                return "AllApps";//桌面上劃顯示所有app圖標(biāo)的那個
            case BACKGROUND_APP_STATE_ORDINAL:
            //launcher在后臺的時候,點(diǎn)擊recents米者,先變成這個狀態(tài)韭畸,再變成overview轉(zhuǎn)改
                return "Background";
            case HINT_STATE_ORDINAL:
                return "Hint";
            case HINT_STATE_TWO_BUTTON_ORDINAL:
                return "Hint2Button";
            case OVERVIEW_SPLIT_SELECT_ORDINAL:
                return "OverviewSplitSelect";
            default:
                return "Unknown";
        }
    }

5.LauncherRecentsView

點(diǎn)擊recents按鈕顯示的頁面就是這個容器了,這里通過在構(gòu)造方法里添加監(jiān)聽的方法蔓搞,獲取state狀態(tài)胰丁,同步修改自己的可見性。

public class LauncherRecentsView extends RecentsView<QuickstepLauncher, LauncherState>
        implements StateListener<LauncherState> {
        
public LauncherRecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr, LauncherActivityInterface.INSTANCE);
    //這里添加了state改變的監(jiān)聽
    mActivity.getStateManager().addStateListener(this);
}

public void startHome() {
//home頁就是normal狀態(tài)
    mActivity.getStateManager().goToState(NORMAL);
    AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted());
}
//這個就是回調(diào)喂分,狀態(tài)改變開始
public void onStateTransitionStart(LauncherState toState) {
    setOverviewStateEnabled(toState.overviewUi);
    setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
    setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
    if (toState == OVERVIEW_MODAL_TASK) {
        setOverviewSelectEnabled(true);
    }
    setFreezeViewVisibility(true);
}

//這個就是回調(diào)锦庸,狀態(tài)改變結(jié)束
public void onStateTransitionComplete(LauncherState finalState) {
    if (finalState == NORMAL || finalState == SPRING_LOADED) {
        reset();
    }
    //點(diǎn)擊recent的話狀態(tài)就是overview
    boolean isOverlayEnabled = finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK;
    setOverlayEnabled(isOverlayEnabled);
    setFreezeViewVisibility(false);
    if (finalState != OVERVIEW_MODAL_TASK) {
        setOverviewSelectEnabled(false);
    }

    if (isOverlayEnabled) {
        runActionOnRemoteHandles(remoteTargetHandle ->
                remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
    }
}    

6.RecentsView

可以看到,又是繼承的PagedView,添加child就可以左右滑動了蒲祈。

public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>,
        STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
        TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
        TaskVisualsChangeListener {
        
public RecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
        BaseActivityInterface sizeStrategy) {
    super(context, attrs, defStyleAttr);
    setEnableFreeScroll(true);
    mSizeStrategy = sizeStrategy;
    mActivity = BaseActivity.fromContext(context);
   //..數(shù)據(jù)的獲取都在這model里
    mModel = RecentsModel.INSTANCE.get(context);
    mIdp = InvariantDeviceProfile.INSTANCE.get(context);
    //加載clearButton布局
    mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
            .inflate(R.layout.overview_clear_all_button, this, false);
    mClearAllButton.setOnClickListener(this::dismissAllTasks);
    //緩存一些我們要用到的view
    mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
            10 /* initial size */);
    mGroupedTaskViewPool = new ViewPool<>(context, this,
            R.layout.task_grouped, 20 /* max size */, 10 /* initial size */);
    mDesktopTaskViewPool = new ViewPool<>(context, this, R.layout.task_desktop,
            5 /* max size */, 1 /* initial size */);

    //初始化empty相關(guān)的圖標(biāo)和文字
    mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
    mEmptyIcon.setCallback(this);
    mEmptyMessage = context.getText(R.string.recents_empty_message);
    mEmptyMessagePaint = new TextPaint();
    mEmptyMessagePaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
    mEmptyMessagePaint.setTextSize(getResources()
            .getDimension(R.dimen.recents_empty_message_text_size));
    mEmptyMessagePaint.setTypeface(Typeface.create(Themes.getDefaultBodyFont(context),
            Typeface.NORMAL));
    mEmptyMessagePaint.setAntiAlias(true);
    mEmptyMessagePadding = getResources()
            .getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
    setWillNotDraw(false);
    updateEmptyMessage();
    //...
}

6.1.init

初始化action view

    public void init(OverviewActionsView actionsView, SplitSelectStateController splitController) {
        mActionsView = actionsView;
        mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
        mSplitSelectStateController = splitController;
    }

QuickstepLauncher.java里會調(diào)用

    protected void setupViews() {
        super.setupViews();

        mActionsView = findViewById(R.id.overview_actions_view);
        RecentsView overviewPanel = getOverviewPanel();
        mSplitSelectStateController =
                new SplitSelectStateController(this, mHandler, getStateManager(),
                        getDepthController(), getStatsLogManager());
        //這調(diào)用
        overviewPanel.init(mActionsView, mSplitSelectStateController);
    //..
        mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
        mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this));

6.2.onAttachedToWindow

主要是添加一堆監(jiān)聽

    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        updateTaskStackListenerState();
        mModel.getThumbnailCache().getHighResLoadingState().addCallback(this);
        mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
        mSyncTransactionApplier = new SurfaceTransactionApplier(this);
        runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
                .setSyncTransactionApplier(mSyncTransactionApplier));
        RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
        mIPipAnimationListener.setActivityAndRecentsView(mActivity, this);
        SystemUiProxy.INSTANCE.get(getContext()).setPipAnimationListener(
                mIPipAnimationListener);
        mOrientationState.initListeners();
        mTaskOverlayFactory.initListeners();
    }

6.3.maybeDrawEmptyMessage

public void draw(Canvas canvas) {
    maybeDrawEmptyMessage(canvas);
    super.draw(canvas);
}

//

    protected void maybeDrawEmptyMessage(Canvas canvas) {
        if (mShowEmptyMessage && mEmptyTextLayout != null) {
            // Offset to center in the visible (non-padded) part of RecentsView
            mTempRect.set(mInsets.left + getPaddingLeft(), mInsets.top + getPaddingTop(),
                    mInsets.right + getPaddingRight(), mInsets.bottom + getPaddingBottom());
            canvas.save();
            canvas.translate(getScrollX() + (mTempRect.left - mTempRect.right) / 2,
                    (mTempRect.top - mTempRect.bottom) / 2);
            mEmptyIcon.draw(canvas);
            canvas.translate(mEmptyMessagePadding,
                    mEmptyIcon.getBounds().bottom + mEmptyMessagePadding);
            mEmptyTextLayout.draw(canvas);
            canvas.restore();
        }
    }

6.4.taskView可見性

看效果圖可以知道甘萧,最多可以顯示3個taskView萝嘁,中間一個完整顯示的,以及兩邊部分顯示的幔嗦。

    public boolean isTaskViewVisible(TaskView tv) {
        if (showAsGrid()) {
            int screenStart = mOrientationHandler.getPrimaryScroll(this);
            int screenEnd = screenStart + mOrientationHandler.getMeasuredSize(this);
            return isTaskViewWithinBounds(tv, screenStart, screenEnd);
        } else {
            // For now, just check if it's the active task or an adjacent task
            return Math.abs(indexOfChild(tv) - getNextPage()) <= 1;
        }
    }

    public boolean isTaskViewFullyVisible(TaskView tv) {
        if (showAsGrid()) {
            int screenStart = mOrientationHandler.getPrimaryScroll(this);
            int screenEnd = screenStart + mOrientationHandler.getMeasuredSize(this);
            return isTaskViewFullyWithinBounds(tv, screenStart, screenEnd);
        } else {
            // For now, just check if it's the active task
            return indexOfChild(tv) == getNextPage();
        }
    }

6.5.resetTaskVisuals

重新設(shè)置下taskView的屬性酿愧,在重新add child以后會執(zhí)行

    public void resetTaskVisuals() {
        for (int i = getTaskViewCount() - 1; i >= 0; i--) {
            TaskView taskView = requireTaskViewAt(i);
            if (mIgnoreResetTaskId != taskView.getTaskIds()[0]) {
                taskView.resetViewTransforms();
                taskView.setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1);
                taskView.setStableAlpha(mContentAlpha);
                taskView.setFullscreenProgress(mFullscreenProgress);
                taskView.setModalness(mTaskModalness);
                taskView.setTaskThumbnailSplashAlpha(mTaskThumbnailSplashAlpha);
            }
        }
        // resetTaskVisuals is called at the end of dismiss animation which could update
        // primary and secondary translation of the live tile cut out. We will need to do so
        // here accordingly.
        runActionOnRemoteHandles(remoteTargetHandle -> {
            TaskViewSimulator simulator = remoteTargetHandle.getTaskViewSimulator();
            simulator.taskPrimaryTranslation.value = 0;
            simulator.taskSecondaryTranslation.value = 0;
            simulator.fullScreenProgress.value = 0;
            simulator.recentsViewScale.value = 1;
        });
        // Similar to setRunningTaskHidden below, reapply the state before runningTaskView is
        // null.
        if (!mRunningTaskShowScreenshot) {
            setRunningTaskViewShowScreenshot(mRunningTaskShowScreenshot);
        }
        if (mRunningTaskTileHidden) {
            setRunningTaskHidden(mRunningTaskTileHidden);
        }

        updateCurveProperties();
        // Update the set of visible task's data
        loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
        setTaskModalness(0);
        setColorTint(0);
    }

6.6.setVisibility

設(shè)置自己的可見性,并同步修改action view的可見性

    public void setVisibility(int visibility) {
        super.setVisibility(visibility);
        if (mActionsView != null) {
            mActionsView.updateHiddenFlags(HIDDEN_NO_RECENTS, visibility != VISIBLE);
            if (visibility != VISIBLE) {
                mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
            }
        }
    }

>調(diào)用的地方

  • 設(shè)置透明度邀泉,為0的時候就不可見了。

      public void setContentAlpha(float alpha) {
          if (alpha == mContentAlpha) {
              return;
          }
          alpha = Utilities.boundToRange(alpha, 0, 1);
          mContentAlpha = alpha;
          int runningTaskId = getTaskIdsForRunningTaskView()[0];
          for (int i = getTaskViewCount() - 1; i >= 0; i--) {
              TaskView child = requireTaskViewAt(i);
              int[] childTaskIds = child.getTaskIds();
              if (!mRunningTaskTileHidden ||
                      (childTaskIds[0] != runningTaskId && childTaskIds[1] != runningTaskId)) {
                  child.setStableAlpha(alpha);
              }
          }
          mClearAllButton.setContentAlpha(mContentAlpha);
          int alphaInt = Math.round(alpha * 255);
          mEmptyMessagePaint.setAlpha(alphaInt);
          mEmptyIcon.setAlpha(alphaInt);
          mActionsView.getContentAlpha().setValue(mContentAlpha);
    
          if (alpha > 0) {
              setVisibility(VISIBLE);
          } else if (!mFreezeViewVisibility) {
          //alpha 為0的時候就不可見了钝鸽。
              setVisibility(INVISIBLE);
          }
      }
    
    

初始化的時候透明度為0汇恤,也就是默認(rèn)啟動的時候是不可見的

    public void init(OverviewActionsView actionsView,
            SplitSelectStateController splitPlaceholderView) {
        super.init(actionsView, splitPlaceholderView);
        setContentAlpha(0);
    }

上邊的方法是在QuickstepLauncher.java的setupViews的方法里調(diào)用的

        overviewPanel.init(mActionsView, mSplitSelectStateController);

  • 下邊這個方法是動畫開始的時候會凍結(jié)view的可見性,結(jié)束的時候會恢復(fù)為false

      public void setFreezeViewVisibility(boolean freezeViewVisibility) {
          if (mFreezeViewVisibility != freezeViewVisibility) {
              mFreezeViewVisibility = freezeViewVisibility;
              if (!mFreezeViewVisibility) {
                  setVisibility(mContentAlpha > 0 ? VISIBLE : INVISIBLE);
              }
          }
      }
    
    

>透明度的改變

前邊有講過拔恰,launcher的view顯示有10種狀態(tài)因谎,我們也簡單分析了幾種常見的。overview的可見性也是通過改變state來實(shí)現(xiàn)的颜懊。有專門的StateManager類來修改state财岔,還有state的處理類。這里簡單貼下非動畫的改變狀態(tài)的情況河爹,動畫的麻煩匠璧,還有l(wèi)istener,就不看了咸这。

BaseRecentsViewStateController.java

    public void setState(@NonNull LauncherState state) {
        float[] scaleAndOffset = state.getOverviewScaleAndOffset(mLauncher);
        RECENTS_SCALE_PROPERTY.set(mRecentsView, scaleAndOffset[0]);
        ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, scaleAndOffset[1]);
        TASK_SECONDARY_TRANSLATION.set(mRecentsView, 0f);

//透明度就是這個了夷恍。overview狀態(tài),透明度為1可見媳维,非overview狀態(tài)酿雪,透明度為0,不可見
        getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
        getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
        RECENTS_GRID_PROGRESS.set(mRecentsView,
                state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f);
        TASK_THUMBNAIL_SPLASH_ALPHA.set(mRecentsView, state.showTaskThumbnailSplash() ? 1f : 0f);
    }

RecentsViewStateController.java

    FloatProperty<RecentsView> getContentAlphaProperty() {
        return CONTENT_ALPHA;
    }

//看下set方法侄刽,可以看到指黎,就是設(shè)置recentsView的透明度,上邊有貼代碼州丹,透明度為0就不可見了醋安。

    public static final FloatProperty<RecentsView> CONTENT_ALPHA =
            new FloatProperty<RecentsView>("contentAlpha") {
                @Override
                public void setValue(RecentsView view, float v) {
                    view.setContentAlpha(v);
                }

                @Override
                public Float get(RecentsView view) {
                    return view.getContentAlpha();
                }
            };

6.7.數(shù)據(jù)的獲取

state改變的回調(diào)哪里調(diào)用的后邊再分析。

>onStateTransitionStart

    public void onStateTransitionStart(LauncherState toState) {
    //overview狀態(tài)的話這個為true
        setOverviewStateEnabled(toState.overviewUi);
        setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
        setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
        if (toState == OVERVIEW_MODAL_TASK) {
            setOverviewSelectEnabled(true);
        }
        setFreezeViewVisibility(true);
    }

>setOverviewStateEnabled

overview狀態(tài)的話這個為true当叭,非overview的時候又成了false

    public void setOverviewStateEnabled(boolean enabled) {
        mOverviewStateEnabled = enabled;
        //更新數(shù)據(jù)
        updateTaskStackListenerState();
        mOrientationState.setRotationWatcherEnabled(enabled);
        if (!enabled) {
            // Reset the running task when leaving overview since it can still have a reference to
            // its thumbnail
            mTmpRunningTasks = null;
            mSplitBoundsConfig = null;
        }
        updateLocusId();
    }

>updateTaskStackListenerState

mHandleTaskStackChanges默認(rèn)是false的茬故,當(dāng)我們點(diǎn)擊recent以后就要顯示overview了,這時候handleTaskStackChanges為true蚁鳖,后邊幾個條件都是true的磺芭。當(dāng)退出overview的時候這里的值又成了false,下次再次進(jìn)入overview的時候又成了true

    private void updateTaskStackListenerState() {
        boolean handleTaskStackChanges = mOverviewStateEnabled && isAttachedToWindow()
                && getWindowVisibility() == VISIBLE;
        if (handleTaskStackChanges != mHandleTaskStackChanges) {
            mHandleTaskStackChanges = handleTaskStackChanges;
            if (handleTaskStackChanges) {
                reloadIfNeeded();
            }
        }
    }

//reloadIfNeeded調(diào)用的是TaskList的方法獲取數(shù)據(jù)

    public int getTasks(Consumer<ArrayList<GroupTask>> callback, Predicate<GroupTask> filter) {
        return mTaskList.getTasks(false /* loadKeysOnly */, callback, filter);
    }


>reloadIfNeeded

這個方法在每次overview顯示的時候都會調(diào)用醉箕,

    public void reloadIfNeeded() {
        if (!mModel.isTaskListValid(mTaskListChangeId)) {
            mTaskListChangeId = mModel.getTasks(this::applyLoadPlan, RecentsFilterState
                    .getFilter(mFilterState.getPackageNameToFilter()));
        }
    }

#onStateTransitionComplete

可以看到钾腺,每次退出overview回到normal狀態(tài)的時候徙垫,會調(diào)用reset

    public void onStateTransitionComplete(LauncherState finalState) {
        if (finalState == NORMAL || finalState == SPRING_LOADED) {
            // Clean-up logic that occurs when recents is no longer in use/visible.
            reset();
        }

#reset

id又改成-1了

    public void reset() {
        setCurrentTask(-1);
        mCurrentPageScrollDiff = 0;
        mIgnoreResetTaskId = -1;
        mTaskListChangeId = -1;
        mFocusedTaskViewId = -1;


6.8.applyLoadPlan

這個就是獲取數(shù)據(jù)以后的回調(diào)了,主要處理child的添加

    protected void applyLoadPlan(ArrayList<GroupTask> taskGroups) {
        if (mPendingAnimation != null) {
            mPendingAnimation.addEndListener(success -> applyLoadPlan(taskGroups));
            return;
        }

        mLoadPlanEverApplied = true;
        //數(shù)據(jù)為null的情況
        if (taskGroups == null || taskGroups.isEmpty()) {
        //移除所有相關(guān)的view
            removeTasksViewsAndClearAllButton();
            //顯示no recent的提示
            onTaskStackUpdated();
            // With all tasks removed, touch handling in PagedView is disabled and we need to reset
            // touch state or otherwise values will be obsolete.
            resetTouchState();
            if (isPageScrollsInitialized()) {
                onPageScrollsInitialized();
            }
            return;
        }

    //..中間一堆代碼就是獲取容器舊的狀態(tài)放棒,方便后邊恢復(fù)姻报,因?yàn)橄逻厱瞥械腸hild
        removeAllViews();

    //..

        // update the map of instance counts
        mFilterState.updateInstanceCountMap(taskGroups);
    //根據(jù)數(shù)據(jù),循環(huán)添加child间螟,主要有兩種吴旋,single,group厢破,其實(shí)還有個desktop不知道啥玩意
        for (int i = taskGroups.size() - 1; i >= 0; i--) {
            GroupTask groupTask = taskGroups.get(i);
            boolean isRemovalNeeded = stagedTaskToBeRemovedFromGrid != null
                    && groupTask.containsTask(stagedTaskToBeRemovedFromGrid.key.id);

          
            TaskView taskView;
            if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
                // If we need to remove half of a pair of tasks, force a TaskView with Type.SINGLE
                // to be a temporary container for the remaining task.
                taskView = getTaskViewFromPool(TaskView.Type.SINGLE);
            } else {
                taskView = getTaskViewFromPool(groupTask.taskViewType);
            }
            //添加到容器
            addView(taskView);

    //下邊就是綁定數(shù)據(jù)了
            if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
                if (groupTask.task1.equals(stagedTaskToBeRemovedFromGrid)) {
                    taskView.bind(groupTask.task2, mOrientationState);
                } else {
                    taskView.bind(groupTask.task1, mOrientationState);
                }
            } else if (isRemovalNeeded) {
                // If the task we need to remove is not part of a pair, bind it to the TaskView
                // first (to prevent problems), then remove the whole thing.
                taskView.bind(groupTask.task1, mOrientationState);
                removeView(taskView);
            } else if (taskView instanceof GroupedTaskView) {
                boolean firstTaskIsLeftTopTask =
                        groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
                Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
                Task rightBottomTask = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1;

                ((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, mOrientationState,
                        groupTask.mSplitBounds);
            } else if (taskView instanceof DesktopTaskView) {
                ((DesktopTaskView) taskView).bind(((DesktopTask) groupTask).tasks,
                        mOrientationState);
            } else {
                taskView.bind(groupTask.task1, mOrientationState);
            }

            // enables instance filtering if the feature flag for it is on
            if (FeatureFlags.ENABLE_MULTI_INSTANCE.get()) {
                taskView.setUpShowAllInstancesListener();
            }
        }
        //添加clearAll button
        if (!taskGroups.isEmpty()) {
            addView(mClearAllButton);
        }

        // Keep same previous focused task
        TaskView newFocusedTaskView = getTaskViewByTaskId(focusedTaskId);
        // If the list changed, maybe the focused task doesn't exist anymore
        if (newFocusedTaskView == null && getTaskViewCount() > 0) {
            newFocusedTaskView = getTaskViewAt(0);
        }
        mFocusedTaskViewId = newFocusedTaskView != null ?
                newFocusedTaskView.getTaskViewId() : -1;
        updateTaskSize();
        updateChildTaskOrientations();

        TaskView newRunningTaskView = null;
        if (runningTaskId != -1) {
            // Update mRunningTaskViewId to be the new TaskView that was assigned by binding
            // the full list of tasks to taskViews
            newRunningTaskView = getTaskViewByTaskId(runningTaskId);
            if (newRunningTaskView != null) {
                mRunningTaskViewId = newRunningTaskView.getTaskViewId();
            } else {
                mRunningTaskViewId = -1;
            }
        }

        int targetPage = -1;
        if (mNextPage != INVALID_PAGE) {
            // Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
            mCurrentPage = previousCurrentPage;
            if (currentTaskId != -1) {
                currentTaskView = getTaskViewByTaskId(currentTaskId);
                if (currentTaskView != null) {
                    targetPage = indexOfChild(currentTaskView);
                }
            }
        } else {
            // Set the current page to the running task, but not if settling on new task.
            if (runningTaskId != -1) {
                targetPage = indexOfChild(newRunningTaskView);
            } else if (getTaskViewCount() > 0) {
                targetPage = indexOfChild(requireTaskViewAt(0));
            }
        }
        if (targetPage != -1 && mCurrentPage != targetPage) {
            int finalTargetPage = targetPage;
            runOnPageScrollsInitialized(() -> {
                setCurrentPage(finalTargetPage);
            });
        }

        if (mIgnoreResetTaskId != -1 &&
                getTaskViewByTaskId(mIgnoreResetTaskId) != ignoreResetTaskView) {
            mIgnoreResetTaskId = -1;
        }
        //taskView 屬性設(shè)置
        resetTaskVisuals();
        onTaskStackUpdated();
        updateEnabledOverlays();
        if (isPageScrollsInitialized()) {
            onPageScrollsInitialized();
        }
    }

7.TaskView

這個就是recent里邊顯示的控件了,加載的布局是這個

7.1.task.xml

<com.android.quickstep.views.TaskView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="false"
    android:defaultFocusHighlightEnabled="false"
    android:focusable="true">

    <com.android.quickstep.views.TaskThumbnailView
        android:id="@+id/snapshot"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <ImageView
        android:id="@+id/show_windows"
        android:layout_height="@dimen/recents_filter_icon_size"
        android:layout_width="@dimen/recents_filter_icon_size"
        android:layout_gravity="end"
        android:alpha="0"
        android:tint="@color/recents_filter_icon"
        android:contentDescription="@string/recents_filter_icon_desc"
        android:importantForAccessibility="no"
        android:src="@drawable/ic_select_windows" />

    <com.android.quickstep.views.IconView
        android:id="@+id/icon"
        android:layout_width="@dimen/task_thumbnail_icon_size"
        android:layout_height="@dimen/task_thumbnail_icon_size"
        android:focusable="false"
        android:importantForAccessibility="no"/>
</com.android.quickstep.views.TaskView>

7.2.TaskView

public class TaskView extends FrameLayout implements Reusable {

>onClick

    private void onClick(View view) {
        if (getTask() == null) {
            return;
        }
        if (confirmSecondSplitSelectApp()) {
            return;
        }
        launchTasks();
    }

>bind

這個就是綁定數(shù)據(jù)了荣瑟,6.8里有用到

    public void bind(Task task, RecentsOrientedState orientedState) {
        cancelPendingLoadTasks();
        mTask = task;
        mTaskIdContainer[0] = mTask.key.id;
        mTaskIdAttributeContainer[0] = new TaskIdAttributeContainer(task, mSnapshotView,
                mIconView, STAGE_POSITION_UNDEFINED);
        mSnapshotView.bind(task);
        setOrientationState(orientedState);
    }

8.GroupedTaskView

就是一個屏幕同時顯示2個app的情況。 我們可以在recents列表里摩泪,點(diǎn)擊某個app上邊的圖標(biāo)笆焰,有個彈框,選擇split top见坑,完事這個app就顯示在頂部了嚷掠,這時候再選一個要同時顯示的app,在其縮略圖上點(diǎn)一下荞驴,就能看到這種效果了不皆。

public class GroupedTaskView extends TaskView {

[圖片上傳失敗...(image-94666b-1687158554079)]

8.1.task_group.xml

可以看到,有2個用來顯示icon戴尸,2個用來顯示縮略圖

<com.android.quickstep.views.GroupedTaskView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="false"
    android:defaultFocusHighlightEnabled="false"
    android:focusable="true">

    <com.android.quickstep.views.TaskThumbnailView
        android:id="@+id/snapshot"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <com.android.quickstep.views.TaskThumbnailView
        android:id="@+id/bottomright_snapshot"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
        
    <ImageView
        android:id="@+id/show_windows"/>
    <ImageView
        android:id="@+id/show_windows_right" />

    <com.android.quickstep.views.IconView
        android:id="@+id/icon"
        android:layout_width="@dimen/task_thumbnail_icon_size"
        android:layout_height="@dimen/task_thumbnail_icon_size"
        android:focusable="false"
        android:importantForAccessibility="no"/>

    <com.android.quickstep.views.IconView
        android:id="@+id/bottomRight_icon"
        android:layout_width="@dimen/task_thumbnail_icon_size"
        android:layout_height="@dimen/task_thumbnail_icon_size"
        android:focusable="false"
        android:importantForAccessibility="no"/>
</com.android.quickstep.views.GroupedTaskView>

9.RecentsModel.java

   private RecentsModel(Context context) {
        mContext = context;
        //app列表
        mTaskList = new RecentTasksList(MAIN_EXECUTOR,
                context.getSystemService(KeyguardManager.class),
                SystemUiProxy.INSTANCE.get(context));

        IconProvider iconProvider = new IconProvider(context);
        //app的圖標(biāo)
        mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
        mIconCache.registerTaskVisualsChangeListener(this);
        //app的縮略圖
        mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);

        //這個很重要粟焊,監(jiān)聽task改變的回調(diào)設(shè)置
        TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
        iconProvider.registerIconChangeListener(this, MAIN_EXECUTOR.getHandler());
    }

10.recent啟動的流程

03NavigationBar的4.7小節(jié)的recents點(diǎn)擊事件里有講到 最終的事件處理是交給一個服務(wù)了,再簡單貼下相關(guān)的代碼

OverviewProxyService.java

private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
    private void internalConnectToCurrentUser() {
        //...
        Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
                .setPackage(mRecentsComponentName.getPackageName());
        try {
            mBound = mContext.bindServiceAsUser(launcherServiceIntent,
                    mOverviewServiceConnection,
                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
                    UserHandle.of(mUserTracker.getUserId()));
        } catch (SecurityException e) {
//...
    }

相關(guān)的服務(wù)

10.1.TouchInteractionService.java

        <service android:name="com.android.quickstep.TouchInteractionService"
             android:permission="android.permission.STATUS_BAR_SERVICE"
             android:directBootAware="true"
             android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.QUICKSTEP_SERVICE"/>
            </intent-filter>
        </service>

TouchInteractionService里的binder如下

    private final TISBinder mTISBinder = new TISBinder();

//TISBinder

    public class TISBinder extends IOverviewProxy.Stub {
    //...
    //這個就是recents按鈕點(diǎn)擊以后調(diào)用的代碼
    public void onOverviewToggle() {
    //...
        TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
        //這里
        mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE);
    }

>onUserUnlocked

解鎖屏幕以后的回調(diào)

    public void onUserUnlocked() {
        mTaskAnimationManager = new TaskAnimationManager(this);
        //oberver類
        mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
        //初始化了helper類
        mOverviewCommandHelper = new OverviewCommandHelper(this,
                mOverviewComponentObserver, mTaskAnimationManager);
                
        mResetGestureInputConsumer = new ResetGestureInputConsumer(
                mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext);
        mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
        mInputConsumer.registerInputConsumer();
        onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags());
        onAssistantVisibilityChanged();

        // Initialize the task tracker
        TopTaskTracker.INSTANCE.get(this);

        // Temporarily disable model preload
        // new ModelPreload().start(this);
        resetHomeBounceSeenOnQuickstepEnabledFirstTime();

        mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange);
        onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
    }

10.2.OverviewCommandHelper.java

    private <T extends StatefulActivity<?>> boolean executeCommand(CommandInfo cmd) {
        BaseActivityInterface<?, T> activityInterface =
                mOverviewComponentObserver.getActivityInterface();
                //只有overview在前臺的話不為null孙蒙,
        RecentsView recents = activityInterface.getVisibleRecentsView();
        //這里只分析下點(diǎn)擊recents按鈕的邏輯项棠,其他type不研究
        if (recents == null) {
    //..
        } else {
            switch (cmd.type) {
            //..
                case TYPE_TOGGLE://recents已經(jīng)顯示的情況,再點(diǎn)擊就會執(zhí)行這個
                    return launchTask(recents, getNextTask(recents), cmd);
                case TYPE_HOME:
                    recents.startHome();
                    return true;
            }
        }        
        //第一種情況挎峦,launcher在前臺香追,這里基本會返回true
      if (activityInterface.switchToRecentsIfVisible(completeCallback)) {
        // If successfully switched, wait until animation finishes
        return false;
    }          
    //第二種,launcher不在前臺坦胶,上邊的if結(jié)果是false透典,會繼續(xù)往下走
        final T activity = activityInterface.getCreatedActivity();
        if (activity != null) {
            InteractionJankMonitorWrapper.begin(
                    activity.getRootView(),
                    InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
        }
    //..
    //最終一般會走else這里,啟動intent
        } else {
            Intent intent = new Intent(interactionHandler.getLaunchIntent());
            intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, gestureState.getGestureId());
            cmd.mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(
                    gestureState, intent, interactionHandler);
            interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/);
            cmd.mActiveCallbacks.addListener(recentAnimListener);
        }
        

>activityInterface

這里貼下activityinterface的代碼顿苇,

            mActivityInterface = LauncherActivityInterface.INSTANCE;

//LauncherActivityInterface.java

    @Override
    public RecentsView getVisibleRecentsView() {
        Launcher launcher = getVisibleLauncher();
        //非overview狀態(tài)的話返回null峭咒,overview狀態(tài)就返回對應(yīng)的recentView
        RecentsView recentsView =
                launcher != null && launcher.getStateManager().getState().overviewUi
                        ? launcher.getOverviewPanel() : null;
        if (recentsView == null || (!launcher.hasBeenResumed()
                && recentsView.getRunningTaskViewId() == -1)) {
            // If live tile has ended, return null.
            return null;
        }
        return recentsView;
    }

>launchTask

recentView顯示的時候,再次點(diǎn)擊recents按鈕纪岁,就會打開某個歷史app凑队,具體打開哪個看下邊的邏輯代碼

    private boolean launchTask(RecentsView recents, @Nullable TaskView taskView, CommandInfo cmd) {
        RunnableList callbackList = null;
        if (taskView != null) {
            taskView.setEndQuickswitchCuj(true);
            callbackList = taskView.launchTasks();
        }

        if (callbackList != null) {
            callbackList.add(() -> scheduleNextTask(cmd));
            return false;
        } else {
        //可以看到taskView為null的話,就直接顯示桌面了幔翰。
            recents.startHome();
            return true;
        }
    }

#getNextTask

看下上邊的taskView的獲取邏輯漩氨,先查找有沒有runningTaskView西壮,沒有的話直接返回第一個,有的話叫惊,taskid加1查找nextTaskView款青,有的話返回,沒有的話就還用runningtaskView

    private TaskView getNextTask(RecentsView view) {
        final TaskView runningTaskView = view.getRunningTaskView();

        if (runningTaskView == null) {
            return view.getTaskViewAt(0);
        } else {
            final TaskView nextTask = view.getNextTaskView();
            return nextTask != null ? nextTask : runningTaskView;
        }
    }

>情況一launcher在前臺

測試步驟: 點(diǎn)擊home鍵回到桌面霍狰,然后點(diǎn)擊recents按鈕

launcher在前臺的邏輯 LauncherActivityInterface.java

    public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
        Launcher launcher = getVisibleLauncher();
        if (launcher == null) {
            return false;//在前臺的話抡草,這里不會為null的,所以會走到下邊
        }
        closeOverlay();
        //會走這里
        launcher.getStateManager().goToState(OVERVIEW,
                launcher.getStateManager().shouldAnimateStateChange(),
                onCompleteCallback == null ? null : forEndCallback(onCompleteCallback));
        return true;
    }

goToState里會會調(diào)listener里的方法蔗坯,我們的RecentsView里就有添加listener渠牲,所以會做出對應(yīng)的操作,顯示或者隱藏自己步悠。

>情況二launcher在后臺?

測試步驟:隨便打開一個app瘫镇,然后點(diǎn)擊recents按鈕鼎兽。

啟動的intent內(nèi)容

Intent { act=android.intent.action.MAIN 
cat=[android.intent.category.HOME] 
flg=0x10000000 
pkg=com.android.launcher3 cmp=com.android.launcher3/.uioverrides.QuickstepLauncher 
(has extras) }

代碼的話是這個

    private static Intent createHomeIntent() {
        return new Intent(Intent.ACTION_MAIN)
                .addCategory(Intent.CATEGORY_HOME)
                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    }

對應(yīng)的是launcher3目錄下的quickstep目錄下的AndroidManifest-launcher.xml文件里

        <activity
           android:name="com.android.launcher3.uioverrides.QuickstepLauncher">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <action android:name="android.intent.action.SHOW_WORK_APPS" />
                <category android:name="android.intent.category.HOME" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.MONKEY"/>
                <category android:name="android.intent.category.LAUNCHER_APP" />
            </intent-filter>
        </activity>

看完intent的內(nèi)容,想當(dāng)然的以為這個就是跳轉(zhuǎn)到QuickstepLauncher铣除,結(jié)果我在onResume里添加日志啥都沒打印谚咬,可recentView頁面確實(shí)出來了,這個頁面也是launcher頁面的view尚粘。后邊一路順著和intent有關(guān)的代碼看了下择卦,啥也看不出來,先簡單記錄下相關(guān)的.

后邊有看到SurfaceController郎嫁,感覺是把launcher繪制出來了秉继,頂層那個activity沒有繪制或者繪制到底層去了,到底咋弄的期待高手了泽铛。

#1 else

        // ----------#2
     cmd.mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(
                    gestureState, intent, interactionHandler);
        interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/);
      //總共加了4個listener尚辑,隨便注釋一個,overview頁面都出不來盔腔,里邊的回調(diào)太復(fù)雜了杠茬,啥也看不懂。
        cmd.mActiveCallbacks.addListener(recentAnimListener);

#2 startRecentsAnimation

TaskAnimationManager.java

    final BaseActivityInterface activityInterface = gestureState.getActivityInterface();
    mLastGestureState = gestureState;
    mCallbacks = new RecentsAnimationCallbacks(SystemUiProxy.INSTANCE.get(mCtx),
            activityInterface.allowMinimizeSplitScreen());

    mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
            @Override
            public void onRecentsAnimationStart(RecentsAnimationController controller,
                    RecentsAnimationTargets targets) {
                activityInterface.runOnInitBackgroundStateUI(() ->
                        interactionHandler.onGestureEnded(0, new PointF(), new PointF()));
                cmd.removeListener(this);
            }        
    //...
    mCallbacks.addListener(gestureState);
    mCallbacks.addListener(listener);
     //...
    } else {
        UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
                .startRecentsActivity(intent, eventTime, mCallbacks, null, null));
    }
    gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED);
    return mCallbacks;
}

#3 RecentsAnimationCallbacks

4里的回調(diào)會調(diào)用這個方法弛随,這個方法里可以看到瓢喉,最終主要是調(diào)用listener的方法,我們在#1#2里添加了4個listener的舀透。

    public final void onAnimationStart(RecentsAnimationControllerCompat animationController,
            RemoteAnimationTarget[] appTargets,
            RemoteAnimationTarget[] wallpaperTargets,
            Rect homeContentInsets, Rect minimizedHomeBounds) {
        mController = new RecentsAnimationController(animationController,
                mAllowMinimizeSplitScreen, this::onAnimationFinished);

        if (mCancelled) {
            Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
                    mController::finishAnimationToApp);
        } else {
            RemoteAnimationTarget[] nonAppTargets =
                    mSystemUiProxy.onGoingToRecentsLegacy(appTargets);
            if (nonAppTargets == null) {
                nonAppTargets = new RemoteAnimationTarget[0];
            }
            final RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
                    wallpaperTargets, nonAppTargets, homeContentInsets, minimizedHomeBounds);

            Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {

                for (RecentsAnimationListener listener : getListeners()) {
                    listener.onRecentsAnimationStart(mController, targets);
                }
            });
        }
    }

#4 ActivityManagerWrapper.java

    public boolean startRecentsActivity(
            Intent intent, long eventTime, RecentsAnimationListener animationHandler) {
        try {
            IRecentsAnimationRunner runner = null;
            if (animationHandler != null) {
                runner = new IRecentsAnimationRunner.Stub() {
                    @Override
                    public void onAnimationStart(IRecentsAnimationController controller,
                            RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
                            Rect homeContentInsets, Rect minimizedHomeBounds) {
                        final RecentsAnimationControllerCompat controllerCompat =
                                new RecentsAnimationControllerCompat(controller);
                                //這里的handler就是#2里的mCallbacks栓票,會調(diào)用mCallbacks添加的listener
                        animationHandler.onAnimationStart(controllerCompat, apps,
                                wallpapers, homeContentInsets, minimizedHomeBounds);
                    }

                    @Override
                    public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) {
                        animationHandler.onAnimationCanceled(
                                ThumbnailData.wrap(taskIds, taskSnapshots));
                    }

                    @Override
                    public void onTasksAppeared(RemoteAnimationTarget[] apps) {
                        animationHandler.onTasksAppeared(apps);
                    }
                };
            }
            getService().startRecentsActivity(intent, eventTime, runner);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

#5 getService()

ActivityTaskManager.java

    public static IActivityTaskManager getService() {
        return IActivityTaskManagerSingleton.get();
    }

    @UnsupportedAppUsage(trackingBug = 129726065)
    private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
            new Singleton<IActivityTaskManager>() {
                @Override
                protected IActivityTaskManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
                    return IActivityTaskManager.Stub.asInterface(b);
                }
            };

最終實(shí)現(xiàn)類是下邊這個類

#6 ActivityTaskManagerService.java

    public void startRecentsActivity(Intent intent, long eventTime,
            @Nullable IRecentsAnimationRunner recentsAnimationRunner) {
        enforceTaskPermission("startRecentsActivity()");
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
                final String recentsFeatureId = mRecentTasks.getRecentsComponentFeatureId();
                final int recentsUid = mRecentTasks.getRecentsComponentUid();
                final WindowProcessController caller = getProcessController(callingPid, callingUid);

                // Start a new recents animation
                final RecentsAnimation anim = new RecentsAnimation(this, mTaskSupervisor,
                        getActivityStartController(), mWindowManager, intent, recentsComponent,
                        recentsFeatureId, recentsUid, caller);
                if (recentsAnimationRunner == null) {
                } else {//-----------走的else,#7
                    anim.startRecentsActivity(recentsAnimationRunner, eventTime);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

#7 RecentsAnimation.java

    void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner, long eventTime) {

    //...
        // If the activity is associated with the root recents task, then try and get that first
        Task targetRootTask = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED,
                mTargetActivityType);
        ActivityRecord targetActivity = getTargetActivity(targetRootTask);
        final boolean hasExistingActivity = targetActivity != null;
        if (hasExistingActivity) {
            mRestoreTargetBehindRootTask = getRootTaskAbove(targetRootTask);
           
            if (mRestoreTargetBehindRootTask == null
                    && targetRootTask.getTopMostTask() == targetActivity.getTask()) {
                notifyAnimationCancelBeforeStart(recentsAnimationRunner);
                return;
            }
        }

        if (targetActivity == null || !targetActivity.isVisibleRequested()) {
            mService.mRootWindowContainer.startPowerModeLaunchIfNeeded(
                    true /* forceSend */, targetActivity);
        }

        final LaunchingState launchingState =
                mTaskSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent);

        setProcessAnimating(true);
        mService.deferWindowLayout(); // 延遲窗口布局
        try {
            if (hasExistingActivity) {
                // Move the recents activity into place for the animation if it is not top most
                mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(targetRootTask);
//...
            } else {
        //...
            }


            targetActivity.mLaunchTaskBehind = true;
            mLaunchedTargetActivity = targetActivity;

            targetActivity.intent.replaceExtras(mTargetIntent);

            // ---------#8
            mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
                    this, mDefaultTaskDisplayArea.getDisplayId(),
                    mTaskSupervisor.mRecentTasks.getRecentTaskIds(), targetActivity);

            mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);

    //..
            // Register for root task order changes
            mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(this);
        } catch (Exception e) {
           
        } finally {
            mService.continueWindowLayout();//恢復(fù)窗口布局
           
        }
    }

#8 initializeRecentsAnimation

    void initializeRecentsAnimation(int targetActivityType,
            IRecentsAnimationRunner recentsAnimationRunner,
            RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId,
            SparseBooleanArray recentTaskIds, ActivityRecord targetActivity) {
        mRecentsAnimationController = new RecentsAnimationController(this, recentsAnimationRunner,
                callbacks, displayId);
        mRoot.getDisplayContent(displayId).mAppTransition.updateBooster();
        mRecentsAnimationController.initialize(targetActivityType, recentTaskIds, targetActivity);
    }

//RecentsAnimationController

    public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds,
            ActivityRecord targetActivity) {
        mTargetActivityType = targetActivityType;
        mDisplayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);


        final ArrayList<Task> visibleTasks = mDisplayContent.getDefaultTaskDisplayArea()
                .getVisibleTasks();
        final Task targetRootTask = mDisplayContent.getDefaultTaskDisplayArea()
                .getRootTask(WINDOWING_MODE_UNDEFINED, targetActivityType);
        if (targetRootTask != null) {
            final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) ->
                { if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class),
                    visibleTasks);
            targetRootTask.forAllLeafTasks(c, true /* traverseTopToBottom */);
            c.recycle();
        }

        final int taskCount = visibleTasks.size();
        for (int i = taskCount - 1; i >= 0; i--) {
            final Task task = visibleTasks.get(i);
            if (skipAnimation(task)) {
                continue;
            }
            // --------------------#9
            addAnimation(task, !recentTaskIds.get(task.mTaskId), false /* hidden */,
                    (type, anim) -> task.forAllWindows(win -> {
                        win.onAnimationFinished(type, anim);
                    }, true /* traverseTopToBottom */));
        }

        // Skip the animation if there is nothing to animate
        if (mPendingAnimations.isEmpty()) {
            cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-noVisibleTasks");
            return;
        }

        try {
            linkToDeathOfRunner();
        } catch (RemoteException e) {
            cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-failedToLinkToDeath");
            return;
        }

        attachNavigationBarToApp();

        // Adjust the wallpaper visibility for the showing target activity

        mTargetActivityRecord = targetActivity;
        if (targetActivity.windowsCanBeWallpaperTarget()) {
            mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
            mDisplayContent.setLayoutNeeded();
        }

        mService.mWindowPlacerLocked.performSurfacePlacement();

        mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(targetActivity);

        // Notify that the animation has started
        if (mStatusBar != null) {
            mStatusBar.onRecentsAnimationStateChanged(true /* running */);
        }
    }

#9 RecentsAnimationController.java

    TaskAnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible, boolean hidden,
            OnAnimationFinishedCallback finishedCallback) {
        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addAnimation(%s)", task.getName());
        final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task,
                isRecentTaskInvisible);
                //----- #11
        task.startAnimation(task.getPendingTransaction(), taskAdapter, hidden,
                ANIMATION_TYPE_RECENTS, finishedCallback);
        task.commitPendingTransaction();
        mPendingAnimations.add(taskAdapter);
        return taskAdapter;
    }

#10 task type

記錄下常見的task的類型

    public static String activityTypeToString(@ActivityType int applicationType) {
        switch (applicationType) {
            case ACTIVITY_TYPE_UNDEFINED: return "undefined";//0
            case ACTIVITY_TYPE_STANDARD: return "standard";//1
            case ACTIVITY_TYPE_HOME: return "home";//2
            case ACTIVITY_TYPE_RECENTS: return "recents";//3
            case ACTIVITY_TYPE_ASSISTANT: return "assistant";//4
            case ACTIVITY_TYPE_DREAM: return "dream";//5
        }
        return String.valueOf(applicationType);
    }

#11 startAnimation

Task類的父類的父類 WindowContainer.java

    protected final SurfaceAnimator mSurfaceAnimator;
    void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
            @AnimationType int type,
            @Nullable OnAnimationFinishedCallback animationFinishedCallback,
            @Nullable Runnable animationCancelledCallback,
            @Nullable AnimationAdapter snapshotAnim) {

        mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
                animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
    }

SurfaceAnimator里有用到SurfaceController類盐杂,這個看起來像是我們要的東西逗载,不細(xì)究了哆窿,太復(fù)雜了。

10.3.StateManager.java

>addStateListener

LauncherRecentsView里用到這個厉斟,監(jiān)聽狀態(tài)的改變好更改自己的可見性挚躯。

    public void addStateListener(StateListener listener) {
        mListeners.add(listener);
    }

>goToState

    private void goToState(
            STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) {
        animated &= areAnimatorsEnabled();
        //要改變的狀態(tài)和activity當(dāng)前狀態(tài)一樣,直接回調(diào)即可
        if (mActivity.isInState(state)) {
            if (mConfig.currentAnimation == null) {
                // Run any queued runnable
                if (listener != null) {
                    listener.onAnimationEnd(null);
                }
                return;
            } else if (!mConfig.userControlled && animated && mConfig.targetState == state) {
                // We are running the same animation as requested
                if (listener != null) {
                    mConfig.currentAnimation.addListener(listener);
                }
                return;
            }
        }
        //不需要動畫
        if (!animated) {
            mAtomicAnimationFactory.cancelAllStateElementAnimation();
            //回調(diào)listener
            onStateTransitionStart(state);
            //有handler的話調(diào)用下對應(yīng)的方法
            for (StateHandler handler : getStateHandlers()) {
                handler.setState(state);
            }
            //回調(diào)listener
            onStateTransitionEnd(state);

            // Run any queued runnable
            if (listener != null) {
                listener.onAnimationEnd(null);
            }
            return;
        }

        if (delay > 0) {
        //延遲后繼續(xù)調(diào)用下邊的方法
        } else {
        //帶動畫過程的擦秽,不看了
            goToStateAnimated(state, fromState, listener);
        }
    }

11. 總結(jié)

  • 主要介紹了码荔,我們點(diǎn)擊recents按鈕以launcher3里邊服務(wù)如何啟動對應(yīng)的頁面
  • recent view的布局結(jié)構(gòu)
  • 自定義的RecentsView邏輯,監(jiān)聽launcher的狀態(tài)變化感挥,隱藏顯示自己
  • 子控件TaskView
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末缩搅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子触幼,更是在濱河造成了極大的恐慌硼瓣,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件置谦,死亡現(xiàn)場離奇詭異堂鲤,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)媒峡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門瘟栖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谅阿,你說我怎么就攤上這事半哟。” “怎么了签餐?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵寓涨,是天一觀的道長。 經(jīng)常有香客問我贱田,道長缅茉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任男摧,我火速辦了婚禮蔬墩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘耗拓。我一直安慰自己拇颅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布乔询。 她就那樣靜靜地躺著樟插,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上黄锤,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天搪缨,我揣著相機(jī)與錄音,去河邊找鬼鸵熟。 笑死副编,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的流强。 我是一名探鬼主播痹届,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼打月!你這毒婦竟也來了队腐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤奏篙,失蹤者是張志新(化名)和其女友劉穎柴淘,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秘通,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡悠就,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了充易。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡荸型,死狀恐怖盹靴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瑞妇,我是刑警寧澤稿静,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站辕狰,受9級特大地震影響改备,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蔓倍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一悬钳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧偶翅,春花似錦默勾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春环疼,著一層夾襖步出監(jiān)牢的瞬間习霹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工炫隶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留淋叶,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓等限,卻偏偏與公主長得像爸吮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子望门,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容