做網(wǎng)站哪家公司比較好網(wǎng)站流量排行
前言
上一篇我們具體分析了系統(tǒng)處于多窗口模式下,Android應(yīng)用和多窗口模式相關(guān)方法的調(diào)用順序,對(duì)于應(yīng)用如何適配多窗口模式有了一個(gè)初步的認(rèn)識(shí),本篇文章我們將會(huì)結(jié)合Android12系統(tǒng)源碼,具體來梳理一下系統(tǒng)是如何觸發(fā)多窗口分屏模式,以及實(shí)現(xiàn)多窗口分屏模式功能的原理。
一、Launcher3觸發(fā)分屏
1、Android12的分屏模式觸發(fā)入口,默認(rèn)是在最近任務(wù)列表中的,而最近任務(wù)列表是包含在Launcher3里面的,當(dāng)我們?cè)谧罱蝿?wù)列表中點(diǎn)擊分屏按鈕后,會(huì)先觸發(fā)Launcher進(jìn)入分屏的一系列懸浮動(dòng)畫以及初始的圖標(biāo)分屏。
以上步驟都屬于Launcher的業(yè)務(wù)邏輯。
2、接下來我們結(jié)合系統(tǒng)源碼來簡(jiǎn)單看下Launcher3模塊是如何觸發(fā)分屏功能的。
packages/apps/Launcher3/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
public class QuickstepLauncher extends BaseQuickstepLauncher {@Overridepublic 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) {//調(diào)用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@Overridepublic 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//調(diào)用RecentsView的getSplitPlaceholder方法,獲取SplitSelectStateController對(duì)象實(shí)例,調(diào)用launchTasks方法recentsView.getSplitPlaceholder().launchTasks(this /*groupedTaskView*/,success -> endCallback.executeAllAndDestroy(),false /* freezeTaskList */);// Callbacks get run from recentsView for case when recents animation already runningrecentsView.addSideTaskLaunchCallback(endCallback);return endCallback;}@Overridepublic void launchTask(@NonNull Consumer<Boolean> callback, boolean freezeTaskList) {//調(diào)用RecentsView的getSplitPlaceholder方法,獲取SplitSelectStateController對(duì)象實(shí)例,調(diào)用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 screenfinal 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);//轉(zhuǎn)場(chǎng)動(dòng)畫final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(RemoteAnimationAdapterCompat.wrapRemoteAnimationRunner(animationRunner),300, 150,ActivityThread.currentActivityThread().getApplicationThread());ActivityOptions mainOpts = ActivityOptions.makeBasic();if (freezeTaskList) {mainOpts.setFreezeRecentTasksReordering();}//調(diào)用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;...代碼省略...}/*** 分屏模式同時(shí)打開多個(gè)任務(wù)*/public void startTasksWithLegacyTransition(int mainTaskId, Bundle mainOptions, int sideTaskId,Bundle sideOptions, @SplitConfigurationOptions.StagePosition int sidePosition,float splitRatio, RemoteAnimationAdapter adapter) {if (mSystemUiProxy != null) {try {//調(diào)用ISplitScreen的startTasksWithLegacyTransition方法觸發(fā)分屏mSplitScreen.startTasksWithLegacyTransition(mainTaskId, mainOptions, sideTaskId,sideOptions, sidePosition, splitRatio, adapter);} catch (RemoteException e) {Log.w(TAG, "Failed call startTasksWithLegacyTransition");}}}}
通過梳理以上代碼,可以發(fā)現(xiàn)Launche3最終是通過調(diào)用SystemUiProxy的startTasksWithLegacyTransition方法觸發(fā)分屏的,而該方法內(nèi)部又進(jìn)一步調(diào)用了類型為ISplitScreen的mSplitScreen對(duì)象的startTasksWithLegacyTransition方法。
3、SystemUiProxy的內(nèi)部屬性對(duì)象mSplitScreen最初是在TouchInteractionService的內(nèi)部類TISBinder的onInitialize方法中被賦值的。
packages/apps/Launcher3/quickstep/src/com/android/quickstep/TouchInteractionService.java
public class TouchInteractionService extends Serviceimplements ProtoTraceable<LauncherTraceProto.Builder> {private final TISBinder mTISBinder = new TISBinder();public class TISBinder extends IOverviewProxy.Stub {@BinderThreadpublic 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));//觸發(fā)分屏就是調(diào)用的這個(gè)對(duì)象的方法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(() -> {//調(diào)用SystemUiProxy的setProxy方法SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,splitscreen, onehanded, shellTransitions, startingWindow, recentTasks,smartspaceTransitionController);TouchInteractionService.this.initInputMonitor();preloadOverview(true /* fromInit */);});sIsInitialized = true;}}@Overridepublic 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的一個(gè)服務(wù),內(nèi)部類TISBinder就是其他模塊綁定TouchInteractionService服務(wù)時(shí)候所返回的IBinder類型的實(shí)例對(duì)象。
二、SystemUI觸發(fā)分屏
1、默認(rèn)情況下,SystemUI模塊對(duì)Launcher3模塊的TouchInteractionService服務(wù)進(jìn)行了綁定。
frameworks/base/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
public class OverviewProxyService extends CurrentUserTracker implementsCallbackController<OverviewProxyListener>, NavigationModeController.ModeChangedListener,Dumpable {private final Optional<SplitScreen> mSplitScreenOptional;//觸發(fā)分屏模式的關(guān)鍵對(duì)象//喚起Launcher3模塊TouchInteractionService的Actionprivate static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";//喚起Launcher3模塊TouchInteractionService的Intentprivate final Intent mQuickStepIntent;//遠(yuǎn)程IPC通信是實(shí)現(xiàn)類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);...代碼省略...//獲取最近應(yīng)用列表組件名稱,其實(shí)就是Launcher3的包名mRecentsComponentName = ComponentName.unflattenFromString(context.getString(com.android.internal.R.string.config_recentsComponentName));//創(chuàng)建最近應(yīng)用列表Activity的意圖對(duì)象mQuickStepIntent = new Intent(ACTION_QUICKSTEP).setPackage(mRecentsComponentName.getPackageName());...代碼省略...startConnectionToCurrentUser();...代碼省略...}//成功綁定服務(wù)所返回的ServiceConnection對(duì)象private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {@Overridepublic 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()));//關(guān)鍵對(duì)象,Optional對(duì)象的的ifPresent方法會(huì)判斷該對(duì)象內(nèi)部的SplitScreen實(shí)例對(duì)象是否為空,//不為空則執(zhí)行回調(diào)方法,也就是把splitscreen對(duì)象實(shí)例存放到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 {//調(diào)用mOverviewProxy的onInitialize,為相關(guān)參數(shù)進(jìn)行服務(wù)mOverviewProxy.onInitialize(params);} catch (RemoteException e) {mCurrentBoundedUserId = -1;Log.e(TAG_OPS, "ServiceConnection Failed to call onInitialize()", e);}dispatchNavButtonBounds();// Force-update the systemui state flagsupdateSystemUiStateFlags();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 {//綁定服務(wù)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類的構(gòu)造方法會(huì)對(duì)Launche3模塊的TouchInteractionService服務(wù)進(jìn)行綁定,并把調(diào)用該服務(wù)返回的Binder對(duì)象的onInitialize,將Launcher3模塊需要的相關(guān)參數(shù)傳了過去,這樣Launch3模塊才能拿到ISplitScreen的實(shí)例對(duì)象,通過調(diào)用該實(shí)例對(duì)象的startTasksWithLegacyTransition方法,最終觸發(fā)分屏模式。那么問題有來了,OverviewProxyService里面的ISplitScreen對(duì)象實(shí)例是如何被賦值的?
2、重新再來看下OverviewProxyService的構(gòu)造方法,這次我們重點(diǎn)關(guān)注一下mSplitScreenOptional這個(gè)對(duì)象。
public class OverviewProxyService extends CurrentUserTracker implementsCallbackController<OverviewProxyListener>, NavigationModeController.ModeChangedListener,Dumpable {private final Optional<SplitScreen> mSplitScreenOptional;//觸發(fā)分屏模式的關(guān)鍵對(duì)象@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() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {...代碼省略...mSplitScreenOptional.ifPresent((splitscreen) -> params.putBinder(KEY_EXTRA_SHELL_SPLIT_SCREEN,//這里調(diào)用splitscreen的createExternalInterface方法splitscreen.createExternalInterface().asBinder()));...代碼省略...}}
}
OverviewProxyService的構(gòu)造方法有一個(gè)關(guān)鍵注解 @Inject,這個(gè)注解是Dagger2的框架注解,該框架會(huì)根據(jù)我們的配置,當(dāng)我們需要在某個(gè)對(duì)象的構(gòu)造方法中傳入特定參數(shù)對(duì)象的時(shí)候,只要添加@Inject注解,該框架會(huì)自動(dòng)幫我們創(chuàng)建參數(shù)對(duì)象并傳入。關(guān)于這個(gè)框架的原理,我在Android 12系統(tǒng)源碼_SystemUI(一)SystemUI的啟動(dòng)流程這篇博客具體分析過,這里不做過多解釋。
3、由于后續(xù)會(huì)多次提到Optional這種類型的數(shù)據(jù)類型,這里我們需要先簡(jiǎn)單看下這個(gè)類的相關(guān)代碼。
public final class Optional<T> {private static final Optional<?> EMPTY = new Optional<>();//內(nèi)部包含的真正對(duì)象private final T value;private Optional(T value) {this.value = Objects.requireNonNull(value);}//如果內(nèi)部對(duì)象不為空,則執(zhí)行consumer方法public void ifPresent(Consumer<? super T> consumer) {if (value != null)consumer.accept(value);}//如果內(nèi)部對(duì)象為空,則返回空對(duì)象,執(zhí)行mapper方法,并將該方法返回的對(duì)象封裝成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、關(guān)于Optional這個(gè)對(duì)象的dagger2框架的配置信息,SystemUI配置在WMComponent這個(gè)接口里面的。
frameworks/base/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
import com.android.wm.shell.dagger.WMShellModule;@WMSingleton//單例
@Subcomponent(modules = {WMShellModule.class})//需要進(jìn)一步結(jié)合WMShellModule做分析
public interface WMComponent {/*** Initializes all the WMShell components before starting any of the SystemUI components.* 在初始化SystemUI組件之前,優(yōu)先初始化WMShell模塊的所有組件*/default void init() {//調(diào)用ShellInit的init,這個(gè)方法需要額外關(guān)注一下,后續(xù)我們會(huì)再次提到getShellInit().init();}//獲取ShellInit對(duì)象實(shí)例@WMSingletonShellInit getShellInit();//獲取Optional<SplitScreen>對(duì)象實(shí)例@WMSingletonOptional<SplitScreen> getSplitScreen();}
- 有了以上配置信息,SystemUI模塊的任何類的構(gòu)造方法只要加上 @Inject注解,我們就可以在該對(duì)象的構(gòu)造方法中拿到WMComponent 中返回的對(duì)象實(shí)例了。
- 結(jié)合getShellInit方法和init方法我們可以知道,SystemUI模塊在初始化該模塊的SystemUI組件之前,會(huì)先初始化WMShell模塊的組件,這就意味著SystemUI模塊的組件都能拿到WMShell模塊的組件,并調(diào)用對(duì)應(yīng)的組件所提供的功能。
- 而Optional到底是如何被創(chuàng)建出來的,這就需要我們進(jìn)一步查看WMComponent的類注解@Subcomponent指向的WMShellModule這個(gè)類的相關(guān)代碼了。
三、WMShell模塊觸發(fā)分屏
1、SystemUI模塊最終是通過WindowManager模塊下的Shell模塊觸發(fā)分屏功能的,來看下前面SystemUI模塊中dagger2注解框架引用到的WMShellModule這個(gè)類。
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
import com.android.wm.shell.splitscreen.SplitScreenController;@Module(includes = WMShellBaseModule.class)//需要進(jìn)一步結(jié)合WMShellBaseModule做分析
public class WMShellModule {@WMSingleton@Provides@DynamicOverridestatic 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) {//創(chuàng)建SplitScreenController對(duì)象實(shí)例return new SplitScreenController(shellTaskOrganizer, syncQueue, context,rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,displayInsetsController, transitions, transactionPool, iconProvider,recentTasks, stageTaskUnfoldControllerProvider);}//這個(gè)方法我們需要關(guān)注一下,后面會(huì)提到@WMSingleton@Providesstatic ShellInit provideShellInit(ShellInitImpl impl) {//調(diào)用ShellInitImpl的asShellInit方法返回ShellInit對(duì)象實(shí)例return impl.asShellInit();}@WMSingleton@Providesstatic 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) {//創(chuàng)建ShellInitImpl的對(duì)象實(shí)例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對(duì)象實(shí)例是如何被創(chuàng)建的,我們需要進(jìn)一步結(jié)合WMShellBaseModule做分析。
2、WMShellBaseModule的關(guān)鍵代碼如下所示。
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@Module(includes = WMShellConcurrencyModule.class)
public abstract class WMShellBaseModule {@WMSingleton@Providesstatic Optional<SplitScreen> provideSplitScreen(Optional<SplitScreenController> splitScreenController) {//結(jié)合前面Optional<T>這個(gè)類的代碼可以知道,調(diào)用splitScreenController對(duì)象的asSplitScreen方法,并將該方法返回的SplitScreen對(duì) 象實(shí)例封裝成Optional<SplitScreen>類型的對(duì)象再返回。return splitScreenController.map((controller) -> controller.asSplitScreen());}@WMSingleton@Providesstatic Optional<SplitScreenController> providesSplitScreenController(@DynamicOverride Optional<SplitScreenController> splitscreenController,Context context) {//AMS是否支持多窗口模式,支持才返回SplitScreenController對(duì)象實(shí)例,否則返回空if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {return splitscreenController;}return Optional.empty();}
}
- WMShellBaseModule的provideSplitScreen方法先是獲取SplitScreenController對(duì)象實(shí)例,該對(duì)象是通過WMShellModule的provideSplitScreenController方法創(chuàng)建,但是會(huì)經(jīng)過providesSplitScreenController做一層封裝,只有當(dāng)系統(tǒng)開啟了支持多窗口模式的開關(guān),也就是AMS支持多窗口模式的時(shí)候,才能拿到該對(duì)象實(shí)例,否則拿到的都是空
- provideSplitScreen方法在得到該對(duì)象實(shí)例后,通過調(diào)用該對(duì)象的asSplitScreen方法,得到了SplitScreen對(duì)象實(shí)例,但是最終返回的是封裝成Optional類型的對(duì)象實(shí)例返回的。
到這里我們終于可以確定是SplitScreenController的asSplitScreen方法創(chuàng)建了SplitScreen對(duì)象實(shí)例。
3、接下來我們繼續(xù)來梳理一下ISplitScreen和SplitScreenController類相關(guān)的代碼。
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;}//這個(gè)方法最初是被ShellInitImpl調(diào)用的public void onOrganizerRegistered() {if (mStageCoordinator == null) {//創(chuàng)建觸發(fā)分屏功能的重要對(duì)象StageCoordinator的實(shí)例。mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,mIconProvider, mRecentTasksOptional, mUnfoldControllerProvider);}} //SplitScreen是一個(gè)接口,具體實(shí)現(xiàn)是內(nèi)部類SplitScreenImpl@ExternalThreadprivate class SplitScreenImpl implements SplitScreen {private ISplitScreenImpl mISplitScreen;private final ArrayMap<SplitScreenListener, Executor> mExecutors = new ArrayMap<>();private final SplitScreen.SplitScreenListener mListener = new SplitScreenListener() {@Overridepublic void onStagePositionChanged(int stage, int position) {...代碼省略...}@Overridepublic void onTaskStageChanged(int taskId, int stage, boolean visible) {...代碼省略...}@Overridepublic void onSplitVisibilityChanged(boolean visible) {...代碼省略...}};@Overridepublic ISplitScreen createExternalInterface() {if (mISplitScreen != null) {mISplitScreen.invalidate();}mISplitScreen = new ISplitScreenImpl(SplitScreenController.this);//返回實(shí)現(xiàn)了ISplitScreen接口的對(duì)象實(shí)例return mISplitScreen;}}//ISplitScreen是一個(gè)aidl,內(nèi)部類ISplitScreenImpl實(shí)現(xiàn)了ISplitScreen的接口方法。@BinderThreadprivate static class ISplitScreenImpl extends ISplitScreen.Stub {private SplitScreenController mController;private final SingleInstanceRemoteListener<SplitScreenController,ISplitScreenListener> mListener;private final SplitScreen.SplitScreenListener mSplitScreenListener =new SplitScreen.SplitScreenListener() {@Overridepublic void onStagePositionChanged(int stage, int position) {mListener.call(l -> l.onStagePositionChanged(stage, position));}@Overridepublic 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;}@Overridepublic void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,float splitRatio, RemoteAnimationAdapter adapter) {//這里顯示進(jìn)行權(quán)限確認(rèn),然后會(huì)調(diào)用StageCoordinator的startTasksWithLegacyTransition方法。executeRemoteCallWithTaskPermission(mController, "startTasks",(controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,splitRatio, adapter));}}}
- SplitScreenController的asSplitScreen方法返回了該類的一個(gè)內(nèi)部對(duì)象SplitScreenImpl,SplitScreenImpl實(shí)現(xiàn)了SplitScreen這個(gè)接口的相關(guān)方法。
- 前面第二節(jié)第1步OverviewProxyService類中我們有提到,SystemUI在成功綁定Launcher3模塊的TouchInteractionService服務(wù)的時(shí)候,調(diào)用了SplitScreen 的createExternalInterface方法,結(jié)合這里我們可以知道此方法返回ISplitScreenImpl對(duì)象實(shí)例,此對(duì)象實(shí)現(xiàn)了ISplitScreen.aidl文件中聲明的接口方法,Launcher3最終得以跨進(jìn)程調(diào)用ISplitScreenImpl的startTasksWithLegacyTransition方法,最終觸發(fā)分屏模式。
- ISplitScreenImpl的startTasksWithLegacyTransition方法內(nèi)部先是做了個(gè)權(quán)限判斷,最終是調(diào)用了SplitScreenController的類型為StageCoordinator的內(nèi)部對(duì)象mStageCoordinator的startTasksWithLegacyTransition方法。
- SplitScreenController的內(nèi)部屬性對(duì)象mStageCoordinator是在onOrganizerRegistered方法中被賦值的,該方法最初是被ShellInitImpl對(duì)象觸發(fā)的。
4、來看下ShellInitImpl的相關(guān)代碼。
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);...代碼省略...}@ExternalThreadprivate class InitImpl implements ShellInit {@Overridepublic void init() {try {//進(jìn)一步調(diào)用ShellInitImpl的Init方法。mMainExecutor.executeBlocking(() -> ShellInitImpl.this.init());} catch (InterruptedException e) {throw new RuntimeException("Failed to initialize the Shell in 2s", e);}}}
}
四、SystemUI模塊初始化分屏組件
1、前面第三節(jié)第1步WMShellModule類中,我們有提到過和ShellInitImpl對(duì)象創(chuàng)建有關(guān)的代碼。
import com.android.wm.shell.splitscreen.SplitScreenController;@Module(includes = WMShellBaseModule.class)
public class WMShellModule {@WMSingleton@Providesstatic ShellInit provideShellInit(ShellInitImpl impl) {//調(diào)用ShellInitImpl的asShellInit方法返回ShellInit對(duì)象實(shí)例return impl.asShellInit();}@WMSingleton@Providesstatic 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) {//創(chuàng)建ShellInitImpl的對(duì)象實(shí)例return new ShellInitImpl(displayController,displayImeController,displayInsetsController,dragAndDropController,shellTaskOrganizer,bubblesOptional,splitScreenOptional,appPairsOptional,pipTouchHandlerOptional,fullscreenTaskListener,appUnfoldTransitionController,freeformTaskListener,recentTasksOptional,transitions,startingWindow,mainExecutor);}
}
2、前面第二節(jié)第4步WMComponent類中,我們有提到SystemUI模塊在初始化SystemUI模塊的組件之前,會(huì)先初始化WMShell模塊的所有組件,這自然也包括分屏組件。
@WMSingleton//單例
@Subcomponent(modules = {WMShellModule.class})
public interface WMComponent {/*** Initializes all the WMShell components before starting any of the SystemUI components.* 在初始化SystemUI組件之前,優(yōu)先初始化WMShell模塊的所有組件*/default void init() {//調(diào)用ShellInit的initgetShellInit().init();}//獲取ShellInit對(duì)象實(shí)例@WMSingletonShellInit getShellInit();}
WMComponent的init方法先是通過getShellInit方法獲取到ShellInit對(duì)象實(shí)例,InitImpl實(shí)現(xiàn)了ShellInit這個(gè)接口,
并實(shí)現(xiàn)了init方法,該方法會(huì)進(jìn)一步調(diào)用ShellInitImpl的init方法,最終會(huì)觸發(fā)SplitScreenController的onOrganizerRegistered方法。
3、SplitScreenController的onOrganizerRegistered方法會(huì)創(chuàng)建控制分屏功能的分屏組件StageCoordinator的對(duì)象實(shí)例。
public class SplitScreenController implements DragAndDropPolicy.Starter, RemoteCallable<SplitScreenController> {private StageCoordinator mStageCoordinator;//這個(gè)方法最初是被ShellInitImpl調(diào)用的public void onOrganizerRegistered() {if (mStageCoordinator == null) {//創(chuàng)建觸發(fā)分屏功能的重要對(duì)象StageCoordinator的實(shí)例。mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,mIconProvider, mRecentTasksOptional, mUnfoldControllerProvider);}}
}
4、StageCoordinator的構(gòu)造方法如下所示。
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);//分屏對(duì)象mMainStage = new MainStage(mTaskOrganizer,mDisplayId,mMainStageListener,mSyncQueue,mSurfaceSession,mMainUnfoldController);//分屏對(duì)象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在進(jìn)程初始化階段就已經(jīng)準(zhǔn)備好分屏所需要的 MainStage和SideStage 對(duì)象,這兩個(gè)對(duì)象很重要,分別負(fù)責(zé)分屏的一邊,對(duì)象內(nèi)部會(huì)創(chuàng)建一個(gè) RootTask 節(jié)點(diǎn)了(這里利用了WindowOrganizer框架的能力),這個(gè)RootTask就是分屏的關(guān)鍵,通過把應(yīng)用的Task節(jié)點(diǎn)掛載到RootTask下,然后修改RootTask節(jié)點(diǎn)的Bounds來改變應(yīng)用顯示的大小。
五、WMShell模塊觸發(fā)分屏
1、前面第三步第4節(jié)我們有做過分析,Launcher3經(jīng)過層層調(diào)用,最終是調(diào)用StageCoordinator的startTasksWithLegacyTransition方法觸發(fā)分屏功能的,繼續(xù)來看下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其實(shí)是調(diào)用了這個(gè)方法觸發(fā)分屏模式的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 */);//設(shè)置分屏中間的分割線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 setRunningRemoteIRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {@Overridepublic 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() {@Overridepublic 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);}}@Overridepublic 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.// 設(shè)置mMainStage對(duì)應(yīng)的RootTask的Bounds,并將其移動(dòng)到最前面mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);}// 設(shè)置mSideStage對(duì)應(yīng)的RootTask的Bounds,并將其移動(dòng)到最前面mSideStage.moveToTop(getSideStageBounds(), wct);// Make sure the launch options will put tasks in the corresponding split roots// 配置launch task的option,讓分屏應(yīng)用的task啟動(dòng)到RootTask節(jié)點(diǎn)之下addActivityOptions(mainOptions, mMainStage);addActivityOptions(sideOptions, mSideStage);// Add task launch requests// 啟動(dòng)分屏應(yīng)用,啟動(dòng)方式和從任務(wù)管理器啟動(dòng)是一樣的,startActivityFromRecentswct.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
- 設(shè)置mMainStage對(duì)應(yīng)的RootTask的Bounds并移動(dòng)到最前面
- 設(shè)置mSideStage對(duì)應(yīng)的RootTask的Bounds并移動(dòng)到最前面
- 啟動(dòng)分屏應(yīng)用,讓分屏應(yīng)用的task啟動(dòng)到RootTask節(jié)點(diǎn)之下,啟動(dòng)方式和從任務(wù)管理器啟動(dòng)是一樣的,Framework側(cè)對(duì)應(yīng)的就是startActivityFromRecents方法
2、繼續(xù)來看下ShellTaskOrganizer的applyTransaction方法。
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
public class ShellTaskOrganizer extends TaskOrganizer implementsCompatUIController.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()) {//調(diào)用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>() {@Overrideprotected IWindowOrganizerController create() {try {return ActivityTaskManager.getService().getWindowOrganizerController();} catch (RemoteException e) {return null;}}};
}
applyTransaction方法最終是其父類WindowOrganizer的方法,該方法先是獲取到WindowOrganizerController的實(shí)例對(duì)象,然后調(diào)用該對(duì)象的applySyncTransaction方法。
3、IWindowOrganizerController是一個(gè)aidl,該接口的具體實(shí)現(xiàn)類是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.Stubimplements BLASTSyncEngine.TransactionReadyListener {@Overridepublic 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);}}}
這里還是運(yùn)用了WindowOrganizer框架的能力,把所有修改點(diǎn)封裝到 WindowContainerTransaction中,然后通過mTaskOrganizer.applyTransaction(wct); 轉(zhuǎn)交給Framework,Framework解析WindowContainerTransaction,然后執(zhí)行對(duì)應(yīng)的變化
我們可以看看WindowContainerTransaction的內(nèi)容
Android12上兩個(gè)應(yīng)用都得是從任務(wù)管理器中起 startActivityFromRecents
在Android13上支持通過wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions)新起一個(gè)應(yīng)用。
六、通過命令行觸發(fā)分屏
1、除了調(diào)用WMShell模塊組件提供的方法觸發(fā)分屏意外,我們還可以通過命令行來觸發(fā)分屏。
// taskId 可以通過adb shell am stack list 來查看應(yīng)用對(duì)應(yīng)的taskId
// SideStagePosition 0 代表左邊, 1 代表右邊
adb shell dumpsys activity service SystemUIService WMShell moveToSideStage <taskId> <SideStagePosition>
命令行會(huì)把taskId對(duì)應(yīng)的task掛載到SideStage對(duì)應(yīng)的RootTask下,然后SideStage監(jiān)聽到task變化,然后就會(huì)激活MainStage,然后申請(qǐng)分屏操作。
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對(duì)應(yīng)的RootTask監(jiān)聽到task變化,然后就會(huì)觸發(fā)分屏操作final WindowContainerTransaction wct = new WindowContainerTransaction();//Make sure the main stage is active.//這里的reparent是關(guān)鍵,為true后會(huì)把后臺(tái)的Task作為分屏的一部分,如果沒有后臺(tái)task,不能觸發(fā)分屏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是關(guān)鍵,為true后會(huì)把后臺(tái)的Task作為分屏的一部分,如果沒有后臺(tái)task,不能觸發(fā)分屏,而且命令行分屏由于缺少了Launcher3的參與,缺少分屏之前的動(dòng)畫,效果上就是直接硬切的。
參考文檔:https://juejin.cn/post/7346977510514884619