android P/Q/R/S 9/10/11/12多任务手势动画OtherActivityInputConsumer情况-第一节

Posted Android高级知识分享官

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android P/Q/R/S 9/10/11/12多任务手势动画OtherActivityInputConsumer情况-第一节相关的知识,希望对你有一定的参考价值。

hi,在手机屏幕屏占比越来越大的时候,用户对手机的屏幕交互体验也在提升,把原来的导航键3个按钮的交互方式,在android P产生了一个巨大的变化,增加了全面屏上导航手势的方式,后面版本也是在P的基础进行一些细微修改。
[入门课,实战课,跨进程专题]

导航手势给人的用户体验也确实很不错,但是他的整套代码实现也是相当复杂的,今天我们对导航手势的OtherActivityInputConsumer情况来进行分析。
1、首先OtherActivityInputConsumer指的是什么情况呢?
这里我们字面意思就可以知道它大概是指的是目前在其他Activity,要通过手势返回到Launcher,这样一个场景。例如一下几幅图:

这个时候处于联系人界面

通过手势滑动到Launcher

直观剖析涉及几个过程:
1、联系人界面在手指滑动过程中实际还依然是一个Activity的窗口,因为滑动过程中Activity窗口是可以更新变化的,加上可以看events日志:

11-29 03:58:57.046 25180 25180 I am_on_restart_called: [0,com.android.launcher3.lineage.LineageLauncher,handleWindowVisibility]
11-29 03:58:57.084 25180 25180 I am_on_start_called: [0,com.android.launcher3.lineage.LineageLauncher,handleWindowVisibility]

手在滑动过程中这里只看到有桌面Activity onStart,没有onResume,联系人应用也并没有onPasue

11-29 03:59:15.709  2522  6076 I am_focused_stack: [0,0,0,31,RecentsAnimation.onAnimationFinished()]
11-29 03:59:15.712 31776 31776 I am_on_top_resumed_lost_called: [0,com.android.contacts.activities.PeopleActivity,topStateChangedWhenResumed]
11-29 03:59:15.714  2522  6076 I am_pause_activity: [0,106262359,com.android.contacts/.activities.PeopleActivity,userLeaving=false]
11-29 03:59:15.726  2522  6076 I am_set_resumed_activity: [0,com.android.launcher3/.lineage.LineageLauncher,resumeTopActivityInnerLocked]
11-29 03:59:15.730  2522  6076 I am_add_to_stopping: [0,106262359,com.android.contacts/.activities.PeopleActivity,makeInvisible]
11-29 03:59:15.734  2522  6076 I am_resume_activity: [0,239824215,471,com.android.launcher3/.lineage.LineageLauncher]
11-29 03:59:15.743 25180 25180 I am_on_resume_called: [0,com.android.launcher3.lineage.LineageLauncher,RESUME_ACTIVITY]
11-29 03:59:15.771 31776 31776 I am_on_paused_called: [0,com.android.contacts.activities.PeopleActivity,performPause]

这里就滑到多任务界面后松手,这里可以看出这个时候Launcher才真正onResume,联系人应用onPasue
2、从第一部分的分析场景看出,这个时候居然是联系人界面显示着还是Resume同时,桌面Activity的RecentView也同时显示,这种场景其实对于大部分同学来说都是没有见过的。因为平时都是要么显示是Launcher的Activity,要么是联系人Activity的界面,这个一下显示两个Activity界面情况还真的。。没见过
下面来想想,该怎么如果我们要处理这种两个Activity同时显示情况,适合由谁来主导这种协调的显示过程呢?
哈哈,这其实很好想到设计者一定会放到Launcher来负责协调两个Activity的同时显示界面,其实这也就是桌面多了一个文件夹quickstep原因,这部分代码还依赖Systemui部分。

