当前位置: 首页 > news >正文

AOSP Android13 Launcher3 最近任务详解

本文档详细介绍了 Launcher3 中最近任务(Recents)的核心架构、手势交互流程、缩略图更新机制以及关键类的设计。

目录

  • 1. 核心类概览
  • 2. 类关系图
  • 3. 手势上滑到最近任务流程
  • 4. 缩略图实时更新机制
  • 5. 关键类详细解析
  • 6. 时序图
  • 7. 关键方法说明

1. 核心类概览

1.1 AbsSwipeUpHandler

文件路径: quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java

核心职责:

  • 处理从应用底部上滑手势的全生命周期
  • 协调手势动画、状态机转换和目标计算
  • 管理 Recents 动画控制器和远程动画目标
  • 控制窗口动画从当前应用过渡到 Launcher

关键字段:

protected RecentsAnimationController mRecentsAnimationController;
protected RecentsAnimationTargets mRecentsAnimationTargets;
protected T mActivity;  // 泛型,通常是 Launcher 实例
protected Q mRecentsView;  // 泛型,通常是 RecentsView 实例
private MultiStateCallback mStateCallback;  // 状态机回调
private AnimatorControllerWithResistance mLauncherTransitionController;

主要状态标志:

  • STATE_LAUNCHER_PRESENT: Launcher 已准备好
  • STATE_GESTURE_STARTED: 手势开始
  • STATE_GESTURE_COMPLETED: 手势完成
  • STATE_LAUNCHER_DRAWN: Launcher 已绘制
  • STATE_APP_CONTROLLER_RECEIVED: 收到动画控制器
  • STATE_SCALED_CONTROLLER_RECENTS: 缩放到 Recents 状态

1.2 RecentsView

文件路径: quickstep/src/com/android/quickstep/views/RecentsView.java

核心职责:

  • 管理所有最近任务的 TaskView
  • 处理任务的布局、滚动和动画
  • 支持网格布局和轮播布局
  • 处理任务的加载、卸载和可见性管理

关键属性:

protected RemoteTargetHandle[] mRemoteTargetHandles;  // 远程动画目标句柄
protected RecentsAnimationController mRecentsAnimationController;
private final ViewPool<TaskView> mTaskViewPool;  // TaskView 对象池
private int mRunningTaskViewId = -1;  // 正在运行的任务 ID
private int mFocusedTaskViewId = -1;  // 聚焦的任务 ID
private float mContentAlpha = 1;  // 内容透明度
private float mFullscreenProgress = 0;  // 全屏进度
private float mTaskModalness = 0;  // 任务模态化程度

重要方法:

  • applyLoadPlan(): 应用任务加载计划,创建 TaskView
  • onRecentsAnimationStart(): Recents 动画开始时的回调
  • updateThumbnail(): 更新任务缩略图
  • onTaskThumbnailChanged(): 缩略图变化的回调

1.3 TaskView

文件路径: quickstep/src/com/android/quickstep/views/TaskView.java

核心职责:

  • 表示单个最近任务的视图
  • 包含缩略图视图 (TaskThumbnailView) 和图标视图 (TaskViewIcon)
  • 处理任务的点击、长按交互
  • 管理任务的视觉变换(缩放、平移、透明度等)

关键组件:

protected Task mTask;  // 关联的任务数据
protected TaskThumbnailView mSnapshotView;  // 缩略图视图
protected TaskViewIcon mIconView;  // 图标视图
protected final FullscreenDrawParams mCurrentFullscreenParams;  // 全屏绘制参数
private CancellableTask mThumbnailLoadRequest;  // 缩略图加载请求
private CancellableTask mIconLoadRequest;  // 图标加载请求

生命周期方法:

  • bind(): 绑定任务数据
  • onTaskListVisibilityChanged(): 任务可见性变化时更新缩略图和图标
  • launchTaskAnimated(): 启动任务并播放动画

1.4 TaskViewSimulator

文件路径: quickstep/src/com/android/quickstep/util/TaskViewSimulator.java

核心职责:

  • 模拟 TaskView 和 RecentsView 的布局行为
  • 在手势过程中实时计算任务窗口的变换矩阵
  • 不需要实际的 TaskView 实例就能计算动画参数

核心动画属性:

public final AnimatedFloat taskPrimaryTranslation;  // 主轴平移
public final AnimatedFloat taskSecondaryTranslation;  // 次轴平移
public final AnimatedFloat carouselScale;  // 轮播缩放
public final AnimatedFloat recentsViewScale;  // RecentsView 缩放
public final AnimatedFloat fullScreenProgress;  // 全屏进度
public final AnimatedFloat recentsViewScroll;  // RecentsView 滚动

关键方法:

  • apply(): 应用当前动画参数到 TransformParams
  • getCurrentMatrix(): 获取当前变换矩阵
  • getCurrentCropRect(): 获取当前裁剪区域

1.5 RemoteAnimationTargets

文件路径: quickstep/src/com/android/quickstep/RemoteAnimationTargets.java

核心职责:

  • 封装系统提供的远程动画目标
  • 按类型过滤 (apps, wallpapers, nonApps)
  • 管理动画目标的生命周期和释放

关键字段:

public final RemoteAnimationTarget[] apps;  // 应用窗口目标
public final RemoteAnimationTarget[] wallpapers;  // 壁纸目标
public final RemoteAnimationTarget[] nonApps;  // 非应用窗口(如导航栏)
public final int targetMode;  // 目标模式(打开/关闭)

1.6 RecentsAnimationController

文件路径: quickstep/src/com/android/quickstep/RecentsAnimationController.java

核心职责:

  • 包装系统的 RecentsAnimationControllerCompat
  • 提供线程安全的动画控制接口
  • 管理动画的完成和清理

关键方法:

