惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
J
Java Code Geeks
Martin Fowler
Martin Fowler
Microsoft Azure Blog
Microsoft Azure Blog
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
人人都是产品经理
人人都是产品经理
有赞技术团队
有赞技术团队
爱范儿
爱范儿
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
博客园 - 【当耐特】
Y
Y Combinator Blog
Last Week in AI
Last Week in AI
MongoDB | Blog
MongoDB | Blog
G
Google Developers Blog
博客园 - 三生石上(FineUI控件)
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
大猫的无限游戏
大猫的无限游戏
罗磊的独立博客
The Cloudflare Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
V2EX
博客园 - 司徒正美

博客园 - 翻滚的咸鱼

Ops权限行为记录源码解析 视频解码器问题 常用命令 扫光动效 引导记录 性能优化 View共享动效 常用脚本文件 Launcher 卡片框架多模块集成 Launcher 桌面源码笔记二 Launcher 桌面源码笔记一 氛围灯动态屏保取色方案二 氛围灯动态屏保取色方案一 自定义最近任务管理器 anr问题 TV RecyclerView 焦点处理笔记 多类型适配器 多屏下字体自动取色 阻尼拉伸方案 图片区域点击处理 RecyclerView设置默认焦点跟多页面焦点抢占 TV RecyclerView 滑动后操作保持落焦在左侧第一个View Fragment翻盖动效 当个View下,使用Drawable入场退场动画
TaskView方案框架原理
翻滚的咸鱼 · 2026-09-02 · via 博客园 - 翻滚的咸鱼

从 android12 开始引入,用来替代 ActivityView

TaskView 是 ActivityView 的升级版,下面基于 Android16 系统进行解读

作用:允许一个应用(宿主)在其自身界面中嵌入并显示另一个完整应用的任务栈(Task Stack)。它不只是显示单个 Activity 的画面,而是能管理整个任务栈及其中的所有 Activity。

设计初衷:作为 ActivityView 的替代方案引入,解决 ActivityView 因依赖 VirtualDisplay 而引发的问题,主要用于系统级多窗口场景,如SystemUI中的Bubbles 、分屏(Split-screen) 、画中画(PiP) 、CarLauncher。

实现原理:TaskView 是 SurfaceView 的子类,并作为 ShellTaskOrganizer.TaskListener 的实现来接收系统回调。当嵌入的任务出现时,系统通过 onTaskAppeared 回调将代表该任务的 SurfaceControl(leash)传递给 TaskView。TaskView 随后在一个 SurfaceControl.Transaction 中,通过 reparent 操作将此 leash 的父级设置为自身的 SurfaceControl,从而将任务的渲染内容嵌入到自己的 Surface 上显示。

优势:ActivityView 只能显示单个 Activity 的画面;TaskView 可以管理完整的任务栈 ,能处理应用页面跳转和返回栈。

1、基础能力

TaskView 本质是一个 SurfaceView,系统提供了api将一个 SurfaceControl 挂载到另一个 SurfaceControl 上。

public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
        ViewTreeObserver.OnComputeInternalInsetsListener, TaskViewBase
    @Override
    public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
            SurfaceControl leash) {
        if (mTaskViewController.isUsingShellTransitions()) {
            mPendingInfo = taskInfo;
            if (mTaskNotFound) {
                // If we were already notified by shell transit that we don't have the
                // the task, clean it up now.
                cleanUpPendingTask();
            }
            // Everything else handled by enter transition.
            return;
        }
        mTaskInfo = taskInfo;
        mTaskToken = taskInfo.token;
        mTaskLeash = leash;

        if (mSurfaceCreated) {
            // Surface is ready, so just reparent the task to this surface control
            mTransaction.reparent(mTaskLeash, mSurfaceControl)
                    .show(mTaskLeash)
                    .apply();
        } else {
            // The surface has already been destroyed before the task has appeared,
            // so go ahead and hide the task entirely
            updateTaskVisibility();
        }
        mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true);
        mSyncQueue.runInSync((t) -> {
            mTaskViewBase.onTaskAppeared(taskInfo, leash);
        });

        if (mListener != null) {
            final int taskId = taskInfo.taskId;
            final ComponentName baseActivity = taskInfo.baseActivity;
            mListenerExecutor.execute(() -> {
                mListener.onTaskCreated(taskId, baseActivity);
            });
        }
    }