重点介绍OtherActivityInputConsumer情况下的多任务卡片的手势运行步骤:
路径:quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java

  private void initInputMonitor() 
       //省略部分

        try 
            mInputMonitorCompat = InputMonitorCompat.fromBundle(mISystemUiProxy
                    .monitorGestureInput("swipe-up", mDefaultDisplayId), KEY_EXTRA_INPUT_MONITOR);
            mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
                    mMainChoreographer, this::onInputEvent);//这里调用InputMonitor注册了全局触摸事件监听,有触摸事件来了后会触发onInputEvent方法
             //省略部分
    
    //接受全局触摸的onInputEvent
  private void onInputEvent(InputEvent ev) 
       //省略部分
        if (event.getAction() == ACTION_DOWN) 
            mLogId = TOUCH_INTERACTION_LOG.generateAndSetLogId();
            sSwipeSharedState.setLogTraceId(mLogId);

            if (mSwipeTouchRegion.contains(event.getX(), event.getY())) 
                boolean useSharedState = mConsumer.useSharedSwipeState();
                mConsumer.onConsumerAboutToBeSwitched();
                mConsumer = newConsumer(useSharedState, event);//这里在其他Activity滑动时候mConsumer其实就是我们今天主角OtherActivityInputConsumer
                TOUCH_INTERACTION_LOG.addLog("setInputConsumer", mConsumer.getType());
                mUncheckedConsumer = mConsumer;
             else if (mIsUserUnlocked && mMode == Mode.NO_BUTTON
                    && canTriggerAssistantAction(event)) 
             //省略部分
            
        

        TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked());
        mUncheckedConsumer.onMotionEvent(event);//这里会调用对应Consumer的onMotionEvent方法
    

好的上面已介绍出了OtherActivityInputConsumer的onMotionEvent,因为本身整个手势动作都是触摸引发,所以接下来触摸相关,这里因为细节代码实在太多,很多地方只能省略,梳理出大概过程,大家知道个轮廓,然后顺着轮廓去自己详细分析。
//quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java

 @Override
    public void onMotionEvent(MotionEvent ev) 
       //省略部分
        switch (ev.getActionMasked()) 
            case ACTION_DOWN: 
               //省略部分
                if (!mIsDeferredDownTarget) 
                    startTouchTrackingForWindowAnimation(ev.getEventTime(), false);
                
                 //省略部分
                break;
            
            //省略部分
            case ACTION_MOVE: 
                //省略部分
                if (mInteractionHandler != null) 
                    if (mPassedWindowMoveSlop) 
                        // 根据得出的触摸滑动距离,调用对应的updateDisplacement方法来更新滑动界面RecentView和联系人窗口位置
                        mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
                    

                    if (mMode == Mode.NO_BUTTON) 
                        mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
                                || isLikelyToStartNewTask);
                        mMotionPauseDetector.addPosition(displacement, ev.getEventTime());
                        mInteractionHandler.setIsLikelyToStartNewTask(isLikelyToStartNewTask);
                    
                
                break;
            
            case ACTION_CANCEL:
            case ACTION_UP: 
                finishTouchTracking(ev);
                break;
            
        
    


这里主要两个方法:
1、startTouchTrackingForWindowAnimation方法
//quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java

 private void startTouchTrackingForWindowAnimation(
            long touchTimeMs, boolean isLikelyToStartNewTask) 
      //省略部分
        if (listenerSet != null) 
            listenerSet.addListener(handler);
            mSwipeSharedState.applyActiveRecentsAnimationState(handler);
            notifyGestureStarted();
         else 
         //构造出RecentsAnimationListenerSet对象
            RecentsAnimationListenerSet newListenerSet =
                    mSwipeSharedState.newRecentsAnimationListenerSet();
            newListenerSet.addListener(handler);
            Intent intent = handler.getLaunchIntent();
            intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mLogId);
             //通过startRecentsActivityAsync来调用把newListenerSet传递到startRecentsActivityAsync
            startRecentsActivityAsync(intent, newListenerSet);
        
    

这里主要是构造RecentsAnimationListenerSet对象然后传递到startRecentsActivityAsync

quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java

 public static void startRecentsActivityAsync(Intent intent, RecentsAnimationListener listener) 
     //这里调用是ActivityManagerWrapper的startRecentsActivity方法
        UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
                .startRecentsActivity(intent, null, listener, null, null));
    

//frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java

   /**
     * Starts the recents activity. The caller should manage the thread on which this is called.
     */
    public void startRecentsActivity(Intent intent, final AssistDataReceiver assistDataReceiver,
            final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback,
            Handler resultCallbackHandler) 
        try 
       //省略部分

            IRecentsAnimationRunner runner = null;
            if (animationHandler != null) 
             //这里构造出了一个binder的服务端,他是准备让systemserver调用的
                runner = new IRecentsAnimationRunner.Stub() 
                    @Override
                    public void onAnimationStart(IRecentsAnimationController controller,
                            RemoteAnimationTarget[] apps, Rect homeContentInsets,
                            Rect minimizedHomeBounds) 
                        final RecentsAnimationControllerCompat controllerCompat =
                                new RecentsAnimationControllerCompat(controller);
                        final RemoteAnimationTargetCompat[] appsCompat =
                                RemoteAnimationTargetCompat.wrap(apps);
                                //这里会调用到上一个步骤的RecentsAnimationListenerSet
                        animationHandler.onAnimationStart(controllerCompat, appsCompat,
                                homeContentInsets, minimizedHomeBounds);
                    

                    @Override
                    public void onAnimationCanceled(boolean deferredWithScreenshot) 
                        animationHandler.onAnimationCanceled(
                                deferredWithScreenshot ? new ThumbnailData() : null);
                    
                ;
            
            //这里跨进程调用到了ActivityTaskManagerService到了systemserver端
            ActivityTaskManager.getService().startRecentsActivity(intent, receiver, runner);
          //省略部分

这时候代码就运行到了systemserver的 ActivityTaskManagerService:
base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

@Override
public void startRecentsActivity(Intent intent, @Deprecated IAssistDataReceiver unused,
        @Nullable IRecentsAnimationRunner recentsAnimationRunner) 
//省略部分
    try 
        synchronized (mGlobalLock) 
            final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
            final int recentsUid = mRecentTasks.getRecentsComponentUid();
            final WindowProcessController caller = getProcessController(callingPid, callingUid);

            // Start a new recents animation
            //构造出对应的RecentsAnimation动画
            final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
                    getActivityStartController(), mWindowManager, intent, recentsComponent,
                    recentsUid, caller);
            if (recentsAnimationRunner == null) 
                anim.preloadRecentsActivity();
             else 
            //调用RecentsAnimation的startRecentsActivity方法
                anim.startRecentsActivity(recentsAnimationRunner);
            
        
     finally 
        Binder.restoreCallingIdentity(origId);
    

这里调用到了RecentsAnimation的startRecentsActivity
base/services/core/java/com/android/server/wm/RecentsAnimation.java

void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner) 
       //省略部分
        ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
                mTargetActivityType);//获取目标栈,这里是Home,就是Launcher
        ActivityRecord targetActivity = getTargetActivity(targetStack);
        final boolean hasExistingActivity = targetActivity != null;
        if (hasExistingActivity) 
            final ActivityDisplay display = targetActivity.getDisplay();
            mRestoreTargetBehindStack = display.getStackAbove(targetStack);
           //省略部分
        
  		//省略部分
        try 
            if (hasExistingActivity) 
                // Move the recents activity into place for the animation if it is not top most
            	//把桌面Launcher的栈stack移到联系人这个当前显示栈的后一位,紧跟着
                mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack);
             //省略部分
             else 
          //省略部分
            

          //省略部分
            mWindowManager.cancelRecentsAnimationSynchronously(REORDER_MOVE_TO_ORIGINAL_POSITION,
                    "startRecentsActivity");
                    //初始化多任务动画相关
            mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
                    this, mDefaultDisplay.mDisplayId,
                    mStackSupervisor.mRecentTasks.getRecentTaskIds());
//这里需要调用保证Launcher是处于Visible的状态,因为之前一直在后台当然是非visible状态
            mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);

            //省略部分
         catch (Exception e) 
            Slog.e(TAG, "Failed to start recents activity", e);
            throw e;
         finally 
            mWindowManager.continueSurfaceLayout();
            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
        
    