public void finishController(boolean toRecents, ...)  // 完成动画
public ThumbnailData screenshotTask(int taskId)  // 截取任务屏幕截图
public void setUseLauncherSystemBarFlags(boolean use)  // 设置系统栏标志
public void enableInputConsumer()  // 启用输入消费者

1.7 RecentsAnimationCallbacks

文件路径: quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java

核心职责:

  • 实现系统 RecentsAnimationListener 接口
  • 将系统回调分发给多个监听器
  • 确保回调在主线程执行

回调方法:

onAnimationStart()  // 动画开始
onAnimationCanceled()  // 动画取消
onTasksAppeared()  // 任务出现
onSwitchToScreenshot()  // 切换到截图

2. 类关系图

manages
controls
uses
creates
contains multiple
interacts
contains
binds to
creates
wraps
AbsSwipeUpHandler
-RecentsAnimationController mRecentsAnimationController
-RecentsAnimationTargets mRecentsAnimationTargets
-RecentsView mRecentsView
-MultiStateCallback mStateCallback
+onGestureStarted()
+onGestureEnded()
+onRecentsAnimationStart()
+animateToProgress()
RecentsView
-RemoteTargetHandle[] mRemoteTargetHandles
-RecentsAnimationController mRecentsAnimationController
-ViewPool<TaskView> mTaskViewPool
-int mRunningTaskViewId
+applyLoadPlan()
+updateThumbnail()
+onRecentsAnimationStart()
+onTaskThumbnailChanged()
TaskView
-Task mTask
-TaskThumbnailView mSnapshotView
-TaskViewIcon mIconView
-CancellableTask mThumbnailLoadRequest
+bind()
+onTaskListVisibilityChanged()
+launchTaskAnimated()
TaskViewSimulator
+AnimatedFloat taskPrimaryTranslation
+AnimatedFloat recentsViewScale
+AnimatedFloat fullScreenProgress
+apply()
+getCurrentMatrix()
RemoteAnimationTargets
+RemoteAnimationTarget[] apps
+RemoteAnimationTarget[] wallpapers
+RemoteAnimationTarget[] nonApps
+findTask()
+release()
RecentsAnimationController
-RecentsAnimationControllerCompat mController
+finish()
+screenshotTask()
+setUseLauncherSystemBarFlags()
RecentsAnimationCallbacks
-Set<RecentsAnimationListener> mListeners
+addListener()
+onAnimationStart()
+onAnimationCanceled()
RecentsAnimationTargets
TaskThumbnailView
Task

核心类之间的关系说明:

  1. AbsSwipeUpHandler 是手势处理的核心协调者

    • 创建和管理 TaskViewSimulator
    • 控制 RecentsAnimationController
    • 使用 RemoteAnimationTargets 进行窗口动画
    • 管理 RecentsView 的可见性和状态
  2. RecentsView 是任务容器

    • 包含多个 TaskView
    • 与 RecentsAnimationController 交互
    • 使用对象池管理 TaskView 实例
  3. TaskView 是单个任务的表现

    • 绑定 Task 数据模型
    • 包含 TaskThumbnailView 和 TaskViewIcon
    • 处理缩略图和图标的加载
  4. 动画系统

    • RecentsAnimationCallbacks 接收系统回调
    • 创建 RecentsAnimationController
    • 包装 RemoteAnimationTargets

3. 手势上滑到最近任务流程

3.1 流程概览

快速上滑
慢速上滑
横向滑动
用户从底部上滑
TouchInteractionService 接收触摸事件
创建 AbsSwipeUpHandler
初始化状态机
onGestureStarted 被调用
请求 Recents 动画
系统回调 onRecentsAnimationStart
初始化 RemoteAnimationTargets
创建 TaskViewSimulator
手势过程中持续更新位置
手势结束判断
目标: HOME/RECENTS
目标: LAST_TASK/RECENTS
目标: NEW_TASK
执行目标动画
finishRecentsAnimation
更新缩略图
完成

3.2 详细步骤

阶段 1: 手势初始化 (0-100ms)

关键文件: AbsSwipeUpHandler.java:391-471

  1. AbsSwipeUpHandler 构造

    public AbsSwipeUpHandler(Context context, ...) {mActivityInterface = gestureState.getActivityInterface();mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);initStateCallbacks();  // 初始化状态机
    }
    
  2. 状态机初始化 (AbsSwipeUpHandler.java:413-471)

    private void initStateCallbacks() {mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,this::onLauncherPresentAndGestureStarted);mStateCallback.runOnceAtState(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED,this::initializeLauncherAnimationController);// ... 更多状态回调
    }
    
  3. 手势开始 (AbsSwipeUpHandler.java:1019-1054)

    public void onGestureStarted(boolean isLikelyToStartNewTask) {mActivityInterface.closeOverlay();notifyGestureStarted();setIsLikelyToStartNewTask(isLikelyToStartNewTask, false);mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);mGestureStarted = true;
    }
    
阶段 2: Recents 动画启动 (100-200ms)

关键文件: AbsSwipeUpHandler.java:946-997

  1. 接收动画回调

    @Override
    public void onRecentsAnimationStart(RecentsAnimationController controller,RecentsAnimationTargets targets) {// 分配动画目标mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(targets);mRecentsAnimationController = controller;mRecentsAnimationTargets = targets;// 初始化设备配置DeviceProfile dp = orientationState.getLauncherDeviceProfile();initTransitionEndpoints(dp);// 通知状态机mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
    }
    
  2. Launcher 准备 (AbsSwipeUpHandler.java:555-622)

    private void onLauncherStart() {mRecentsView.updateRecentsRotation();mAnimationFactory = mActivityInterface.prepareRecentsUI(mDeviceState, mWasLauncherAlreadyVisible,this::onAnimatorPlaybackControllerCreated);mStateCallback.setState(STATE_LAUNCHER_DRAWN);
    }
    
  3. RecentsView 初始化 (RecentsView.java:664-685)

    protected void notifyGestureAnimationStartToRecents() {Task[] runningTasks = mGestureState.getRunningTask().getPlaceholderTasks();if (mRecentsView != null) {mRecentsView.onGestureAnimationStart(runningTasks, rotationHelper);}
    }
    
阶段 3: 手势过程中的实时更新

关键文件: AbsSwipeUpHandler.java:892-913

  1. 当前偏移更新

    @UiThread
    @Override
    public void onCurrentShiftUpdated() {// 更新全屏进度float threshold = LauncherPrefs.get(mContext).get(ALL_APPS_OVERVIEW_THRESHOLD) / 100f;setIsInAllAppsRegion(mCurrentShift.value >= threshold);// 更新系统 UI 标志updateSysUiFlags(mCurrentShift.value);// 应用滚动和变换applyScrollAndTransform();// 更新 Launcher 过渡进度updateLauncherTransitionProgress();
    }
    
  2. 窗口变换应用 (SwipeUpAnimationLogic.java)

    protected void applyScrollAndTransform() {// 计算当前窗口位置for (RemoteTargetHandle handle : mRemoteTargetHandles) {TaskViewSimulator simulator = handle.getTaskViewSimulator();simulator.apply(handle.getTransformParams());}
    }
    
  3. TaskViewSimulator 计算 (TaskViewSimulator.java:421-519)

    public void apply(TransformParams params) {// 应用缩略图矩阵mMatrix.set(mPositionHelper.getMatrix());// 应用 TaskView 变换mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE, taskPrimaryTranslation.value);// 应用轮播缩放mMatrix.postScale(carouselScale.value, carouselScale.value, mPivot.x, mPivot.y);// 应用 RecentsView 缩放mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y);params.applySurfaceParams(params.createSurfaceParams(this));
    }
    
阶段 4: 手势结束和目标计算

关键文件: AbsSwipeUpHandler.java:1103-1123

  1. 手势结束

    public void onGestureEnded(float endVelocityPxPerMs, PointF velocityPxPerMs) {float flingThreshold = mContext.getResources().getDimension(R.dimen.quickstep_fling_threshold_speed);boolean isFling = mGestureStarted && !mIsMotionPaused&& Math.abs(endVelocityPxPerMs) > flingThreshold;mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);handleNormalGestureEnd(endVelocityPxPerMs, isFling, velocityPxPerMs, false);
    }
    
  2. 计算结束目标 (AbsSwipeUpHandler.java:1224-1264)

    private GestureEndTarget calculateEndTarget(PointF velocityPxPerMs, float endVelocityPxPerMs, boolean isFlingY, boolean isCancel) {if (isCancel) {return LAST_TASK;} else if (isFlingY) {return calculateEndTargetForFlingY(velocityPxPerMs, endVelocityPxPerMs);} else {return calculateEndTargetForNonFling(velocityPxPerMs);}
    }
    
  3. 目标类型:

    • HOME: 回到桌面
    • RECENTS: 进入最近任务界面
    • LAST_TASK: 返回原任务
    • NEW_TASK: 切换到新任务
    • ALL_APPS: 打开应用抽屉
阶段 5: 目标动画执行