TaskView.java

当应用启动时,可以从 ShellTaskOrganizer.TaskListener 监听中拿到 TaskInfo 跟对应的 SurfaceControl,从而实现挂载的过程。

2、窗口bounds

分屏窗口下,并非全屏显示,系统要控制 Activity 窗口的显示范围。

WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setBounds(taskView.getTaskToken(), boundsOnScreen);
mSyncQueue.queue(wct);
    private void prepareActivityOptions(ActivityOptions options, Rect launchBounds,
            @NonNull TaskViewTaskController destination) {
        final Binder launchCookie = new Binder();
        mShellExecutor.execute(() -> {
            mTaskOrganizer.setPendingLaunchCookieListener(launchCookie, destination);
        });
        options.setLaunchBounds(launchBounds);
        options.setLaunchCookie(launchCookie);
        options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
        options.setRemoveWithTaskOrganizer(true);
    }

wct.setBounds

startActivity 时,需要在 ActivityOptions 指定 Task 任务窗口的 LauncherBounds,根据 TaskView 的窗口位置大小,通过 WindowContainerTransaction 重新设置 Bounds,TaskView 的布局变化都需要 updateBounds 来保证画面同步(一般系统会自行处理)。

3、生命周期

WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setHidden(token, !mSurfaceCreate);
mSyncQueue.queue(wct);

wct.setHidden

setHidden 可以控制 TaskView 上的 topActivity 进入stop或者resume状态,在 onTaskAppeared 等回调中,根据业务需求(如应用进入后台)调用显示/隐藏方法。

4、事件分发

使用 TaskView 存在多个 Activity 处于 Resume 状态,只会有一个窗口获取焦点,而 wms 会根据最近触摸的窗口去重新分配焦点,获取焦点的窗口可以正常接收 input 事件。

TaskView 作为一个容器,实现了 OnComputeInternalInsetsListener 接口来精确控制可触摸区域。

@Override
    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
        // TODO(b/176854108): Consider to move the logic into gatherTransparentRegions since this
        //   is dependent on the order of listener.
        // If there are multiple TaskViews, we'll set the touchable area as the root-view, then
        // subtract each TaskView from it.
        if (inoutInfo.touchableRegion.isEmpty()) {
            inoutInfo.setTouchableInsets(
                    ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
            View root = getRootView();
            root.getLocationInWindow(mTmpLocation);
            mTmpRootRect.set(mTmpLocation[0], mTmpLocation[1], root.getWidth(), root.getHeight());
            inoutInfo.touchableRegion.set(mTmpRootRect);
        }
        getLocationInWindow(mTmpLocation);
        mTmpRect.set(mTmpLocation[0], mTmpLocation[1],
                mTmpLocation[0] + getWidth(), mTmpLocation[1] + getHeight());
        if (mCaptionInsets != null) {
            mTmpRect.inset(mCaptionInsets);
            getBoundsOnScreen(mBoundsOnScreen);
            mTaskViewTaskController.setCaptionInsets(new Rect(
                    mBoundsOnScreen.left,
                    mBoundsOnScreen.top,
                    mBoundsOnScreen.right + getWidth(),
                    mBoundsOnScreen.top + mCaptionInsets.top));
        }
        inoutInfo.touchableRegion.op(mTmpRect, Region.Op.DIFFERENCE);

        if (mObscuredTouchRegion != null) {
            inoutInfo.touchableRegion.op(mObscuredTouchRegion, Region.Op.UNION);
        }
    }

onComputeInternalInsets

通过计算 TouchableRegion,精确控制每个窗口的触摸范围,可以把自身区域排除,系统就会分发给其它窗口,否则可能出现触摸事件无法正确传递给内部应用,或与SystemUI的其他手势(如状态栏下拉)冲突的情况。

