前文
Android U 多任务启动分屏——Launcher流程(下分屏)
前文说到通过ISplitScreen
接口跨进程调用到了SystemUI进程,我们继续分析分屏在systemui中的实现,这些实现都是在WMShell中实现的。
WMShell实现分屏流程
实现ISplitScreen
接口
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
/**
* The interface for calls from outside the host process.
*/
@BinderThread
private static class ISplitScreenImpl extends ISplitScreen.Stub
implements ExternalInterfaceBinder {
......
@Override
public void startTasks(int taskId1, @Nullable Bundle options1, int taskId2,
@Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
//切换到了binder线程到main线程,调用StageCoordinator的startTasks方法
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasks(taskId1, options1,
taskId2, options2, splitPosition, splitRatio, remoteTransition,
instanceId));
}
......
}
这个方法主要就是做了两件事:
1.权限检查并切换至主线程操作
2.调用StageCoordinator的startTasks方法启动分屏
权限检查并切换至主线程操作
先来说说executeRemoteCallWithTaskPermission
方法,这个方法主要是做了一些鉴权和切换至主线程的操作。
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java
/**
* Helpers for working with executors
*/
public class ExecutorUtils {
/**
* Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given
* callback.
*/
public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance,
String log, Consumer<T> callback) {
executeRemoteCallWithTaskPermission(controllerInstance, log, callback,
false /* blocking */);
}
/**
* Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given
* callback.
*/
public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance,
String log, Consumer<T> callback, boolean blocking) {
if (controllerInstance == null) return;
final RemoteCallable<T> controller = controllerInstance;
//检查MANAGE_ACTIVITY_TASKS权限
controllerInstance.getContext().enforceCallingPermission(
Manifest.permission.MANAGE_ACTIVITY_TASKS, log);
if (blocking) {
try {
controllerInstance.getRemoteCallExecutor().executeBlocking(() -> {
callback.accept((T) controller);
});
} catch (InterruptedException e) {
Slog.e("ExecutorUtils", "Remote call failed", e);
}
} else {
//切换主线程执行callback
controllerInstance.getRemoteCallExecutor().execute(() -> {
callback.accept((T) controller);
});
}
}
}
这个方法主要就做了两件事:
1.检查MANAGE_ACTIVITY_TASKS
权限
enforceCallingPermission
方法的实现在ContextImpl.java
中。
2.切换主线程执行callback
这里getRemoteCallExecutor
方法的实现就在SplitScreenController.java
中。
@Override
public ShellExecutor getRemoteCallExecutor() {
return mMainExecutor;
}
代码很简单,就是调用主线程。
调用分屏方法
我们再来看看startTasks
方法。
controller.mStageCoordinator.startTasks(taskId1, options1,
taskId2, options2, splitPosition, splitRatio, remoteTransition,
instanceId));
这里面的参数正是在下分屏的流程中跨进程传递过来的。
分屏方法的实现
设置上分屏
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
/** Starts 2 tasks in one transition. */
void startTasks(int taskId1, @Nullable Bundle options1, int taskId2,
@Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
//创建WindowContainerTransaction对象
final WindowContainerTransaction wct = new WindowContainerTransaction();
//判断taskId2是否是无效的id,如果是无效id则退出分屏,回到全屏
if (taskId2 == INVALID_TASK_ID) {
if (mMainStage.containsTask(taskId1) || mSideStage.containsTask(taskId1)) {
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
}
if (mRecentTasks.isPresent()) {
mRecentTasks.get().removeSplitPair(taskId1);
}
options1 = options1 != null ? options1 : new Bundle();
addActivityOptions(options1, null);
wct.startTask(taskId1, options1);
mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
return;
}
//设置分屏位置
setSideStagePosition(splitPosition, wct);
//获取options1
options1 = options1 != null ? options1 : new Bundle();
//设置options1中的token为SideStage
addActivityOptions(options1, mSideStage);
//关联options1与taskId1,即taskId1设为SideStage
wct.startTask(taskId1, options1);
//设置下分屏后,启动分屏
startWithTask(wct, taskId2, options2, splitRatio, remoteTransition, instanceId);
}
桌面流程中传递过来的值是0,即splitPosition
值为0,因此SideStage
在上分屏显示。
设置下分屏
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
/**
* Starts with the second task to a split pair in one transition.
*
* @param wct transaction to start the first task
* @param instanceId if {@code null}, will not log. Otherwise it will be used in
* {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)}
*/
private void startWithTask(WindowContainerTransaction wct, int mainTaskId,
@Nullable Bundle mainOptions, float splitRatio,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
if (!mMainStage.isActive()) {
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
mMainStage.activate(wct, false /* reparent */);
}
//设置分屏比例,桌面侧传递过来的值,默认为0.5
mSplitLayout.setDivideRatio(splitRatio);
//计算出上下分屏的bound都设置给对应的configration,
//传递到systemserver端,方便systemserver更新task的bound
updateWindowBounds(mSplitLayout, wct);
//mRootTaskInfo.token指的就是分屏的rootTask
//让装载分屏的rootTask进行reorder,主要就是为了把分屏移到最前面
wct.reorder(mRootTaskInfo.token, true);
//设置rootTask是否半透明
setRootForceTranslucent(false, wct);
// Make sure the launch options will put tasks in the corresponding split roots
//设置下分屏options
mainOptions = mainOptions != null ? mainOptions : new Bundle();
addActivityOptions(mainOptions, mMainStage);
// Add task launch requests
//添加启动的下分屏task
wct.startTask(mainTaskId, mainOptions);
// leave recents animation by re-start pausing tasks
//清除任务列表中缓存的mainTask
if (mPausingTasks.contains(mainTaskId)) {
mPausingTasks.clear();
}
//启动进入分屏的过渡动画,提交WindowContainerTransaction,让分屏显示出来
mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, remoteTransition, this,
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
setEnterInstanceId(instanceId);
}
我们mSplitTransitions.startEnterTransition
这个方法中传递了WindowContainerTransaction
对象wct
,用于后续system_server流程中提交该事务,并且启动分屏。
其他参数:
-
TRANSIT_TO_FRONT
:默认值为3,表示已存在但不可见的窗口将变为可见
代码路径:frameworks/base/core/java/android/view/WindowManager.java/** * A window that already existed but was not visible is made visible. * @hide */ int TRANSIT_TO_FRONT = 3;
-
TRANSIT_SPLIT_SCREEN_PAIR_OPEN
:表示同时两个task的动画类型
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java/** Transition type for launching 2 tasks simultaneously. */ public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 4;
继续跟踪mSplitTransitions.startEnterTransition
启动分屏(过渡至system_server侧处理)
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
/** Starts a transition to enter split with a remote transition animator. */
IBinder startEnterTransition(
@WindowManager.TransitionType int transitType,
WindowContainerTransaction wct,
@Nullable RemoteTransition remoteTransition,
Transitions.TransitionHandler handler,
int extraTransitType, boolean resizeAnim) {
//mPendingEnter是EnterSession对象,
//如果已有一个待处理的进入过渡(mPendingEnter 不为 null)表示不会启动新的过渡。
if (mPendingEnter != null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ " skip to start enter split transition since it already exist. ");
return null;
}
//启动动画和提交WindowContainerTransaction相关事务
final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
//设置mPendingEnter动画
//这个方法主要就是创建新的EnterSession,然后复制给mPendingEnter
setEnterTransition(transition, remoteTransition, extraTransitType, resizeAnim);
return transition;
}
这里我们主要关注mTransitions.startTransition(transitType, wct, handler);
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
/** Start a new transition directly. */
public IBinder startTransition(@WindowManager.TransitionType int type,
@NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition "
+ "type=%d wct=%s handler=%s", type, wct, handler);
final ActiveTransition active = new ActiveTransition();
active.mHandler = handler;
//启动新的过渡动画,并提交WindowContainerTransaction相关事务
active.mToken = mOrganizer.startNewTransition(type, wct);
mPendingTransitions.add(active);
return active.mToken;
}
这个方法用于直接启动一个新的过渡动画,并提交WindowContainerTransaction相关事务。
mOrganizer
是WindowOrganizer
对象,mOrganizer.startNewTransition(type, wct);
传递了参数type
(TRANSIT_TO_FRONT)和wct
(WindowContainerTransaction对象),后续进入到system_server流程中处理。