这里主要介绍一下initializeRecentsAnimation这个关键方法:
base/services/core/java/com/android/server/wm/WindowManagerService.java

    public void initializeRecentsAnimation(int targetActivityType,
            IRecentsAnimationRunner recentsAnimationRunner,
            RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId,
            SparseBooleanArray recentTaskIds) 
        synchronized (mGlobalLock) 
        //这里构造出RecentsAnimationController对象
            mRecentsAnimationController = new RecentsAnimationController(this,
                    recentsAnimationRunner, callbacks, displayId);
            mRoot.getDisplayContent(displayId).mAppTransition.updateBooster();
            //调用了RecentsAnimationController的initialize方法
            mRecentsAnimationController.initialize(targetActivityType, recentTaskIds);
        
    

这里的调用了RecentsAnimationController的initialize
base/services/core/java/com/android/server/wm/RecentsAnimationController.java

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

        // Make leashes for each of the visible/target tasks and add it to the recents animation to
        // be started
        //获取当前显示的Task
        final ArrayList<Task> visibleTasks = mDisplayContent.getVisibleTasks();
                //获取目标Stack
        final TaskStack targetStack = mDisplayContent.getStack(WINDOWING_MODE_UNDEFINED,
                targetActivityType);
        if (targetStack != null) 
            for (int i = targetStack.getChildCount() - 1; i >= 0; i--) 
                final Task t = targetStack.getChildAt(i);
                if (!visibleTasks.contains(t)) 
                    visibleTasks.add(t);//把对应Stack的task加入到visibleTasks
                
            
        
        final int taskCount = visibleTasks.size();
        for (int i = 0; i < taskCount; i++) 
            final Task task = visibleTasks.get(i);
            final WindowConfiguration config = task.getWindowConfiguration();
            if (config.tasksAreFloating()
                    || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) 
                continue;
            
            //把对应visibleTasks一个个加入Animation中
            addAnimation(task, !recentTaskIds.get(task.mTaskId));
        

        //省略部分
		//调用
        mService.mWindowPlacerLocked.performSurfacePlacement();

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

这里主要来看看addAnimation和performSurfacePlacement:
base/services/core/java/com/android/server/wm/RecentsAnimationController.java

@VisibleForTesting
AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) 
    if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "addAnimation(" + task.getName() + ")");
    //基于Task构造出对应的TaskAnimationAdapter
    final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task,
            isRecentTaskInvisible);
    //这里直接调用了task的startAnimation
    task.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */);
    task.commitPendingTransaction();
    //taskAdapter添加到了mPendingAnimations
    mPendingAnimations.add(taskAdapter);
    return taskAdapter;

这里主要就是TaskAnimationAdapter构造以后,task调用了startAnimation
base/services/core/java/com/android/server/wm/WindowContainer.java

    void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) 
        if (DEBUG_ANIM) Slog.v(TAG, "Starting animation on " + this + ": " + anim);

        // TODO: This should use isVisible() but because isVisible has a really weird meaning at
        // the moment this doesn't work for all animatable window containers.
        mSurfaceAnimator.startAnimation(t, anim, hidden);
    


然后调用到了SurfaceAnimator的startAnimation,这里才是关键:
base/services/core/java/com/android/server/wm/SurfaceAnimator.java

void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) 
    cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
    mAnimation = anim;
    final SurfaceControl surface = mAnimatable.getSurfaceControl();
    if (surface == null) 
        Slog.w(TAG, "Unable to start animation, surface is null or no children."以上是关于android P/Q/R/S 9/10/11/12多任务手势动画OtherActivityInputConsumer情况-第一节的主要内容,如果未能解决你的问题,请参考以下文章

android P/Q/R/S 9/10/11/12多任务手势动画OtherActivityInputConsumer情况-第一节

android P/Q/R/S 9/10/11/12多任务手势动画OtherActivityInputConsumer情况-第一节

UVA10375选择与除法

UVa 10375 Choose and divide (唯一分解定理)

UVa 10375 - Choose and divide(唯一分解定理)

UVA-10375 数学