5、startActivity

private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo,
            boolean removeLaunchCookieIfNeeded) {

        final int taskId = runningTaskInfo.taskId;
        TaskListener listener;

        // First priority goes to listener that might be pending for this task.
        final ArrayList<IBinder> launchCookies = runningTaskInfo.launchCookies;
        for (int i = launchCookies.size() - 1; i >= 0; --i) {
            final IBinder cookie = launchCookies.get(i);
            listener = mLaunchCookieToListener.get(cookie);
            if (listener == null) continue;

            if (removeLaunchCookieIfNeeded) {
                // Remove the cookie and add the listener.
                mLaunchCookieToListener.remove(cookie);
                mTaskListeners.put(taskId, listener);
            }
            return listener;
        }

        // Next priority goes to taskId specific listeners.
        listener = mTaskListeners.get(taskId);
        if (listener != null) return listener;

        // Next priority goes to the listener listening to its parent.
        if (runningTaskInfo.hasParentTask()) {
            listener = mTaskListeners.get(runningTaskInfo.parentTaskId);
            if (listener != null) return listener;
        }

        // Next we try type specific listeners.
        final int taskListenerType = taskInfoToTaskListenerType(runningTaskInfo);
        return mTaskListeners.get(taskListenerType);
    }

getTaskListener

由于 startActivity 是异步操作,系统服务(ATMS)创建好任务后,需要通过 Binder 回调 ShellTaskOrganizer.onTaskAppeared,ShellTaskOrganizer 为了知道这个新任务该交给哪个 TaskView 去渲染,就需要依赖 LaunchCookie 这个临时映射。

所以 TaskView 设计初衷是为特定应用(如 Launcher)提供一个容器来按需嵌入其他应用,不支持全局窗口模式,系统无法在不知道要将应用嵌入到哪个具体 TaskView 的情况下,自动完成绑定。

修改 Framework 层(系统级拦截)

在 ActivityTaskManagerService 中的 startActivityAsUser 方法拦截所有应用启动请求,并为其分配 TaskView,这是所有 Activity 启动请求的最终汇聚点之一,统一处理启动参数,实现应用无侵入启动。

@Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
            Bundle bOptions) {
        return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
    }

startActivity

但是涉及frameworks修改(极氪方案)

应用侧自行处理

涉及模块过多,并且不可控,忽略方案

替补方案1(动态代理IActivityManager)

在应用进程调用 startActivity 时,拦截并替换掉与系统通信的 IActivityManager Binder 代理对象。

获取原始代理:通过反射获取 ActivityManager 类中的 IActivityManagerSingleton 对象,进而得到原始的IActivityManager 实例;

创建动态代理:使用 Proxy.newProxyInstance 创建一个代理对象,并实现 InvocationHandler 接口。

拦截方法:在 InvocationHandler 的 invoke 方法中,当检测到是 startActivity 相关方法时,修改传入的 Intent 参数(例如,修改 ComponentName 或添加特殊 Flags)。

替换代理:通过反射将 IActivityManagerSingleton 中的原始实例替换为你创建的动态代理对象。

替补方案2(IActivityController)

IActivityController 是一个 AIDL 接口,系统允许通过 ActivityManager 的 setActivityController 方法来设置一个控制器。每次系统尝试启动 Activity 时,都会回调你控制器中的方法。

实现接口:实现 IActivityController.Stub 接口。

关键方法:重点实现 activityStarting(in Intent intent, String pkg) 方法。此方法在 Activity 启动前被调用,你可以在这里检查并修改 Intent,或通过返回值true(允许)或false(拒绝)来控制是否启动。

注册控制器:通过 ActivityManager.setActivityController,并传入你的控制器实例。

扩展相关

> 扩展 ActivityView

在Android12就已经被弃用

作用:用于将一个 Activity 的画面嵌入到另一个 Activity 的视图中。

设计初衷:作为 SystemUI / Launcher 的通用嵌入式 UI 工具。

