从 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); }); } }
当应用启动时,可以从 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); }
startActivity 时,需要在 ActivityOptions 指定 Task 任务窗口的 LauncherBounds,根据 TaskView 的窗口位置大小,通过 WindowContainerTransaction 重新设置 Bounds,TaskView 的布局变化都需要 updateBounds 来保证画面同步(一般系统会自行处理)。
3、生命周期
WindowContainerTransaction wct = new WindowContainerTransaction(); wct.setHidden(token, !mSurfaceCreate); mSyncQueue.queue(wct);
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); } }
通过计算 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); }
由于 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()); }
但是涉及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 注入入口。
startActivity
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
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); } }
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 渲染显示。
浙公网安备 33010602011771号