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

92 篇文章 64 订阅
92 篇文章 72 订阅

hi,在手机屏幕屏占比越来越大的时候,用户对手机的屏幕交互体验也在提升,把原来的导航键3个按钮的交互方式,在android P产生了一个巨大的变化,增加了全面屏上导航手势的方式,后面版本也是在P的基础进行一些细微修改。
入门课,实战课,跨进程专题
ps需要学习深入framework课程和课程优惠
(可以加我qq:2102309716 优惠购买)
导航手势给人的用户体验也确实很不错,但是他的整套代码实现也是相当复杂的,今天我们对导航手势的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.");
        cancelAnimation();
        return;
    }
    //根据AnimationAdapter创建出了对应的mLeash牵引
    mLeash = createAnimationLeash(surface, t,
            mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden);
    mAnimatable.onAnimationLeashCreated(t, mLeash);
    if (mAnimationStartDelayed) {
        if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
        return;
    }
    mAnimation.startAnimation(mLeash, t, mInnerAnimationFinishedCallback);
}

创建出来了mLeash动画Surface,然后把mLeash设置回了AnimationAdapter

创建牵引过程:

  private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width,
            int height, boolean hidden) {
        if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
        final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
                .setParent(mAnimatable.getAnimationLeashParent())
                .setName(surface + " - animation-leash");
        final SurfaceControl leash = builder.build();
        t.setWindowCrop(leash, width, height);
        t.show(leash);
        // TODO: change this back to use show instead of alpha when b/138459974 is fixed.
        t.setAlpha(leash, hidden ? 0 : 1);
        t.reparent(surface, leash);
        return leash;
    }

那么接下来分析 mService.mWindowPlacerLocked.performSurfacePlacement()方法:
他最后一路调用会调用到base/services/core/java/com/android/server/wm/RootWindowContainer.java:

 // "Something has changed!  Let's make it correct now."
    // TODO: Super crazy long method that should be broken down...
    void performSurfacePlacementNoTrace(boolean recoveringMemory) {
       //省略非本次重点讨论部分
        // Defer starting the recents animation until the wallpaper has drawn
        final RecentsAnimationController recentsAnimationController =
                mWmService.getRecentsAnimationController();
        if (recentsAnimationController != null) {
            recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
        }
 //省略非本次重点讨论部分
    }
    //这里会调用到RecentsAnimationController的checkAnimationReady:

 void checkAnimationReady(WallpaperController wallpaperController) {
        if (mPendingStart) {
            final boolean wallpaperReady = !isTargetOverWallpaper()
                    || (wallpaperController.getWallpaperTarget() != null
                            && wallpaperController.wallpaperTransitionReady());
            if (wallpaperReady) {
                //调用了RecentsAnimationController的startAnimation
                mService.getRecentsAnimationController().startAnimation();
            }
        }
    }

这里来看RecentsAnimationController的startAnimation:
base/services/core/java/com/android/server/wm/RecentsAnimationController.java

 void startAnimation() {
     //省略非本次重点讨论部分
        try {
            final ArrayList<RemoteAnimationTarget> appAnimations = new ArrayList<>();
            for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
                final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
                //taskAdapter创建出对应的RemoteAnimationTarget
                final RemoteAnimationTarget target = taskAdapter.createRemoteAnimationApp();
                if (target != null) {
                    appAnimations.add(target);
                } else {
                    removeAnimation(taskAdapter);
                }
            }
//省略非本次重点讨论部分
//这里调用了mRunner的onAnimationStart,这个mRunner就是在桌面端的binder服务端对象,这里把appTargets传递给了Launcher端,其实就是mPendingAnimations中的taskAdapter调用createRemoteAnimationApp()创建出来的
            mRunner.onAnimationStart(mController, appTargets, contentInsets, minimizedHomeBounds);
            if (DEBUG_RECENTS_ANIMATIONS) {
                Slog.d(TAG, "startAnimation(): Notify animation start:");
                for (int i = 0; i < mPendingAnimations.size(); i++) {
                    final Task task = mPendingAnimations.get(i).mTask;
                    Slog.d(TAG, "\t" + task.mTaskId);
                }
            }
        } catch (RemoteException e) {
            Slog.e(TAG, "Failed to start recents animation", e);
        }
        final SparseIntArray reasons = new SparseIntArray();
        reasons.put(WINDOWING_MODE_FULLSCREEN, APP_TRANSITION_RECENTS_ANIM);
        mService.mAtmInternal.notifyAppTransitionStarting(reasons, SystemClock.uptimeMillis());
    }
//这里对createRemoteAnimationApp进行解释
     RemoteAnimationTarget createRemoteAnimationApp() {
         //省略部分
         //这里主要mTaskId,mCapturedLeash(Leash的SurfaceControl,这个属于非常重要的,后面Launcher手势动画客户端就是靠它来控制画面缩放移动)
            mTarget = new RemoteAnimationTarget(mTask.mTaskId, mode, mCapturedLeash,
                    !topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
                    insets, mTask.getPrefixOrderIndex(), mPosition, mBounds,
                    mTask.getWindowConfiguration(), mIsRecentTaskInvisible, null, null);
            return mTarget;
        }

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值