实现原理:会创建一个 VirtualDisplay 启动目标应用的 Activity,ActivityView 拿到 VirtualDisplay 中对应的 SurfaceControl,通过reparent挂载到当前的SurfaceView上渲染。

弃用原因:VirtualDisplay 设计初衷是用于屏幕录制、投屏等场景,不适合复杂的多窗口管理(触摸事件、输入法(IME)、Z-Order 层级等问题频发,底层依赖不稳定的私有 API,难以维护),而 ActivityView 严重依赖 VirtualDisplay。

> 扩展 TaskOrganizer 相关

任务组织者,提供了一系列 TaskListener

ShellTaskOrganizer: TaskOrganizer 的子类,具体 Organizer 实现,它负责接收系统服务(TaskOrganizerController)发来的任务回调,并根据任务类型(分屏、全屏、自由窗口等)分发给对应的 TaskListener。

SystemUIInitializer.java: SystemUI 的初始化器,负责搭建 Dagger 的依赖注入,init 方法中实例化了 WMComponent。

WMComponent.java : 具体 WMShellModule 注入入口。

ShellInit: 调度器,保证 WMShell 模块中各个组件按照正确的依赖顺序初始化,通过 ShellTaskOrganizer.onInit() 调用 registerOrganizer(),通过 Binder 向 TaskOrganizerController 发起注册请求。

注册流程:

SystemUIApplication.onCreate() → SystemUIInitializer.init() → WMComponent → WMShellBaseModule.provideShellTaskOrganizer() → registerOrganizer()

> 扩展 SurfaceView 相关

SurfaceView:特殊View,拥有独立的绘图层,可见时,会创建一个SurfaceControl;

Activity 拥有一个 Surface,xml中的视图最终都写在这个 Surface 中,而 SurfaceView 系统会额外在创建一个 Surface,并且跟 Activity 的 Surface 是平级关系,所以在 View 视图层级中都属于一个层级,但在 SurfaceFlinger 图层层级中不是一个层级,在物理层中 Activity 是LayerA,SurfaceView 就是LayerB。

Surface:画板,存储图形数据的缓冲区。

SurfaceControl:遥控,持有 Surface 的引用,用来操控 Surface 的一个句柄(创建或销毁surface,设置图层的位置、大小、透明度、裁剪区域),每个control都对应屏幕上的一个图层。

Transaction:用来操作SurfaceControl的指令,可以将一系列动作(移动位置、改变大小、透明度)组成一个事务,最后通过apply方法提交给 SurfaceFlinger 执行,可以避免多次单独调用而导致的画面闪烁问题;

transaction.reparent(sc, newParent) 用来改变一个图层在图层树中的父节点,将sc这个图层从它当前的父节点下移除,并挂载到 newParent 这个新父节点下。

SurfaceFlinger: 合成显示,输出到硬件屏幕中。

维护着Layer Tree,记录了每个图层的父子关系、位置、大小、透明度等属性。Transaction 中调用的 setPosition、setAlpha、reparent 等操作,最终都是去修改 SurfaceFlinger 内存中这个 Layer 对象的对应字段。

> 扩展 AAOS RemoteCarTaskView

CarSystemUI 在 TaskView 基础上扩展了 RemoteCarTaskView 抽象类,里面包含一整套标准化接口。

ControlledRemoteCarTaskView: 管理一个预知的 Intent 任务,它在初始化后会自动启动 startActivity,并管理其整个生命周期。

RemoteCarRootTaskView: 创建一个Root Task,所有在该容器内启动的 Activity 都会被放入这个堆栈中,形成一个独立的任务栈,提供对内部任务栈的精细控制,可以在 RemoteCarRootTaskViewConfig 配置允许启动的 Activity 列表,提供稳定的、独立的容器来承载。

RemoteCarDefaultRootTaskView: 默认Task,共享同一个任务栈,无需指定容器。

> 扩展 Activity 启动流程

Android 10 之后,Activity 和 Task 管理从 ActivityManagerService 中拆分

AMS:主要负责进程、Service、Broadcast、Provider 等管理。

