媒介
上一篇我们具体分析了系统处于多窗口模式下,Android应用和多窗口模式相干方法的调用次序,对于应用如何适配多窗口模式有了一个开端的熟悉,本篇文章我们将会联合Android12系统源码,具体来梳理一下系统是如何触发多窗口分屏模式,以及实现多窗口分屏模式功能的原理。
一、Launcher3触发分屏
1、Android12的分屏模式触发入口,默认是在最近任务列表中的,而最近任务列表是包含在Launcher3内里的,当我们在最近任务列表中点击分屏按钮后,会先触发Launcher进入分屏的一系列悬浮动画以及初始的图标分屏。
以上步骤都属于Launcher的业务逻辑。
2、接下来我们联合系统源码来简朴看下Launcher3模块是如何触发分屏功能的。
packages/apps/Launcher3/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
- public class QuickstepLauncher extends BaseQuickstepLauncher {
- @Override
- public void onStateSetEnd(LauncherState state) {
- super.onStateSetEnd(state);
- switch (state.ordinal) {
- ...代码省略...
- case QUICK_SWITCH_STATE_ORDINAL: {
- RecentsView rv = getOverviewPanel();
- TaskView tasktolaunch = rv.getTaskViewAt(0);
- if (tasktolaunch != null) {
- //调用TaskView的launchTask方法
- tasktolaunch.launchTask(success -> {
- if (!success) {
- getStateManager().goToState(OVERVIEW);
- } else {
- getStateManager().moveToRestState();
- }
- });
- } else {
- getStateManager().goToState(NORMAL);
- }
- break;
- }
- }
- }
- }
复制代码 package/apps/Launcher3/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
- public class GroupedTaskView extends TaskView {
- @Nullable
- @Override
- public RunnableList launchTaskAnimated() {
- if (mTask == null || mSecondaryTask == null) {
- return null;
- }
- RunnableList endCallback = new RunnableList();
- RecentsView recentsView = getRecentsView();
- // Callbacks run from remote animation when recents animation not currently running
- //调用RecentsView的getSplitPlaceholder方法,获取SplitSelectStateController对象实例,调用launchTasks方法
- recentsView.getSplitPlaceholder().launchTasks(this /*groupedTaskView*/,
- success -> endCallback.executeAllAndDestroy(),
- false /* freezeTaskList */);
- // Callbacks get run from recentsView for case when recents animation already running
- recentsView.addSideTaskLaunchCallback(endCallback);
- return endCallback;
- }
- @Override
- public void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {
- //调用RecentsView的getSplitPlaceholder方法,获取SplitSelectStateController对象实例,调用launchTasks方法
- getRecentsView().getSplitPlaceholder().launchTasks(mTask, mSecondaryTask,
- STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList,
- getSplitRatio());
- }
- }
复制代码 package/apps/Launcher3/quickstep/src/com/android/quickstep/views/RecentsView.java
- public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>,
- STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
- TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
- TaskVisualsChangeListener, SplitScreenBounds.OnChangeListener {
- public SplitSelectStateController getSplitPlaceholder() {
- return mSplitSelectStateController;
- }
- }
复制代码 package/apps/Launcher3/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
- public class SplitSelectStateController {
- public void launchTasks(Task task1, Task task2, @StagePosition int stagePosition,
- Consumer<Boolean> callback, boolean freezeTaskList, float splitRatio) {
- // Assume initial task is for top/left part of screen
- final int[] taskIds = stagePosition == STAGE_POSITION_TOP_OR_LEFT
- ? new int[]{task1.key.id, task2.key.id}
- : new int[]{task2.key.id, task1.key.id};
- if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
- RemoteSplitLaunchTransitionRunner animationRunner =
- new RemoteSplitLaunchTransitionRunner(task1, task2);
- mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
- null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT, splitRatio,
- new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR,
- ActivityThread.currentActivityThread().getApplicationThread()));
- } else {
- RemoteSplitLaunchAnimationRunner animationRunner =
- new RemoteSplitLaunchAnimationRunner(task1, task2, callback);
- //转场动画
- final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
- RemoteAnimationAdapterCompat.wrapRemoteAnimationRunner(animationRunner),
- 300, 150,
- ActivityThread.currentActivityThread().getApplicationThread());
- ActivityOptions mainOpts = ActivityOptions.makeBasic();
- if (freezeTaskList) {
- mainOpts.setFreezeRecentTasksReordering();
- }
- //调用SystemUiProxy的startTasksWithLegacyTransition方法
- mSystemUiProxy.startTasksWithLegacyTransition(taskIds[0], mainOpts.toBundle(),
- taskIds[1], null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
- splitRatio, adapter);
- }
- }
- }
复制代码 packages/apps/Launcher3/quickstep/src/com/android/quickstep/SystemUiProxy.java
- public class SystemUiProxy implements ISystemUiProxy,
- SysUINavigationMode.NavigationModeChangeListener {
-
- public static final MainThreadInitializedObject<SystemUiProxy> INSTANCE =new MainThreadInitializedObject<>(SystemUiProxy::new);
-
- private ISplitScreen mSplitScreen;
-
- public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
- IOneHanded oneHanded, IShellTransitions shellTransitions,
- IStartingWindow startingWindow, IRecentTasks recentTasks,
- ISmartspaceTransitionController smartSpaceTransitionController) {
- ...代码省略...
- mSplitScreen = splitScreen;
- ...代码省略...
- }
- /**
- * 分屏模式同时打开多个任务
- */
- public void startTasksWithLegacyTransition(int mainTaskId, Bundle mainOptions, int sideTaskId,
- Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition,
- float splitRatio, RemoteAnimationAdapter adapter) {
- if (mSystemUiProxy != null) {
- try {
- //调用ISplitScreen的startTasksWithLegacyTransition方法触发分屏
- mSplitScreen.startTasksWithLegacyTransition(mainTaskId, mainOptions, sideTaskId,
- sideOptions, sidePosition, splitRatio, adapter);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call startTasksWithLegacyTransition");
- }
- }
- }
-
- }
复制代码 通过梳理以上代码,可以发现Launche3终极是通过调用SystemUiProxy的startTasksWithLegacyTransition方法触发分屏的,而该方法内部又进一步调用了范例为ISplitScreen的mSplitScreen对象的startTasksWithLegacyTransition方法。
3、SystemUiProxy的内部属性对象mSplitScreen最初是在TouchInteractionService的内部类TISBinder的onInitialize方法中被赋值的。
packages/apps/Launcher3/quickstep/src/com/android/quickstep/TouchInteractionService.java
- public class TouchInteractionService extends Service
- implements ProtoTraceable<LauncherTraceProto.Builder> {
-
- private final TISBinder mTISBinder = new TISBinder();
-
- public class TISBinder extends IOverviewProxy.Stub {
- @BinderThread
- public void onInitialize(Bundle bundle) {
- ISystemUiProxy proxy = ISystemUiProxy.Stub.asInterface(
- bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
- IPip pip = IPip.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_PIP));
- //触发分屏就是调用的这个对象的方法
- ISplitScreen splitscreen = ISplitScreen.Stub.asInterface(bundle.getBinder(
- KEY_EXTRA_SHELL_SPLIT_SCREEN));
- IOneHanded onehanded = IOneHanded.Stub.asInterface(
- bundle.getBinder(KEY_EXTRA_SHELL_ONE_HANDED));
- IShellTransitions shellTransitions = IShellTransitions.Stub.asInterface(
- bundle.getBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS));
- IStartingWindow startingWindow = IStartingWindow.Stub.asInterface(
- bundle.getBinder(KEY_EXTRA_SHELL_STARTING_WINDOW));
- ISmartspaceTransitionController smartspaceTransitionController =
- ISmartspaceTransitionController.Stub.asInterface(
- bundle.getBinder(KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER));
- IRecentTasks recentTasks = IRecentTasks.Stub.asInterface(
- bundle.getBinder(KEY_EXTRA_RECENT_TASKS));
- MAIN_EXECUTOR.execute(() -> {
- //调用SystemUiProxy的setProxy方法
- SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
- splitscreen, onehanded, shellTransitions, startingWindow, recentTasks,
- smartspaceTransitionController);
- TouchInteractionService.this.initInputMonitor();
- preloadOverview(true /* fromInit */);
- });
- sIsInitialized = true;
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- Log.d(TAG, "Touch service connected: user=" + getUserId());
- return mTISBinder;
- }
- }
复制代码 packages/apps/Launcher3/quickstep/AndroidManifest.xml
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.launcher3">
-
- <application android:backupAgent="com.android.launcher3.LauncherBackupAgent">
- <service android:name="com.android.quickstep.TouchInteractionService"
- android:permission="android.permission.STATUS_BAR_SERVICE"
- android:directBootAware="true"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.QUICKSTEP_SERVICE"/>
- </intent-filter>
- </service>
-
- </application>
- </manifest>
复制代码 TouchInteractionService是Launcher的一个服务,内部类TISBinder就是其他模块绑定TouchInteractionService服务时间所返回的IBinder范例的实例对象。
二、SystemUI触发分屏
1、默认情况下,SystemUI模块对Launcher3模块的TouchInteractionService服务举行了绑定。
frameworks/base/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
- public class OverviewProxyService extends CurrentUserTracker implements
- CallbackController<OverviewProxyListener>, NavigationModeController.ModeChangedListener,
- Dumpable {
-
- private final Optional<SplitScreen> mSplitScreenOptional;//触发分屏模式的关键对象
- //唤起Launcher3模块TouchInteractionService的Action
- private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
- //唤起Launcher3模块TouchInteractionService的Intent
- private final Intent mQuickStepIntent;
- //远程IPC通信是实现类
- private IOverviewProxy mOverviewProxy;
- private boolean mBound;
-
- public OverviewProxyService(Context context, CommandQueue commandQueue,
- Lazy<NavigationBarController> navBarControllerLazy,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
- NavigationModeController navModeController,
- NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
- Optional<Pip> pipOptional,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreen> splitScreenOptional,
- Optional<OneHanded> oneHandedOptional,
- Optional<RecentTasks> recentTasks,
- Optional<StartingSurface> startingSurface,
- BroadcastDispatcher broadcastDispatcher,
- ShellTransitions shellTransitions,
- ScreenLifecycle screenLifecycle,
- SmartspaceTransitionController smartspaceTransitionController,
- UiEventLogger uiEventLogger,
- DumpManager dumpManager) {
- super(broadcastDispatcher);
- ...代码省略...
- //获取最近应用列表组件名称,其实就是Launcher3的包名
- mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
- com.android.internal.R.string.config_recentsComponentName));
- //创建最近应用列表Activity的意图对象
- mQuickStepIntent = new Intent(ACTION_QUICKSTEP).setPackage(mRecentsComponentName.getPackageName());
- ...代码省略...
- startConnectionToCurrentUser();
- ...代码省略...
- }
-
- //成功绑定服务所返回的ServiceConnection对象
- private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- ...代码省略...
- mCurrentBoundedUserId = getCurrentUserId();
- //为mOverviewProxy赋值
- mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
-
- Bundle params = new Bundle();
- params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder());
- params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius);
- params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows);
-
- mPipOptional.ifPresent((pip) -> params.putBinder(
- KEY_EXTRA_SHELL_PIP,
- pip.createExternalInterface().asBinder()));
- //关键对象,Optional对象的的ifPresent方法会判断该对象内部的SplitScreen实例对象是否为空,
- //不为空则执行回调方法,也就是把splitscreen对象实例存放到params里面。
- mSplitScreenOptional.ifPresent((splitscreen) -> params.putBinder(
- KEY_EXTRA_SHELL_SPLIT_SCREEN,
- splitscreen.createExternalInterface().asBinder()));
- mOneHandedOptional.ifPresent((onehanded) -> params.putBinder(
- KEY_EXTRA_SHELL_ONE_HANDED,
- onehanded.createExternalInterface().asBinder()));
- params.putBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS,
- mShellTransitions.createExternalInterface().asBinder());
- mStartingSurface.ifPresent((startingwindow) -> params.putBinder(
- KEY_EXTRA_SHELL_STARTING_WINDOW,
- startingwindow.createExternalInterface().asBinder()));
- params.putBinder(
- KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER,
- mSmartspaceTransitionController.createExternalInterface().asBinder());
- mRecentTasks.ifPresent(recentTasks -> params.putBinder(
- KEY_EXTRA_RECENT_TASKS,
- recentTasks.createExternalInterface().asBinder()));
-
- try {
- //调用mOverviewProxy的onInitialize,为相关参数进行服务
- mOverviewProxy.onInitialize(params);
- } catch (RemoteException e) {
- mCurrentBoundedUserId = -1;
- Log.e(TAG_OPS, "ServiceConnection Failed to call onInitialize()", e);
- }
- dispatchNavButtonBounds();
- // Force-update the systemui state flags
- updateSystemUiStateFlags();
- notifySystemUiStateFlags(mSysUiState.getFlags());
-
- notifyConnectionChanged();
- }
-
- };
-
- public void startConnectionToCurrentUser() {
- if (mHandler.getLooper() != Looper.myLooper()) {
- mHandler.post(mConnectionRunnable);
- } else {
- internalConnectToCurrentUser();
- }
- }
- private void internalConnectToCurrentUser() {
- ...代码省略...
- Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
- .setPackage(mRecentsComponentName.getPackageName());
- try {
- //绑定服务
- mBound = mContext.bindServiceAsUser(launcherServiceIntent,
- mOverviewServiceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
- UserHandle.of(getCurrentUserId()));
- } catch (SecurityException e) {
- Log.e(TAG_OPS, "Unable to bind because of security error", e);
- }
- ...代码省略...
- }
-
- public IOverviewProxy getProxy() {
- return mOverviewProxy;
- }
-
- }
复制代码 SystemUI模块的OverviewProxyService类的构造方法会对Launche3模块的TouchInteractionService服务举行绑定,并把调用该服务返回的Binder对象的onInitialize,将Launcher3模块必要的相干参数传了过去,这样Launch3模块才能拿到ISplitScreen的实例对象,通过调用该实例对象的startTasksWithLegacyTransition方法,终极触发分屏模式。那么问题有来了,OverviewProxyService内里的ISplitScreen对象实例是如何被赋值的?
2、重新再来看下OverviewProxyService的构造方法,这次我们重点关注一下mSplitScreenOptional这个对象。
- public class OverviewProxyService extends CurrentUserTracker implements
- CallbackController<OverviewProxyListener>, NavigationModeController.ModeChangedListener,
- Dumpable {
-
- private final Optional<SplitScreen> mSplitScreenOptional;//触发分屏模式的关键对象
-
- @Inject//Dagger2框架注解
- public OverviewProxyService(Context context, CommandQueue commandQueue,
- Lazy<NavigationBarController> navBarControllerLazy,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
- NavigationModeController navModeController,
- NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
- Optional<Pip> pipOptional,
- Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreen> splitScreenOptional,
- Optional<OneHanded> oneHandedOptional,
- Optional<RecentTasks> recentTasks,
- Optional<StartingSurface> startingSurface,
- BroadcastDispatcher broadcastDispatcher,
- ShellTransitions shellTransitions,
- ScreenLifecycle screenLifecycle,
- SmartspaceTransitionController smartspaceTransitionController,
- UiEventLogger uiEventLogger,
- DumpManager dumpManager) {
- super(broadcastDispatcher);
- ...代码省略...
- mSplitScreenOptional = splitScreenOptional;//为mSplitScreenOptional赋值
- ...代码省略...
- }
- private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- ...代码省略...
- mSplitScreenOptional.ifPresent((splitscreen) -> params.putBinder(
- KEY_EXTRA_SHELL_SPLIT_SCREEN,
- //这里调用splitscreen的createExternalInterface方法
- splitscreen.createExternalInterface().asBinder()));
- ...代码省略...
- }
- }
- }
复制代码 OverviewProxyService的构造方法有一个关键注解 @Inject,这个注解是Dagger2的框架注解,该框架会根据我们的配置,当我们必要在某个对象的构造方法中传入特定参数对象的时间,只要添加@Inject注解,该框架会自动帮我们创建参数对象并传入。关于这个框架的原理,我在Android 12系统源码_SystemUI(一)SystemUI的启动流程这篇博客具体分析过,这里不做过多解释。
3、由于后续会多次提到Optional这种范例的数据范例,这里我们必要先简朴看下这个类的相干代码。
- public final class Optional<T> {
- private static final Optional<?> EMPTY = new Optional<>();
- //内部包含的真正对象
- private final T value;
-
- private Optional(T value) {
- this.value = Objects.requireNonNull(value);
- }
-
- //如果内部对象不为空,则执行consumer方法
- public void ifPresent(Consumer<? super T> consumer) {
- if (value != null)
- consumer.accept(value);
- }
- //如果内部对象为空,则返回空对象,执行mapper方法,并将该方法返回的对象封装成Optional<T>类型返回。
- public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
- Objects.requireNonNull(mapper);
- if (!isPresent())
- return empty();
- else {
- return Optional.ofNullable(mapper.apply(value));
- }
- }
- }
复制代码 4、关于Optional这个对象的dagger2框架的配置信息,SystemUI配置在WMComponent这个接口内里的。
frameworks/base/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
- import com.android.wm.shell.dagger.WMShellModule;
- @WMSingleton//单例
- @Subcomponent(modules = {WMShellModule.class})//需要进一步结合WMShellModule做分析
- public interface WMComponent {
- /**
- * Initializes all the WMShell components before starting any of the SystemUI components.
- * 在初始化SystemUI组件之前,优先初始化WMShell模块的所有组件
- */
- default void init() {
- //调用ShellInit的init,这个方法需要额外关注一下,后续我们会再次提到
- getShellInit().init();
- }
- //获取ShellInit对象实例
- @WMSingleton
- ShellInit getShellInit();
- //获取Optional<SplitScreen>对象实例
- @WMSingleton
- Optional<SplitScreen> getSplitScreen();
- }
复制代码
- 有了以上配置信息,SystemUI模块的任何类的构造方法只要加上 @Inject注解,我们就可以在该对象的构造方法中拿到WMComponent 中返回的对象实例了。
- 联合getShellInit方法和init方法我们可以知道,SystemUI模块在初始化该模块的SystemUI组件之前,会先初始化WMShell模块的组件,这就意味着SystemUI模块的组件都能拿到WMShell模块的组件,并调用对应的组件所提供的功能。
- 而Optional到底是如何被创建出来的,这就必要我们进一步查看WMComponent的类注解@Subcomponent指向的WMShellModule这个类的相干代码了。
三、WMShell模块触发分屏
1、SystemUI模块终极是通过WindowManager模块下的Shell模块触发分屏功能的,来看下前面SystemUI模块中dagger2注解框架引用到的WMShellModule这个类。
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
- import com.android.wm.shell.splitscreen.SplitScreenController;
- @Module(includes = WMShellBaseModule.class)//需要进一步结合WMShellBaseModule做分析
- public class WMShellModule {
- @WMSingleton
- @Provides
- @DynamicOverride
- static SplitScreenController provideSplitScreenController(
- ShellTaskOrganizer shellTaskOrganizer,
- SyncTransactionQueue syncQueue, Context context,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
- @ShellMainThread ShellExecutor mainExecutor,
- DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool, IconProvider iconProvider,
- Optional<RecentTasksController> recentTasks,
- Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
- //创建SplitScreenController对象实例
- return new SplitScreenController(shellTaskOrganizer, syncQueue, context,
- rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
- displayInsetsController, transitions, transactionPool, iconProvider,
- recentTasks, stageTaskUnfoldControllerProvider);
- }
-
- //这个方法我们需要关注一下,后面会提到
- @WMSingleton
- @Provides
- static ShellInit provideShellInit(ShellInitImpl impl) {
- //调用ShellInitImpl的asShellInit方法返回ShellInit对象实例
- return impl.asShellInit();
- }
- @WMSingleton
- @Provides
- static ShellInitImpl provideShellInitImpl(DisplayController displayController,
- DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController,
- DragAndDropController dragAndDropController,
- ShellTaskOrganizer shellTaskOrganizer,
- Optional<BubbleController> bubblesOptional,
- Optional<SplitScreenController> splitScreenOptional,
- Optional<AppPairsController> appPairsOptional,
- Optional<PipTouchHandler> pipTouchHandlerOptional,
- FullscreenTaskListener fullscreenTaskListener,
- Optional<FullscreenUnfoldController> appUnfoldTransitionController,
- Optional<FreeformTaskListener> freeformTaskListener,
- Optional<RecentTasksController> recentTasksOptional,
- Transitions transitions,
- StartingWindowController startingWindow,
- @ShellMainThread ShellExecutor mainExecutor) {
- //创建ShellInitImpl的对象实例
- return new ShellInitImpl(displayController,
- displayImeController,
- displayInsetsController,
- dragAndDropController,
- shellTaskOrganizer,
- bubblesOptional,
- splitScreenOptional,
- appPairsOptional,
- pipTouchHandlerOptional,
- fullscreenTaskListener,
- appUnfoldTransitionController,
- freeformTaskListener,
- recentTasksOptional,
- transitions,
- startingWindow,
- mainExecutor);
- }
- }
复制代码 由于WMShellModule的类注解有依赖@Module(includes = WMShellBaseModule.class),要想完全搞明确Optional对象实例是如何被创建的,我们必要进一步联合WMShellBaseModule做分析。
2、WMShellBaseModule的关键代码如下所示。
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
- @Module(includes = WMShellConcurrencyModule.class)
- public abstract class WMShellBaseModule {
- @WMSingleton
- @Provides
- static Optional<SplitScreen> provideSplitScreen(
- Optional<SplitScreenController> splitScreenController) {
- //结合前面Optional<T>这个类的代码可以知道,调用splitScreenController对象的asSplitScreen方法,并将该方法返回的SplitScreen对 象实例封装成Optional<SplitScreen>类型的对象再返回。
- return splitScreenController.map((controller) -> controller.asSplitScreen());
- }
- @WMSingleton
- @Provides
- static Optional<SplitScreenController> providesSplitScreenController(
- @DynamicOverride Optional<SplitScreenController> splitscreenController,
- Context context) {
- //AMS是否支持多窗口模式,支持才返回SplitScreenController对象实例,否则返回空
- if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
- return splitscreenController;
- }
- return Optional.empty();
- }
- }
复制代码
- WMShellBaseModule的provideSplitScreen方法先是获取SplitScreenController对象实例,该对象是通过WMShellModule的provideSplitScreenController方法创建,但是会经过providesSplitScreenController做一层封装,只有当系统开启了支持多窗口模式的开关,也就是AMS支持多窗口模式的时间,才能拿到该对象实例,否则拿到的都是空
- provideSplitScreen方法在得到该对象实例后,通过调用该对象的asSplitScreen方法,得到了SplitScreen对象实例,但是终极返回的是封装成Optional范例的对象实例返回的。
到这里我们终于可以确定是SplitScreenController的asSplitScreen方法创建了SplitScreen对象实例。
3、接下来我们继续来梳理一下ISplitScreen和SplitScreenController类相干的代码。
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
- interface ISplitScreen {
- oneway void registerSplitScreenListener(in ISplitScreenListener listener) = 1;
-
- oneway void unregisterSplitScreenListener(in ISplitScreenListener listener) = 2;
- oneway void setSideStageVisibility(boolean visible) = 3;
- oneway void removeFromSideStage(int taskId) = 4;
- oneway void exitSplitScreen(int toTopTaskId) = 5;
- oneway void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 6;
-
- oneway void startTask(int taskId, int position, in Bundle options) = 7;
-
- oneway void startShortcut(String packageName, String shortcutId, int position,
- in Bundle options, in UserHandle user) = 8;
- oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int position,
- in Bundle options) = 9;
-
- oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId,
- in Bundle sideOptions, int sidePosition, float splitRatio,
- in RemoteTransition remoteTransition) = 10;
-
- oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
- int sideTaskId, in Bundle sideOptions, int sidePosition,
- float splitRatio, in RemoteAnimationAdapter adapter) = 11;
- }
复制代码 frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
- public class SplitScreenController implements DragAndDropPolicy.Starter, RemoteCallable<SplitScreenController> {
- private final SplitScreenImpl mImpl = new SplitScreenImpl();
-
- private StageCoordinator mStageCoordinator;
-
- public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
- SyncTransactionQueue syncQueue, Context context,
- RootTaskDisplayAreaOrganizer rootTDAOrganizer,
- ShellExecutor mainExecutor, DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController,
- Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider,
- Optional<RecentTasksController> recentTasks,
- Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
- ...代码省略...
- }
-
- public SplitScreen asSplitScreen() {
- return mImpl;
- }
-
- //这个方法最初是被ShellInitImpl调用的
- public void onOrganizerRegistered() {
- if (mStageCoordinator == null) {
- //创建触发分屏功能的重要对象StageCoordinator的实例。
- mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
- mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,
- mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
- mIconProvider, mRecentTasksOptional, mUnfoldControllerProvider);
- }
- }
- //SplitScreen是一个接口,具体实现是内部类SplitScreenImpl
- @ExternalThread
- private class SplitScreenImpl implements SplitScreen {
- private ISplitScreenImpl mISplitScreen;
- private final ArrayMap<SplitScreenListener, Executor> mExecutors = new ArrayMap<>();
- private final SplitScreen.SplitScreenListener mListener = new SplitScreenListener() {
-
- @Override
- public void onStagePositionChanged(int stage, int position) {
- ...代码省略...
- }
- @Override
- public void onTaskStageChanged(int taskId, int stage, boolean visible) {
- ...代码省略...
- }
- @Override
- public void onSplitVisibilityChanged(boolean visible) {
- ...代码省略...
- }
- };
- @Override
- public ISplitScreen createExternalInterface() {
- if (mISplitScreen != null) {
- mISplitScreen.invalidate();
- }
- mISplitScreen = new ISplitScreenImpl(SplitScreenController.this);
- //返回实现了ISplitScreen接口的对象实例
- return mISplitScreen;
- }
- }
- //ISplitScreen是一个aidl,内部类ISplitScreenImpl实现了ISplitScreen的接口方法。
- @BinderThread
- private static class ISplitScreenImpl extends ISplitScreen.Stub {
- private SplitScreenController mController;
- private final SingleInstanceRemoteListener<SplitScreenController,
- ISplitScreenListener> mListener;
- private final SplitScreen.SplitScreenListener mSplitScreenListener =
- new SplitScreen.SplitScreenListener() {
- @Override
- public void onStagePositionChanged(int stage, int position) {
- mListener.call(l -> l.onStagePositionChanged(stage, position));
- }
- @Override
- public void onTaskStageChanged(int taskId, int stage, boolean visible) {
- mListener.call(l -> l.onTaskStageChanged(taskId, stage, visible));
- }
- };
- public ISplitScreenImpl(SplitScreenController controller) {
- mController = controller;
- mListener = new SingleInstanceRemoteListener<>(controller,
- c -> c.registerSplitScreenListener(mSplitScreenListener),
- c -> c.unregisterSplitScreenListener(mSplitScreenListener));
- }
- void invalidate() {
- mController = null;
- }
- @Override
- public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
- int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
- float splitRatio, RemoteAnimationAdapter adapter) {
- //这里显示进行权限确认,然后会调用StageCoordinator的startTasksWithLegacyTransition方法。
- executeRemoteCallWithTaskPermission(mController, "startTasks",
- (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
- mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,
- splitRatio, adapter));
- }
- }
- }
复制代码
- SplitScreenController的asSplitScreen方法返回了该类的一个内部对象SplitScreenImpl,SplitScreenImpl实现了SplitScreen这个接口的相干方法。
- 前面第二节第1步OverviewProxyService类中我们有提到,SystemUI在成功绑定Launcher3模块的TouchInteractionService服务的时间,调用了SplitScreen 的createExternalInterface方法,联合这里我们可以知道此方法返回ISplitScreenImpl对象实例,此对象实现了ISplitScreen.aidl文件中声明的接口方法,Launcher3终极得以跨进程调用ISplitScreenImpl的startTasksWithLegacyTransition方法,终极触发分屏模式。
- ISplitScreenImpl的startTasksWithLegacyTransition方法内部先是做了个权限判断,终极是调用了SplitScreenController的范例为StageCoordinator的内部对象mStageCoordinator的startTasksWithLegacyTransition方法。
- SplitScreenController的内部属性对象mStageCoordinator是在onOrganizerRegistered方法中被赋值的,该方法最初是被ShellInitImpl对象触发的。
4、来看下ShellInitImpl的相干代码。
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
- public class ShellInitImpl {
- private static final String TAG = ShellInitImpl.class.getSimpleName();
- private final Optional<SplitScreenController> mSplitScreenOptional;
- private final InitImpl mImpl = new InitImpl();
- public ShellInitImpl(
- DisplayController displayController,
- DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController,
- DragAndDropController dragAndDropController,
- ShellTaskOrganizer shellTaskOrganizer,
- Optional<BubbleController> bubblesOptional,
- Optional<SplitScreenController> splitScreenOptional,
- Optional<AppPairsController> appPairsOptional,
- Optional<PipTouchHandler> pipTouchHandlerOptional,
- FullscreenTaskListener fullscreenTaskListener,
- Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController,
- Optional<FreeformTaskListener> freeformTaskListenerOptional,
- Optional<RecentTasksController> recentTasks,
- Transitions transitions,
- StartingWindowController startingWindow,
- ShellExecutor mainExecutor) {
- ...代码省略...
- mSplitScreenOptional = splitScreenOptional;
- ...代码省略...
- }
- public ShellInit asShellInit() {
- return mImpl;
- }
- private void init() {
- ...代码省略...
- mSplitScreenOptional.ifPresent(SplitScreenController::onOrganizerRegistered);
- ...代码省略...
- }
- @ExternalThread
- private class InitImpl implements ShellInit {
- @Override
- public void init() {
- try {
- //进一步调用ShellInitImpl的Init方法。
- mMainExecutor.executeBlocking(() -> ShellInitImpl.this.init());
- } catch (InterruptedException e) {
- throw new RuntimeException("Failed to initialize the Shell in 2s", e);
- }
- }
- }
- }
复制代码 四、SystemUI模块初始化分屏组件
1、前面第三节第1步WMShellModule类中,我们有提到过和ShellInitImpl对象创建有关的代码。
- import com.android.wm.shell.splitscreen.SplitScreenController;
- @Module(includes = WMShellBaseModule.class)
- public class WMShellModule {
-
- @WMSingleton
- @Provides
- static ShellInit provideShellInit(ShellInitImpl impl) {
- //调用ShellInitImpl的asShellInit方法返回ShellInit对象实例
- return impl.asShellInit();
- }
-
- @WMSingleton
- @Provides
- static ShellInitImpl provideShellInitImpl(DisplayController displayController,
- DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController,
- DragAndDropController dragAndDropController,
- ShellTaskOrganizer shellTaskOrganizer,
- Optional<BubbleController> bubblesOptional,
- Optional<SplitScreenController> splitScreenOptional,
- Optional<AppPairsController> appPairsOptional,
- Optional<PipTouchHandler> pipTouchHandlerOptional,
- FullscreenTaskListener fullscreenTaskListener,
- Optional<FullscreenUnfoldController> appUnfoldTransitionController,
- Optional<FreeformTaskListener> freeformTaskListener,
- Optional<RecentTasksController> recentTasksOptional,
- Transitions transitions,
- StartingWindowController startingWindow,
- @ShellMainThread ShellExecutor mainExecutor) {
- //创建ShellInitImpl的对象实例
- return new ShellInitImpl(displayController,
- displayImeController,
- displayInsetsController,
- dragAndDropController,
- shellTaskOrganizer,
- bubblesOptional,
- splitScreenOptional,
- appPairsOptional,
- pipTouchHandlerOptional,
- fullscreenTaskListener,
- appUnfoldTransitionController,
- freeformTaskListener,
- recentTasksOptional,
- transitions,
- startingWindow,
- mainExecutor);
- }
- }
复制代码 2、前面第二节第4步WMComponent类中,我们有提到SystemUI模块在初始化SystemUI模块的组件之前,会先初始化WMShell模块的所有组件,这自然也包罗分屏组件。
- @WMSingleton//单例
- @Subcomponent(modules = {WMShellModule.class})
- public interface WMComponent {
- /**
- * Initializes all the WMShell components before starting any of the SystemUI components.
- * 在初始化SystemUI组件之前,优先初始化WMShell模块的所有组件
- */
- default void init() {
- //调用ShellInit的init
- getShellInit().init();
- }
- //获取ShellInit对象实例
- @WMSingleton
- ShellInit getShellInit();
- }
复制代码 WMComponent的init方法先是通过getShellInit方法获取到ShellInit对象实例,InitImpl实现了ShellInit这个接口,
并实现了init方法,该方法会进一步调用ShellInitImpl的init方法,终极会触发SplitScreenController的onOrganizerRegistered方法。
3、SplitScreenController的onOrganizerRegistered方法会创建控制分屏功能的分屏组件StageCoordinator的对象实例。
- public class SplitScreenController implements DragAndDropPolicy.Starter, RemoteCallable<SplitScreenController> {
- private StageCoordinator mStageCoordinator;
-
- //这个方法最初是被ShellInitImpl调用的
- public void onOrganizerRegistered() {
- if (mStageCoordinator == null) {
- //创建触发分屏功能的重要对象StageCoordinator的实例。
- mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
- mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,
- mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
- mIconProvider, mRecentTasksOptional, mUnfoldControllerProvider);
- }
- }
- }
复制代码 4、StageCoordinator的构造方法如下所示。
- class StageCoordinator implements SplitLayout.SplitLayoutHandler,
- RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler {
- StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
- RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
- DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool, SplitscreenEventLogger logger,
- Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
- mContext = context;
- mDisplayId = displayId;
- mSyncQueue = syncQueue;
- mRootTDAOrganizer = rootTDAOrganizer;
- mTaskOrganizer = taskOrganizer;
- mLogger = logger;
- mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
- mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
- //分屏对象
- mMainStage = new MainStage(
- mTaskOrganizer,
- mDisplayId,
- mMainStageListener,
- mSyncQueue,
- mSurfaceSession,
- mMainUnfoldController);
- //分屏对象
- mSideStage = new SideStage(
- mContext,
- mTaskOrganizer,
- mDisplayId,
- mSideStageListener,
- mSyncQueue,
- mSurfaceSession,
- mSideUnfoldController);
- mDisplayImeController = displayImeController;
- mDisplayInsetsController = displayInsetsController;
- mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSideStage);
- mRootTDAOrganizer.registerListener(displayId, this);
- final DeviceStateManager deviceStateManager =
- mContext.getSystemService(DeviceStateManager.class);
- deviceStateManager.registerCallback(taskOrganizer.getExecutor(),
- new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged));
- mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
- mOnTransitionAnimationComplete);
- transitions.addHandler(this);
- }
- }
复制代码 由此可知,SystemUI在进程初始化阶段就已经准备好分屏所必要的 MainStage和SideStage 对象,这两个对象很紧张,分别负责分屏的一边,对象内部会创建一个 RootTask 节点了(这里利用了WindowOrganizer框架的能力),这个RootTask就是分屏的关键,通过把应用的Task节点挂载到RootTask下,然后修改RootTask节点的Bounds来改变应用显示的巨细。
五、WMShell模块触发分屏
1、前面第三步第4节我们有做过分析,Launcher3经过层层调用,终极是调用StageCoordinator的startTasksWithLegacyTransition方法触发分屏功能的,继续来看下StageCoordinator的startTasksWithLegacyTransition方法。
- class StageCoordinator implements SplitLayout.SplitLayoutHandler,
- RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler {
-
- private final ShellTaskOrganizer mTaskOrganizer;
-
- StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
- RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
- DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool, SplitscreenEventLogger logger,
- IconProvider iconProvider,
- Optional<RecentTasksController> recentTasks,
- Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
- mContext = context;
- mDisplayId = displayId;
- mSyncQueue = syncQueue;
- mRootTDAOrganizer = rootTDAOrganizer;
- mTaskOrganizer = taskOrganizer;//为mTaskOrganizer赋值
- ...代码省略...
- }
- //Launcher3其实是调用了这个方法触发分屏模式的
- void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
- int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
- float splitRatio, RemoteAnimationAdapter adapter) {
- // Init divider first to make divider leash for remote animation target.
- setDividerVisibility(true /* visible */);//设置分屏中间的分割线View可见
- // Set false to avoid record new bounds with old task still on top;
- mShouldUpdateRecents = false;
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- final WindowContainerTransaction evictWct = new WindowContainerTransaction();
- prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct);
- prepareEvictChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, evictWct);
- // Need to add another wrapper here in shell so that we can inject the divider bar
- // and also manage the process elevation via setRunningRemote
- IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
- @Override
- public void onAnimationStart(@WindowManager.TransitionOldType int transit,
- RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers,
- RemoteAnimationTarget[] nonApps,
- final IRemoteAnimationFinishedCallback finishedCallback) {
- mIsDividerRemoteAnimating = true;
- RemoteAnimationTarget[] augmentedNonApps =
- new RemoteAnimationTarget[nonApps.length + 1];
- for (int i = 0; i < nonApps.length; ++i) {
- augmentedNonApps[i] = nonApps[i];
- }
- augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
- IRemoteAnimationFinishedCallback wrapCallback =
- new IRemoteAnimationFinishedCallback.Stub() {
- @Override
- public void onAnimationFinished() throws RemoteException {
- mIsDividerRemoteAnimating = false;
- mShouldUpdateRecents = true;
- mSyncQueue.queue(evictWct);
- mSyncQueue.runInSync(t -> applyDividerVisibility(t));
- finishedCallback.onAnimationFinished();
- }
- };
- try {
- try {
- ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
- adapter.getCallingApplication());
- } catch (SecurityException e) {
- Slog.e(TAG, "Unable to boost animation thread. This should only happen"
- + " during unit tests");
- }
- adapter.getRunner().onAnimationStart(transit, apps, wallpapers,
- augmentedNonApps, wrapCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error starting remote animation", e);
- }
- }
- @Override
- public void onAnimationCancelled() {
- mIsDividerRemoteAnimating = false;
- mShouldUpdateRecents = true;
- mSyncQueue.queue(evictWct);
- mSyncQueue.runInSync(t -> applyDividerVisibility(t));
- try {
- adapter.getRunner().onAnimationCancelled();
- } catch (RemoteException e) {
- Slog.e(TAG, "Error starting remote animation", e);
- }
- }
- };
- RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
- wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
- if (mainOptions == null) {
- mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
- } else {
- ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
- mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
- mainOptions = mainActivityOptions.toBundle();
- }
- sideOptions = sideOptions != null ? sideOptions : new Bundle();
- setSideStagePosition(sidePosition, wct);
- mSplitLayout.setDivideRatio(splitRatio);
- if (mMainStage.isActive()) {
- mMainStage.moveToTop(getMainStageBounds(), wct);
- } else {
- // 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对应的RootTask的Bounds,并将其移动到最前面
- mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
- }
- // 设置mSideStage对应的RootTask的Bounds,并将其移动到最前面
- mSideStage.moveToTop(getSideStageBounds(), wct);
- // Make sure the launch options will put tasks in the corresponding split roots
- // 配置launch task的option,让分屏应用的task启动到RootTask节点之下
- addActivityOptions(mainOptions, mMainStage);
- addActivityOptions(sideOptions, mSideStage);
- // Add task launch requests
- // 启动分屏应用,启动方式和从任务管理器启动是一样的,startActivityFromRecents
- wct.startTask(mainTaskId, mainOptions);
- wct.startTask(sideTaskId, sideOptions);
- // Using legacy transitions, so we can't use blast sync since it conflicts.
- // 所有修改封装到WindowContainerTransaction中然后通过WindowOrganizer框架完成上面的变化
- mTaskOrganizer.applyTransaction(wct);
- }
- }
复制代码
- 显示分屏中间的View
- 设置mMainStage对应的RootTask的Bounds并移动到最前面
- 设置mSideStage对应的RootTask的Bounds并移动到最前面
- 启动分屏应用,让分屏应用的task启动到RootTask节点之下,启动方式和从任务管理器启动是一样的,Framework侧对应的就是startActivityFromRecents方法
2、继续来看下ShellTaskOrganizer的applyTransaction方法。
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
- public class ShellTaskOrganizer extends TaskOrganizer implements
- CompatUIController.CompatUICallback {
- }
复制代码 frameworks/base/core/java/android/window/TaskOrganizer.java
- public class TaskOrganizer extends WindowOrganizer {
- }
复制代码 frameworks/base/core/java/android/window/TaskOrganizer.java
- public class WindowOrganizer {
- //applyTransaction是ShellTaskOrganizer的父类方法
- public void applyTransaction(@NonNull WindowContainerTransaction t) {
- try {
- if (!t.isEmpty()) {
- //调用IWindowOrganizerController的applyTransaction方法
- getWindowOrganizerController().applyTransaction(t);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- static IWindowOrganizerController getWindowOrganizerController() {
- return IWindowOrganizerControllerSingleton.get();
- }
-
- private static final Singleton<IWindowOrganizerController> IWindowOrganizerControllerSingleton =
- new Singleton<IWindowOrganizerController>() {
- @Override
- protected IWindowOrganizerController create() {
- try {
- return ActivityTaskManager.getService().getWindowOrganizerController();
- } catch (RemoteException e) {
- return null;
- }
- }
- };
- }
复制代码 applyTransaction方法终极是其父类WindowOrganizer的方法,该方法先是获取到WindowOrganizerController的实例对象,然后调用该对象的applySyncTransaction方法。
3、IWindowOrganizerController是一个aidl,该接口的具体实现类是WindowOrganizerController。
frameworks/base/core/java/android/window/IWindowOrganizerController.aidl
- interface IWindowOrganizerController {
- int applySyncTransaction(in WindowContainerTransaction t,
- in IWindowContainerTransactionCallback callback);
- }
复制代码 frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java
- class WindowOrganizerController extends IWindowOrganizerController.Stub
- implements BLASTSyncEngine.TransactionReadyListener {
-
- @Override
- public int applySyncTransaction(WindowContainerTransaction t,
- IWindowContainerTransactionCallback callback) {
- if (t == null) {
- throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
- }
- enforceTaskPermission("applySyncTransaction()");
- final CallerInfo caller = new CallerInfo();
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- int syncId = -1;
- if (callback != null) {
- syncId = startSyncWithOrganizer(callback);
- }
- applyTransaction(t, syncId, null /*transition*/, caller);
- if (syncId >= 0) {
- setSyncReady(syncId);
- }
- return syncId;
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
复制代码 这里还是运用了WindowOrganizer框架的能力,把所有修改点封装到 WindowContainerTransaction中,然后通过mTaskOrganizer.applyTransaction(wct); 转交给Framework,Framework剖析WindowContainerTransaction,然后实行对应的厘革
我们可以看看WindowContainerTransaction的内容
Android12上两个应用都得是从任务管理器中起 startActivityFromRecents
在Android13上支持通过wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions)新起一个应用。
六、通过命令行触发分屏
1、除了调用WMShell模块组件提供的方法触发分屏意外,我们还可以通过命令行来触发分屏。
- // taskId 可以通过adb shell am stack list 来查看应用对应的taskId
- // SideStagePosition 0 代表左边, 1 代表右边
- adb shell dumpsys activity service SystemUIService WMShell moveToSideStage <taskId> <SideStagePosition>
复制代码 命令行会把taskId对应的task挂载到SideStage对应的RootTask下,然后SideStage监听到task厘革,然后就会激活MainStage,然后申请分屏操纵。
2、这部分代码如下。
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
- class StageCoordinator implements SplitLayout.SplitLayoutHandler,
- RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler {
- private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
- final boolean hasChildren = stageListener.mHasChildren;
- final boolean isSideStage = stageListener == mSideStageListener;
- if (!hasChildren) {
- if (isSideStage && mMainStageListener.mVisible) {
- // Exit to main stage if side stage no longer has children.
- exitSplitScreen(mMainStage, EXIT_REASON_APP_FINISHED);
- } else if (!isSideStage && mSideStageListener.mVisible) {
- // Exit to side stage if main stage no longer has children.
- exitSplitScreen(mSideStage, EXIT_REASON_APP_FINISHED);
- }
- } else if (isSideStage) {
- //SideStage对应的RootTask监听到task变化,然后就会触发分屏操作
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- //Make sure the main stage is active.
- //这里的reparent是关键,为true后会把后台的Task作为分屏的一部分,如果没有后台task,不能触发分屏
- mMainStage.activate(getMainStageBounds(), wct, true /* reparent */);
- mSideStage.moveToTop(getSideStageBounds(), wct);
- mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t -> updateSurfaceBounds(mSplitLayout, t));
- }
- if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
- mShouldUpdateRecents = true;
- updateRecentTasksSplitPair();
- if (!mLogger.hasStartedSession()) {
- mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
- getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLandscape());
- }
- }
- }
-
- }
复制代码 这里必要注意 mMainStage.activate(getMainStageBounds(), wct, true /* reparent */ ); 这里的reparent是关键,为true后会把后台的Task作为分屏的一部分,如果没有后台task,不能触发分屏,而且命令行分屏由于缺少了Launcher3的加入,缺少分屏之前的动画,效果上就是直接硬切的。
参考文档:https://juejin.cn/post/7346977510514884619
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |