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情况-第一节
UVa 10375 Choose and divide (唯一分解定理)