ATMS:主要负责Activity、Task、窗口层级、Activity 生命周期调度。

Activity 启动大致流程

@UnsupportedAppUsage
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        if (DEBUG_START_ACTIVITY) {
            Log.d(TAG, "startActivity: who=" + who + " source=" + target + " intent=" + intent
                    + " requestCode=" + requestCode + " options=" + options, new Throwable());
        }
        Objects.requireNonNull(intent);
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
        if (isSdkSandboxAllowedToStartActivities()) {
            adjustIntentForCtsInSdkSandboxInstrumentation(intent);
        }
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    ActivityResult result = null;
                    if (am.ignoreMatchingSpecificIntents()) {
                        if (options == null) {
                            options = ActivityOptions.makeBasic().toBundle();
                        }
                        result = am.onStartActivity(who, intent, options);
                    }
                    if (result != null) {
                        am.mHits++;
                        return result;
                    } else if (am.match(who, null, intent)) {
                        am.mHits++;
                        if (am.isBlocking()) {
                            return requestCode >= 0 ? am.getResult() : null;
                        }
                        break;
                    }
                }
            }
        }
        try {
            intent.migrateExtraStreamToClipData(who);
            intent.prepareToLeaveProcess(who);
            int result = ActivityTaskManager.getService().startActivity(whoThread,
                    who.getOpPackageName(), who.getAttributionTag(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()), token,
                    target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
            notifyStartActivityResult(result, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }
    /**
     * @return A starter to configure and execute starting an activity. It is valid until after
     *         {@link ActivityStarter#execute} is invoked. At that point, the starter should be
     *         considered invalid and no longer modified or used.
     */
    ActivityStarter obtainStarter(Intent intent, String reason) {
        return mFactory.obtain().setIntent(intent).setReason(reason);
    }
    
        @Override
        public ActivityStarter obtain() {
            ActivityStarter starter = mStarterPool.acquire();

            if (starter == null) {
                if (mService.mRootWindowContainer == null) {
                    throw new IllegalStateException("Too early to start activity.");
                }
                starter = new ActivityStarter(mController, mService, mSupervisor, mInterceptor);
            }

            return starter;
        }
    int execute() {
        try {
            int res = START_CANCELED;
            res = executeRequest(mRequest);
        } finally {
            onExecutionComplete();
        }
    }
    void addChild(WindowContainer child, final boolean toTop, boolean showForAllUsers) {
        Task task = child.asTask();
        try {
            if (task != null) {
                task.setForceShowForAllUsers(showForAllUsers);
            }
            // We only want to move the parents to the parents if we are creating this task at the
            // top of its root task.
            addChild(child, toTop ? MAX_VALUE : 0, toTop /*moveParents*/);
        } finally {
            if (task != null) {
                task.setForceShowForAllUsers(false);
            }
        }
    }
    
    void removeChild(WindowContainer child, boolean removeSelfIfPossible) {
        super.removeChild(child);
        final ActivityRecord r = child.asActivityRecord();
        final WindowProcessController hostProcess = getOrganizerProcessIfDifferent(r);
        if (hostProcess != null) {
            hostProcess.removeEmbeddedActivity(r);
        }
        if (removeSelfIfPossible && shouldRemoveSelfOnLastChildRemoval() && !hasChild()) {
            removeImmediately("removeLastChild " + child);
        }
    }

startActivity

1、App 进程 Activity.startActivity() 通过 Instrumentation 发起启动请求;

Instrumentation.execStartActivity() → ActivityTaskManager.getService().startActivity()

2、System Server 解析 Intent、ActivityTaskManagerService 发起执行,ActivityStarter 负责启动,最后会将 Activity 放入 Task;

  ActivityStarter.execute() → executeRequest() → startActivityUnchecked() → startActivityInner()

3、目标 App 进程接收生命周期事务,ActivityThread 创建 Activity;

4、执行 onCreate / onStart / onResume,并进入窗口显示流程;

5、最后WMS通过 SurfaceFlinger 渲染显示。