关键文件: AbsSwipeUpHandler.java:1469-1677

  1. 动画到目标进度

    private void animateToProgressInternal(float start, float end, long duration,Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {if (target == HOME) {// 创建回到 Home 的动画HomeAnimationFactory homeAnimFactory = createHomeAnimationFactory(...);RectFSpringAnim[] windowAnim = createWindowAnimationToHome(start, homeAnimFactory);windowAnim[0].start(mContext, dp, velocityPxPerMs);} else {// 创建到 Recents 或其他目标的动画ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end);windowAnim.setDuration(duration).setInterpolator(interpolator);windowAnim.start();}
    }
    
  2. RecentsView 准备 (RecentsView.java)

    public void onPrepareGestureEndAnimation(AnimatorSet animatorSet,GestureEndTarget endTarget, TaskViewSimulator[] simulators) {// 根据目标类型准备动画if (endTarget == RECENTS) {// 快照到最近页面snapToPage(getDestinationPage(), duration);}
    }
    
阶段 6: 动画完成和清理

关键文件: AbsSwipeUpHandler.java:1148-1204

  1. 目标到达处理

    private void onSettledOnEndTarget() {final GestureEndTarget endTarget = mGestureState.getEndTarget();switch (endTarget) {case HOME:mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);SystemUiProxy.INSTANCE.get(mContext).setPipAnimationTypeToAlpha();break;case RECENTS:mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT);break;case NEW_TASK:mStateCallback.setState(STATE_START_NEW_TASK | STATE_CAPTURE_SCREENSHOT);break;case LAST_TASK:mStateCallback.setState(STATE_RESUME_LAST_TASK);break;}
    }
    
  2. 完成 Recents 动画

    private void finishCurrentTransitionToRecents() {if (mRecentsAnimationController != null) {mRecentsAnimationController.finish(true /* toRecents */, () -> {// 动画完成回调});}
    }
    

4. 缩略图实时更新机制

4.1 缩略图更新流程

Android系统TaskStackListenerRecentsModelThumbnailCacheRecentsViewTaskViewTaskThumbnailViewonTaskSnapshotChanged(taskId)通知缩略图变化更新缓存onTaskThumbnailChanged(taskId, thumbnailData)查找 TaskView by taskIdsetThumbnail(task, thumbnailData)更新位图并刷新显示Android系统TaskStackListenerRecentsModelThumbnailCacheRecentsViewTaskViewTaskThumbnailView

4.2 缩略图加载时机

关键文件: TaskView.java:1045-1098

可见时加载
public void onTaskListVisibilityChanged(boolean visible, @TaskDataChanges int changes) {if (mTask == null) {return;}cancelPendingLoadTasks();if (visible) {RecentsModel model = RecentsModel.INSTANCE.get(getContext());TaskThumbnailCache thumbnailCache = model.getThumbnailCache();if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {// 在后台线程加载高分辨率缩略图mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(mTask, thumbnail -> {// 回到主线程更新 UImSnapshotView.setThumbnail(mTask, thumbnail);});}} else {// 不可见时清除缩略图以节省内存if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {mSnapshotView.setThumbnail(null, null);mTask.thumbnail = null;}}
}

4.3 缩略图缓存机制

分层缓存策略:

  1. 内存缓存 (TaskThumbnailCache)

    • 缓存最近使用的缩略图
    • LRU 策略自动淘汰旧缩略图
  2. 系统缓存

    • 通过 ActivityManagerWrapper 获取
    • 系统维护的任务快照
  3. 实时截图

    • 在 Recents 动画期间使用 screenshotTask()
    • 用于获取最新的任务画面

关键代码: RecentsAnimationController.java:76-78

public ThumbnailData screenshotTask(int taskId) {return mController.screenshotTask(taskId);
}

4.4 缩略图更新路径

路径 1: 系统回调更新

触发时机: 任务快照在系统中更新时

流程:

  1. TaskStackChangeListener.onTaskSnapshotChanged() 被调用
  2. RecentsModel 通知所有注册的监听器
  3. RecentsView.onTaskThumbnailChanged() 查找对应的 TaskView
  4. TaskView 更新其 TaskThumbnailView

关键代码: RecentsView.java:989-1004

@Override
public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {if (mHandleTaskStackChanges) {TaskView taskView = getTaskViewByTaskId(taskId);if (taskView != null) {for (TaskIdAttributeContainer container :taskView.getTaskIdAttributeContainers()) {if (container != null && taskId == container.getTask().key.id) {container.getThumbnailView().setThumbnail(container.getTask(), thumbnailData);}}}}return null;
}
路径 2: 手势动画期间更新

触发时机: 在 Recents 动画控制器激活时

流程:

  1. AbsSwipeUpHandler 持有 RecentsAnimationController
  2. 需要时调用 screenshotTask(taskId) 获取实时截图
  3. 将截图数据缓存到 mTaskSnapshotCache
  4. 动画结束后更新到 RecentsView

关键代码: AbsSwipeUpHandler.java:1569-1577

UI_HELPER_EXECUTOR.execute(() -> {if (mRecentsAnimationController == null) {return;}final int taskId = mGestureState.getTopRunningTaskId();mTaskSnapshotCache.put(taskId,mRecentsAnimationController.screenshotTask(taskId));
});
路径 3: 从缓存异步加载

触发时机: TaskView 变为可见时

流程:

  1. TaskView.onTaskListVisibilityChanged(true) 被调用
  2. 创建后台任务从 TaskThumbnailCache 加载
  3. 加载完成后在主线程更新 UI

优势:

  • 避免阻塞主线程
  • 支持高分辨率缩略图的渐进式加载
  • 缓存可在多个地方复用

4.5 缩略图切换到截图

场景: 当 Recents 动画被取消时,需要从实时窗口切换到静态截图

关键方法: RecentsView.switchToScreenshot()

调用时机:

mGestureState.runOnceAtState(STATE_RECENTS_ANIMATION_CANCELED, () -> {if (mRecentsView == null) return;HashMap<Integer, ThumbnailData> snapshots =mGestureState.consumeRecentsAnimationCanceledSnapshot();if (snapshots != null) {mRecentsView.switchToScreenshot(snapshots, () -> {if (mRecentsAnimationController != null) {mRecentsAnimationController.cleanupScreenshot();}});}
});

切换逻辑:

  1. 使用最后截取的快照
  2. 将实时窗口替换为静态图片
  3. 清理动画资源

5. 关键类详细解析

5.1 AbsSwipeUpHandler 状态机

状态标志位 (Flags)
状态说明设置时机
STATE_LAUNCHER_PRESENTLauncher 已初始化onActivityInit()
STATE_LAUNCHER_STARTEDLauncher 已启动onLauncherStart()
STATE_LAUNCHER_DRAWNLauncher 已绘制完成Launcher 首帧绘制后
STATE_LAUNCHER_BIND_TO_SERVICE已绑定到触摸服务onLauncherBindToService()
STATE_GESTURE_STARTED手势已开始onGestureStarted()
STATE_GESTURE_COMPLETED手势已完成onGestureEnded()
STATE_APP_CONTROLLER_RECEIVED收到动画控制器onRecentsAnimationStart()
STATE_CAPTURE_SCREENSHOT需要截取屏幕目标确定后
STATE_SCREENSHOT_CAPTURED截图已完成截图操作后
STATE_SCALED_CONTROLLER_HOME缩放到 Home动画到 Home
STATE_SCALED_CONTROLLER_RECENTS缩放到 Recents动画到 Recents
状态组合与回调
// 示例:Launcher 准备好且手势开始
mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,this::onLauncherPresentAndGestureStarted);// 示例:完成到 Recents 的过渡
mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED | STATE_SCALED_CONTROLLER_RECENTS,this::finishCurrentTransitionToRecents);

5.2 TaskViewSimulator 动画参数

核心动画属性详解
属性类型用途取值范围
fullScreenProgressAnimatedFloat全屏进度0=应用全屏, 1=Overview
recentsViewScaleAnimatedFloatRecentsView 整体缩放通常 < 1
taskPrimaryTranslationAnimatedFloat主轴(通常是 X)平移像素值
taskSecondaryTranslationAnimatedFloat次轴(通常是 Y)平移像素值
carouselScaleAnimatedFloat轮播模式缩放通常接近 1
recentsViewScrollAnimatedFloatRecentsView 滚动偏移像素值
变换矩阵计算

关键代码: TaskViewSimulator.java:462-489

public void apply(TransformParams params) {// 1. 基础缩略图变换mMatrix.set(mPositionHelper.getMatrix());// 2. TaskView 位置和平移mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE, taskPrimaryTranslation.value);mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE, taskSecondaryTranslation.value);// 3. 轮播缩放mMatrix.postScale(carouselScale.value, carouselScale.value, mPivot.x, mPivot.y);mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE, carouselPrimaryTranslation.value);// 4. RecentsView 滚动mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE, recentsViewScroll.value);// 5. RecentsView 整体缩放mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y);// 6. 应用窗口到 Home 的旋转applyWindowToHomeRotation(mMatrix);// 7. 应用到 Surfaceparams.applySurfaceParams(params.createSurfaceParams(this));
}

5.3 RecentsView 任务加载

applyLoadPlan 流程

关键代码: RecentsView.java:1699-1922

步骤:

  1. 清理旧任务

    unloadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
    removeAllViews();
    
  2. 创建 TaskView

    for (int i = taskGroups.size() - 1; i >= 0; i--) {GroupTask groupTask = taskGroups.get(i);TaskView taskView = getTaskViewFromPool(groupTask.taskViewType);addView(taskView);if (taskView instanceof GroupedTaskView) {((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, ...);} else {taskView.bind(groupTask.task1, mOrientationState);}
    }
    
  3. 设置当前页

    if (targetPage != -1 && mCurrentPage != targetPage) {runOnPageScrollsInitialized(() -> {setCurrentPage(targetPage);});
    }
    
  4. 更新任务视觉

    resetTaskVisuals();
    onTaskStackUpdated();
    updateEnabledOverlays();
    
TaskView 对象池

优势:

  • 避免频繁创建/销毁 View
  • 减少 GC 压力
  • 提升滚动性能

实现:

private final ViewPool<TaskView> mTaskViewPool;// 获取 TaskView
TaskView taskView = mTaskViewPool.getView();// 回收 TaskView
mTaskViewPool.recycle(taskView);

6. 时序图

6.1 完整手势流程时序图

用户TouchInteractionServiceAbsSwipeUpHandlerRecentsAnimationControllerRecentsViewTaskView系统从底部上滑创建 HandlerinitStateCallbacks()onGestureStarted()请求 Recents 动画onRecentsAnimationStart(controller, targets)创建 Controller设置 STATE_APP_CONTROLLER_RECEIVEDonGestureAnimationStart(runningTasks)创建占位 TaskViewbind(task)buildAnimationController()创建 TaskViewSimulatorpar[Launcher 初始化][动画准备]手指移动onCurrentShiftUpdated()applyScrollAndTransform()更新窗口变换应用 SurfaceTransactionloop[手势移动中]手指抬起onGestureEnded(velocity)calculateEndTarget()animateToProgress(target)onPrepareGestureEndAnimation()snapToPage()finish(toRecents=true)完成动画更新缩略图createWindowAnimationToHome()finish(toRecents=false)完成动画finish(toRecents=false)恢复任务alt[目标是 RECENTS][目标是 HOME][目标是 LAST_TASK]reset()用户TouchInteractionServiceAbsSwipeUpHandlerRecentsAnimationControllerRecentsViewTaskView系统

6.2 缩略图更新时序图

系统TaskStackListenerRecentsModelThumbnailCacheRecentsViewTaskViewTaskThumbnailView任务快照更新onTaskSnapshotChanged(taskId)通知缩略图变化updateThumbnailInBackground()在后台线程加载解码位图更新 LRU 缓存onTaskThumbnailChanged(taskId, data)getTaskViewByTaskId(taskId)setThumbnail(task, data)updateThumbnailMatrix()invalidate()par[缓存更新][UI 通知]缩略图显示更新系统TaskStackListenerRecentsModelThumbnailCacheRecentsViewTaskViewTaskThumbnailView

7. 关键方法说明

7.1 AbsSwipeUpHandler 关键方法

onRecentsAnimationStart()

文件: AbsSwipeUpHandler.java:946-997

作用: 接收系统 Recents 动画回调,初始化动画目标和控制器

参数:

  • RecentsAnimationController controller: 动画控制器
  • RecentsAnimationTargets targets: 远程动画目标

流程:

  1. 分配动画目标(支持分屏和桌面模式)
  2. 初始化设备配置
  3. 创建 TaskViewSimulator
  4. 设置状态标志 STATE_APP_CONTROLLER_RECEIVED

代码片段:

@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,RecentsAnimationTargets targets) {super.onRecentsAnimationStart(controller, targets);// 根据模式分配目标if (isDesktopModeSupported() && targets.hasDesktopTasks()) {mRemoteTargetHandles = mTargetGluer.assignTargetsForDesktop(targets);} else {mRemoteTargetHandles = mTargetGluer.assignTargetsForSplitScreen(targets);}mRecentsAnimationController = controller;mRecentsAnimationTargets = targets;// 通知状态机mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
}
calculateEndTarget()

文件: AbsSwipeUpHandler.java:1224-1264

作用: 根据手势速度和位置计算手势结束目标

参数:

  • PointF velocityPxPerMs: X/Y 方向速度
  • float endVelocityPxPerMs: 主方向速度
  • boolean isFlingY: 是否为 Y 方向快速滑动
  • boolean isCancel: 是否取消

返回: GestureEndTarget 枚举值

逻辑:

private GestureEndTarget calculateEndTarget(PointF velocityPxPerMs, float endVelocityPxPerMs, boolean isFlingY, boolean isCancel) {if (isCancel) {return LAST_TASK;} else if (isFlingY) {// 快速滑动的判断if (mIsInAllAppsRegion) {return isSwipeUp ? ALL_APPS : LAST_TASK;}return isScrollingToNewTask() ? NEW_TASK : HOME;} else {// 慢速滑动的判断if (mIsInAllAppsRegion) {return ALL_APPS;} else if (mIsMotionPaused) {return RECENTS;} else if (isScrollingToNewTask()) {return NEW_TASK;}return velocity.y < 0 && mCanSlowSwipeGoHome ? HOME : LAST_TASK;}
}
applyScrollAndTransform()

文件: SwipeUpAnimationLogic.java (基类)

作用: 应用当前滚动和变换到窗口

流程:

  1. mCurrentShift 计算进度
  2. 更新 RecentsView 滚动
  3. 对每个 RemoteTargetHandle 应用变换
  4. 通过 TaskViewSimulator.apply() 计算矩阵
  5. 应用 SurfaceTransaction

7.2 RecentsView 关键方法

applyLoadPlan()

文件: RecentsView.java:1699-1922

作用: 应用任务加载计划,创建并绑定 TaskView

参数:

  • ArrayList<GroupTask> taskGroups: 任务组列表

流程:

  1. 如果列表为空,移除所有视图并重置状态
  2. 保存当前页面状态
  3. 卸载可见任务数据
  4. 移除所有现有视图
  5. 倒序遍历任务组(从最旧到最新)
  6. 从对象池获取 TaskView
  7. 根据任务类型绑定数据(单任务/分组任务/桌面任务)
  8. 添加 Clear All 按钮
  9. 恢复焦点和当前页
  10. 重置任务视觉效果
  11. 触发 onTaskStackUpdated()

关键代码:

protected void applyLoadPlan(ArrayList<GroupTask> taskGroups) {if (mPendingAnimation != null) {// 等待动画完成后再应用mPendingAnimation.addEndListener(success -> applyLoadPlan(taskGroups));return;}// 卸载旧数据unloadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);removeAllViews();// 创建新的 TaskViewfor (int i = taskGroups.size() - 1; i >= 0; i--) {GroupTask groupTask = taskGroups.get(i);TaskView taskView = getTaskViewFromPool(groupTask.taskViewType);addView(taskView);taskView.bind(groupTask.task1, mOrientationState);}// 添加 Clear All 按钮if (!taskGroups.isEmpty()) {addView(mClearAllButton);}// 恢复页面状态if (targetPage != -1) {runOnPageScrollsInitialized(() -> setCurrentPage(targetPage));}resetTaskVisuals();onTaskStackUpdated();
}
onTaskThumbnailChanged()

文件: RecentsView.java:989-1004

作用: 当任务缩略图在系统中更新时的回调

参数:

  • int taskId: 任务 ID
  • ThumbnailData thumbnailData: 新的缩略图数据

返回: 更新的 Task 对象(或 null)

流程:

  1. 检查是否处理任务栈变化
  2. 通过 taskId 查找对应的 TaskView
  3. 遍历 TaskView 的任务属性容器
  4. 找到匹配的任务并更新其缩略图

代码:

@Override
public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {if (mHandleTaskStackChanges) {TaskView taskView = getTaskViewByTaskId(taskId);if (taskView != null) {for (TaskIdAttributeContainer container :taskView.getTaskIdAttributeContainers()) {if (container != null && taskId == container.getTask().key.id) {container.getThumbnailView().setThumbnail(container.getTask(), thumbnailData);}}}}return null;
}

7.3 TaskView 关键方法

bind()

文件: TaskView.java:647-655

作用: 绑定任务数据到 TaskView

参数:

  • Task task: 要绑定的任务
  • RecentsOrientedState orientedState: 方向状态

流程:

  1. 取消待处理的加载任务
  2. 设置 mTask 字段
  3. 更新任务 ID 容器
  4. 调用 mSnapshotView.bind()
  5. 设置方向状态

代码:

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);
}
onTaskListVisibilityChanged()

文件: TaskView.java:1045-1098

作用: 当任务可见性改变时加载或卸载资源

参数:

  • boolean visible: 是否可见
  • @TaskDataChanges int changes: 需要更新的标志

流程:

  1. 取消待处理的加载任务
  2. 如果可见:
    • 从缓存或系统异步加载缩略图
    • 异步加载图标
    • 更新圆角半径
  3. 如果不可见:
    • 清除缩略图
    • 清除图标
    • 释放内存

代码:

public void onTaskListVisibilityChanged(boolean visible, @TaskDataChanges int changes) {if (mTask == null) {return;}cancelPendingLoadTasks();if (visible) {RecentsModel model = RecentsModel.INSTANCE.get(getContext());TaskThumbnailCache thumbnailCache = model.getThumbnailCache();TaskIconCache iconCache = model.getIconCache();if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {// 后台加载缩略图mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(mTask, thumbnail -> {mSnapshotView.setThumbnail(mTask, thumbnail);});}if (needsUpdate(changes, FLAG_UPDATE_ICON)) {// 后台加载图标mIconLoadRequest = iconCache.updateIconInBackground(mTask, (task) -> {setIcon(mIconView, task.icon);setText(mIconView, task.title);mDigitalWellBeingToast.initialize(task);});}} else {// 清除资源if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {mSnapshotView.setThumbnail(null, null);mTask.thumbnail = null;}if (needsUpdate(changes, FLAG_UPDATE_ICON)) {setIcon(mIconView, null);setText(mIconView, null);}}
}
launchTaskAnimated()

文件: TaskView.java:855-891

作用: 启动任务并播放动画

返回: RunnableList 用于动画完成回调

流程:

  1. 检查任务是否存在
  2. 获取 Activity 启动选项
  3. 调用 ActivityManagerWrapper.startActivityFromRecents()
  4. 如果在 Live Tile 模式:
    • 添加侧边任务启动回调
    • 返回回调列表
  5. 记录任务启动事件

代码:

@Nullable
public RunnableList launchTaskAnimated() {if (mTask != null) {ActivityOptionsWrapper opts = mActivity.getActivityLaunchOptions(this, null);opts.options.setLaunchDisplayId(getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());if (ActivityManagerWrapper.getInstance().startActivityFromRecents(mTask.key, opts.options)) {RecentsView recentsView = getRecentsView();if (recentsView.getRunningTaskViewId() != -1) {// Live Tile 模式recentsView.onTaskLaunchedInLiveTileMode();RunnableList callbackList = new RunnableList();recentsView.addSideTaskLaunchCallback(callbackList);return callbackList;}return opts.onEndCallback;} else {notifyTaskLaunchFailed(TAG);return null;}}return null;
}

7.4 TaskViewSimulator 关键方法

apply()

文件: TaskViewSimulator.java:421-519

作用: 应用当前动画参数到远程动画目标

参数:

  • TransformParams params: 变换参数容器

流程:

  1. 检查设备配置和缩略图位置
  2. 如果布局无效,重新计算:
    • 获取全屏缩放比例
    • 更新缩略图矩阵
    • 更新方向旋转
  3. 设置全屏进度
  4. 构建变换矩阵:
    • 应用缩略图矩阵
    • 应用 TaskView 平移
    • 应用轮播缩放
    • 应用 RecentsView 滚动
    • 应用 RecentsView 缩放
    • 应用窗口到 Home 的旋转
  5. 计算裁剪矩形
  6. 应用 Surface 参数

代码:

public void apply(TransformParams params) {if (mDp == null || mThumbnailPosition.isEmpty()) {return;}if (!mLayoutValid || mOrientationStateId != mOrientationState.getStateId()) {mLayoutValid = true;mOrientationStateId = mOrientationState.getStateId();getFullScreenScale();mThumbnailData.rotation = mOrientationState.getDisplayRotation();// 更新缩略图矩阵mPositionHelper.updateThumbnailMatrix(mThumbnailPosition, mThumbnailData,mTaskRect.width(), mTaskRect.height(),mDp.isTablet, mOrientationState.getRecentsActivityRotation(), isRtlEnabled);mPositionHelper.getMatrix().invert(mInversePositionMatrix);}float fullScreenProgress = Utilities.boundToRange(this.fullScreenProgress.value, 0, 1);mCurrentFullscreenParams.setProgress(fullScreenProgress, recentsViewScale.value,carouselScale.value);// 构建变换矩阵mMatrix.set(mPositionHelper.getMatrix());mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);// 应用各种变换mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE, taskPrimaryTranslation.value);mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE, taskSecondaryTranslation.value);mMatrix.postScale(carouselScale.value, carouselScale.value, mPivot.x, mPivot.y);mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y);applyWindowToHomeRotation(mMatrix);// 计算裁剪区域mTempRectF.set(0, 0, mTaskRect.width(), mTaskRect.height());mInversePositionMatrix.mapRect(mTempRectF);mTempRectF.roundOut(mTmpCropRect);// 应用到 Surfaceparams.applySurfaceParams(params.createSurfaceParams(this));
}

7.5 RecentsAnimationController 关键方法

finish()

文件: RecentsAnimationController.java:125-195

作用: 完成 Recents 动画

参数:

  • boolean toRecents: 是否到 Recents(true)还是应用(false)
  • Runnable onFinishComplete: 完成回调
  • boolean sendUserLeaveHint: 是否发送用户离开提示

流程:

  1. 添加回调到待处理列表
  2. 如果已经请求完成,直接返回
  3. 设置完成标志
  4. 通知监听器
  5. 在后台线程执行完成操作
  6. 结束 Jank 监控
  7. 执行回调

代码:

@UiThread
public void finishController(boolean toRecents, Runnable callback,boolean sendUserLeaveHint, boolean forceFinish) {mPendingFinishCallbacks.add(callback);if (!forceFinish && mFinishRequested) {return;  // 已经请求完成}mFinishRequested = true;mFinishTargetIsLauncher = toRecents;mOnFinishedListener.accept(this);Runnable finishCb = () -> {mController.finish(toRecents, sendUserLeaveHint, new IResultReceiver.Stub() {@Overridepublic void send(int i, Bundle bundle) throws RemoteException {MAIN_EXECUTOR.execute(() -> {mPendingFinishCallbacks.executeAllAndDestroy();});}});// 结束 Jank 监控InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_QUICK_SWITCH);InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);};if (forceFinish) {finishCb.run();} else {UI_HELPER_EXECUTOR.execute(finishCb);}
}

8. 最佳实践和注意事项

8.1 性能优化

  1. TaskView 对象池

    • 复用 TaskView 实例,避免频繁创建
    • 及时回收不可见的 TaskView
  2. 异步加载

    • 缩略图和图标在后台线程加载
    • 避免阻塞主线程
  3. 按需加载

    • 只为可见任务加载高分辨率资源
    • 不可见时释放内存
  4. 动画优化

    • 使用 TaskViewSimulator 避免实际 View 操作
    • 直接操作 Surface 而非 View 层级

8.2 状态管理

  1. MultiStateCallback 使用

    • 明确定义所有状态标志
    • 使用状态组合触发回调
    • 避免竞态条件
  2. 手势取消处理

    • 正确处理 onRecentsAnimationCanceled()
    • 切换到截图避免闪烁
    • 清理动画资源
  3. 生命周期管理

    • Activity 重建时正确恢复状态
    • 及时注销监听器
    • 避免内存泄漏

8.3 调试技巧

  1. 日志记录

    • 使用 ActiveGestureLog 记录关键事件
    • 记录状态转换
    • 记录动画参数
  2. 测试协议

    • 使用 TestProtocol 标记关键点
    • 支持自动化测试
  3. 性能监控

    • 使用 InteractionJankMonitorWrapper 监控卡顿
    • 追踪动画完成时间

9. 常见问题和解决方案

Q1: 缩略图不更新?

原因:

  • TaskView 不可见
  • 缓存未失效
  • 系统未发送更新通知

解决:

// 强制刷新缩略图
taskView.onTaskListVisibilityChanged(true, TaskView.FLAG_UPDATE_THUMBNAIL);

Q2: 动画卡顿?

原因:

  • 主线程执行耗时操作
  • Surface 事务过多
  • GC 频繁触发

解决:

  • 使用对象池
  • 批量应用 Surface 事务
  • 异步加载资源

Q3: 手势识别不准确?

原因:

  • 速度阈值不合理
  • 状态判断逻辑错误
  • 触摸事件被拦截

解决:

  • 调整 quickstep_fling_threshold_speed
  • 检查 calculateEndTarget() 逻辑
  • 确保 InputConsumer 正确启用

Q4: Launcher 未正确显示?

原因:

  • 状态机未到达必要状态
  • Launcher 被其他窗口覆盖
  • Activity 生命周期异常

解决:

  • 检查 STATE_LAUNCHER_DRAWN 标志
  • 确保调用 clearForceInvisibleFlag()
  • 检查 onActivityInit() 返回值

10. 总结

Launcher3 的最近任务系统是一个复杂但设计精良的架构,主要特点包括:

  1. 清晰的职责分离

    • 手势处理(AbsSwipeUpHandler)
    • UI 管理(RecentsView/TaskView)
    • 动画计算(TaskViewSimulator)
    • 系统交互(RecentsAnimationController)
  2. 高效的性能

    • 对象池复用
    • 异步资源加载
    • 直接操作 Surface
    • 按需加载策略
  3. 灵活的状态管理

    • MultiStateCallback 机制
    • 清晰的状态转换
    • 可组合的状态标志
  4. 完善的动画系统

    • 实时窗口变换
    • 平滑过渡动画
    • 支持多种手势结束目标

理解这些核心概念和流程,将有助于:

  • 修复 Recents 相关 bug
  • 添加新的手势功能
  • 优化动画性能
  • 定制 Launcher 行为

附录

A. 相关文件列表

文件说明行数
AbsSwipeUpHandler.java手势处理核心~2000
RecentsView.java任务容器视图~6000
TaskView.java单个任务视图~1943
TaskViewSimulator.java动画计算器~562
RemoteAnimationTargets.java动画目标封装~198
RecentsAnimationController.java动画控制器~282
RecentsAnimationCallbacks.java动画回调~262

B. 关键常量

// 动画时长
MAX_SWIPE_DURATION = 350ms  // 最大滑动时长
RECENTS_ATTACH_DURATION = 300ms  // Recents 附着时长// 进度阈值
MIN_PROGRESS_FOR_OVERVIEW = 0.7f  // 进入 Overview 的最小进度
UPDATE_SYSUI_FLAGS_THRESHOLD = 0.85f  // 更新系统 UI 的阈值// 状态标志
STATE_LAUNCHER_PRESENT = 1 << 0
STATE_GESTURE_STARTED = 1 << 5
STATE_APP_CONTROLLER_RECEIVED = 1 << 7

C. 参考资料

  • Android Recents Animation 文档
  • Launcher3 源码
  • 手势导航设计文档
http://www.dtcms.com/a/602043.html

相关文章:

  • 青岛市网站制作seo搜索引擎优化薪资
  • MediaPipe LLM Inference:在WEB浏览器中“裸跑”大语言模型
  • 网站平台建设公司经营范围域名注册成功怎么做网站
  • 南昌哪里有网站建设网页制作平台flash
  • 2025 创客匠人全球创始人 IP + AI 万人高峰论坛:家庭教育与企业管理的变革指南
  • Canvas指纹模拟避坑指南Canvas指纹防护实测案例
  • 2.11 实践二:基于 LoRA 微调一个垂直领域客服问答模型并部署为 API
  • 建设房屋出租网站饮食网站首页页面
  • 网站怎样自动文字排版网站建设58
  • 从工作流搭建看智能体与RPA流程自动化有何不同?
  • C语言编译器IDE | 提升程序开发效率的最佳选择
  • 当遇到 502 错误(Bad Gateway)怎么办
  • 告别停机焦虑:耐达讯自动化Profibus光纤模块——您的控制链路‘救星’在此”
  • 天津做网站优化的公司酒店网站收入如何做帐务处理
  • 数据智能时代的安全困局与 AI 破局逻辑
  • Docker镜像操作:构建、推送、拉取与优化
  • 网站流量怎么做的丹阳网站建设哪家好
  • 做团餐 承包食堂的企业网站管理咨询项目
  • 什么是大数据迁移?
  • Paimon——追根溯源
  • 门户类网站图片百度账号购买网站
  • 做钓鱼网站视频教程网络营销是什么行业
  • 安全版三权分立及密码限制
  • 网站空间在线解压网页背景做的比较好的网站
  • GIT版本管理工具轻松入门 | TortoiseGit,分支的创建与合并,笔记06
  • 网站自身seo优化怎么做ssh实训做网站代码下载
  • 网络范围的流量矩阵(TM)多步预测
  • 国外黄冈网站推广软件有哪些单县城乡住房建设局网站
  • TDengine 字符串函数 LIKE_IN_SET 用户手册
  • Spring5基础教程(2)--代理模式/AOP/Mybatis-Spring