从 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就已经被弃用
源码链接ActivityView.java
作用:用于将一个 Activity 的画面嵌入到另一个 Activity 的视图中。
设计初衷:作为 SystemUI / Launcher 的通用嵌入式 UI 工具。
实现原理:会创建一个 VirtualDisplay 启动目标应用的 Activity,ActivityView 拿到 VirtualDisplay 中对应的 SurfaceControl,通过reparent挂载到当前的SurfaceView上渲染。
弃用原因:VirtualDisplay 设计初衷是用于屏幕录制、投屏等场景,不适合复杂的多窗口管理(触摸事件、输入法(IME)、Z-Order 层级等问题频发,底层依赖不稳定的私有 API,难以维护),而 ActivityView 严重依赖 VirtualDisplay。
> 扩展 TaskOrganizer 相关
任务组织者,提供了一系列 TaskListener
TaskOrganizerController: 位于frameworks/services/core,是 TaskOrganizer 注册的最终接收端。
WMShellBaseModule: Dagger 模块,通过 @Provides 创建 ShellTaskOrganizer
WMShellModule.java: 提供 BaseModule 的依赖
ShellTaskOrganizer: TaskOrganizer 的子类,具体 Organizer 实现,它负责接收系统服务(TaskOrganizerController)发来的任务回调,并根据任务类型(分屏、全屏、自由窗口等)分发给对应的 TaskListener。
SystemUIInitializer.java: SystemUI 的初始化器,负责搭建 Dagger 的依赖注入,init 方法中实例化了 WMComponent。
SystemUIApplication.java: 入口启动,通过 SystemUIInitializer.getSysUIComponent() 拿到。
WMComponent.java : 具体 WMShellModule 注入入口。
ShellInit: 调度器,保证 WMShell 模块中各个组件按照正确的依赖顺序初始化,通过 ShellTaskOrganizer.onInit() 调用 registerOrganizer(),通过 Binder 向 TaskOrganizerController 发起注册请求。
注册流程:
SystemUIApplication.onCreate() → SystemUIInitializer.init() → WMComponent → WMShellBaseModule.provideShellTaskOrganizer() → registerOrganizer()
> 扩展 SurfaceView 相关
源码链接:SurfaceView.java
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
源码链接:RemoteCarTaskView.java
CarSystemUI 在 TaskView 基础上扩展了 RemoteCarTaskView 抽象类,里面包含一整套标准化接口。
ControlledRemoteCarTaskView: 管理一个预知的 Intent 任务,它在初始化后会自动启动 startActivity,并管理其整个生命周期(如地图,车模)。
RemoteCarRootTaskView: 创建一个Root Task,所有在该容器内启动的 Activity 都会被放入这个堆栈中,形成一个独立的任务栈,提供对内部任务栈的精细控制,可以在 RemoteCarRootTaskViewConfig 配置允许启动的 Activity 列表,提供稳定的、独立的容器来承载(最近任务,语言助手)。
RemoteCarDefaultRootTaskView: 默认Task,共享同一个任务栈,无需指定容器(applist)。
> 扩展 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 渲染显示。
posted on 2026-09-02 10:30  翻滚的咸鱼  阅读(7)  评论(0)    收藏  举报