1. 関連する TaskView を構築し、対応する ViewGroup にロードします。
ステーション b での無料ビデオチュートリアル説明:
https://www.bilibili.com/video/BV1wj411o7A9/
//packages/apps/Car/Launcher/src/com/android/car/carlauncher/CarLauncher.java
void onCreate() {
//ignore
setContentView(R.layout.car_launcher);
// We don't want to show Map card unnecessarily for the headless user 0.
if (!UserHelperLite.isHeadlessSystemUser(getUserId())) {
ViewGroup mapsCard = findViewById(R.id.maps_card);
if (mapsCard != null) {
//这里获取了mapsCard后,接下来就是构造出对应的TaskView
setUpTaskView(mapsCard);
}
}
//ignore
}
//进行TaskView相关的设置
private void setUpTaskView(ViewGroup parent) {
//把TaskViewManager进行构造
mTaskViewManager = new TaskViewManager(this,
new HandlerExecutor(getMainThreadHandler()), mCarActivityManagerRef);
//创建对应的TaskView
mTaskViewManager.createTaskView(taskView -> {
taskView.setListener(getMainExecutor(), mTaskViewListener);
//把TaskView添加到了mapsCard
parent.addView(taskView);
mTaskView = taskView;
});
}
//packages/apps/Car/Launcher/src/com/android/car/carlauncher/TaskViewManager.java
public TaskViewManager(@UiContext Context context, HandlerExecutor handlerExecutor,
AtomicReference<CarActivityManager> carActivityManagerRef) {
mContext = context;
mExecutor = handlerExecutor;
//构造对应的ShellTaskOrganizer
mTaskOrganizer = new ShellTaskOrganizer(mExecutor, mContext);
//把TaskOrganizer进行对应的注册
initTaskOrganizer(carActivityManagerRef, transactionPool);
}
//构造出TaskView
void createTaskView(Consumer<TaskView> onCreate) {
CarTaskView taskView = new CarTaskView(mContext, mTaskOrganizer, mSyncQueue);
mExecutor.execute(() -> {
onCreate.accept(taskView);
});
}
概要:
1. 主に TaskOrganizer の登録と初期化を目的として、対応する TaskViewManager を構築します。
2. 関連する TaskView (SurfaceView) を作成し、CarLauncher の ViewGroup に配置します。
タイミング図は次のとおりです。
2. 関連活動の開始
アクティビティ関連のスタックを開始する
07-26 17:25:48.075 1584 1584 I lsm3333 : TaskView startActivity
07-26 17:25:48.075 1584 1584 I lsm3333 : java.lang.Exception
07-26 17:25:48.075 1584 1584 I lsm3333 : at com.android.wm.shell.TaskView.startActivity(TaskView.java:179)
07-26 17:25:48.075 1584 1584 I lsm3333 : at com.android.car.carlauncher.CarLauncher.startMapsInTaskView(CarLauncher.java:373)
07-26 17:25:48.075 1584 1584 I lsm3333 : at com.android.car.carlauncher.CarLauncher.-$$Nest$mstartMapsInTaskView(Unknown Source:0)
07-26 17:25:48.075 1584 1584 I lsm3333 : at com.android.car.carlauncher.CarLauncher$1.onInitialized(CarLauncher.java:108)
07-26 17:25:48.075 1584 1584 I lsm3333 : at com.android.wm.shell.TaskView.lambda$surfaceCreated$11$com-android-wm-shell-TaskView(TaskView.java:416)
07-26 17:25:48.075 1584 1584 I lsm3333 : at com.android.wm.shell.TaskView$$ExternalSyntheticLambda11.run(Unknown Source:2)
07-26 17:25:48.075 1584 1584 I lsm3333 : at android.os.Handler.handleCallback(Handler.java:942)
07-26 17:25:48.075 1584 1584 I lsm3333 : at android.os.Handler.dispatchMessage(Handler.java:99)
07-26 17:25:48.075 1584 1584 I lsm3333 : at android.os.Looper.loopOnce(Looper.java:201)
07-26 17:25:48.075 1584 1584 I lsm3333 : at android.os.Looper.loop(Looper.java:288)
07-26 17:25:48.075 1584 1584 I lsm3333 : at android.app.ActivityThread.main(ActivityThread.java:7898)
07-26 17:25:48.075 1584 1584 I lsm3333 : at java.lang.reflect.Method.invoke(Native Method)
07-26 17:25:48.075 1584 1584 I lsm3333 : at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
07-26 17:25:48.075 1584 1584 I lsm3333 : at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
主なコード フローの手順は次のとおりです。
TaskView.onInitialized -->CarLauncher.startMapsInTaskView -->
TaskView.startActivity -->TaskView.prepareActivityOptions -->PendingIntent.send
関連するシーケンス図は次のとおりです。
3. タスクの開始後、関連する表面の再マウント操作を実行します。
タスクが開始されると、関連する onTaskAppeared コールバック:
関連スタック:
07-26 17:33:03.205 2931 2931 I lsm3333 : onTaskAppeared taskInfo = TaskInfo{
userId=10 taskId=1000013 displayId=0 isRunning=true baseIntent=Intent {
act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.android.car.mapsplaceholder/.MapsPlaceholderActivity } baseActivity=ComponentInfo{
com.android.car.mapsplaceholder/com.android.car.mapsplaceholder.MapsPlaceholderActivity} topActivity=ComponentInfo{
com.android.car.mapsplaceholder/com.android.car.mapsplaceholder.MapsPlaceholderActivity} origActivity=null realActivity=ComponentInfo{
com.android.car.mapsplaceholder/com.android.car.mapsplaceholder.MapsPlaceholderActivity} numActivities=1 lastActiveTime=462943 supportsSplitScreenMultiWindow=false supportsMultiWindow=true resizeMode=2 isResizeable=true minWidth=-1 minHeight=-1 defaultMinSize=220 token=WCT{
android.window.IWindowContainerToken$Stub$Proxy@8b9b010} topActivityType=1 pictureInPictureParams=null shouldDockBigOverlays=false launchIntoPipHostTaskId=0 displayCutoutSafeInsets=null topActivityInfo=ActivityInfo{
7150709 com.android.car.mapsplaceholder.MapsPlaceholderActivity} launchCookies=[android.os.Binder@a4aa911] positionInParent=Point(303, 57) parentTaskId=-1 isFocused=true isVisible=false isSleeping=false topActivityInSizeCompat=false topActivityEligibleForLetterboxEducation= false locusId=null displayAreaFeatureId=1 cameraCompatControlState=hidden}
07-26 17:33:03.205 2931 2931 I lsm3333 : java.lang.Exception
07-26 17:33:03.205 2931 2931 I lsm3333 : at com.android.wm.shell.TaskView.onTaskAppeared(TaskView.java:313)
07-26 17:33:03.205 2931 2931 I lsm3333 : at com.android.car.carlauncher.CarTaskView.onTaskAppeared(CarTaskView.java:46)
07-26 17:33:03.205 2931 2931 I lsm3333 : at com.android.wm.shell.ShellTaskOrganizer.onTaskAppeared(ShellTaskOrganizer.java:440)
07-26 17:33:03.205 2931 2931 I lsm3333 : at com.android.wm.shell.ShellTaskOrganizer.onTaskAppeared(ShellTaskOrganizer.java:429)
07-26 17:33:03.205 2931 2931 I lsm3333 : at android.window.TaskOrganizer$1.lambda$onTaskAppeared$4$android-window-TaskOrganizer$1(TaskOrganizer.java:306)
07-26 17:33:03.205 2931 2931 I lsm3333 : at android.window.TaskOrganizer$1$$ExternalSyntheticLambda6.run(Unknown Source:6)
07-26 17:33:03.205 2931 2931 I lsm3333 : at android.os.Handler.handleCallback(Handler.java:942)
07-26 17:33:03.205 2931 2931 I lsm3333 : at android.os.Handler.dispatchMessage(Handler.java:99)
07-26 17:33:03.205 2931 2931 I lsm3333 : at android.os.Looper.loopOnce(Looper.java:201)
07-26 17:33:03.205 2931 2931 I lsm3333 : at android.os.Looper.loop(Looper.java:288)
07-26 17:33:03.205 2931 2931 I lsm3333 : at android.app.ActivityThread.main(ActivityThread.java:7898)
07-26 17:33:03.205 2931 2931 I lsm3333 : at java.lang.reflect.Method.invoke(Native Method)
07-26 17:33:03.205 2931 2931 I lsm3333 : at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
07-26 17:33:03.205 2931 2931 I lsm3333 : at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
関連するビジネスコードの実行:
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl leash) {
if (mSurfaceCreated) {
// Surface is ready, so just reparent the task to this surface control
//核心的关键方法,对task的surface进行从新reparent到surfaceview的surafce下,这样实现了activity之间的内嵌
mTransaction.reparent(mTaskLeash, getSurfaceControl())
.show(mTaskLeash)
.apply();
} else {
}
//这里会再次进行相关的位置变化通知,该方法会触发重新对task的bounds进行更新
onLocationChanged();
if (taskInfo.taskDescription != null) {
int backgroundColor = taskInfo.taskDescription.getBackgroundColor();
mSyncQueue.runInSync((t) -> {
setResizeBackgroundColor(t, backgroundColor);
});
}
if (mListener != null) {
final int taskId = taskInfo.taskId;
final ComponentName baseActivity = taskInfo.baseActivity;
mListenerExecutor.execute(() -> {
//回调对应的CarLauncher的onTaskCreated方法
mListener.onTaskCreated(taskId, baseActivity);
});
}
}
4. タスク起動処理の通知処理の詳細分析
1. まず、タスクの表示を監視するには、関連する監視メソッドを登録して、
関連する ShellTaskOrganizer を構築する必要があります。
//省略相关方法
public TaskViewManager(@UiContext Context context, HandlerExecutor handlerExecutor,
AtomicReference<CarActivityManager> carActivityManagerRef) {
//构造对应的TaskOrganizer
mTaskOrganizer = new ShellTaskOrganizer(mExecutor, mContext);
initTaskOrganizer(carActivityManagerRef, transactionPool);
}
private void initTaskOrganizer(AtomicReference<CarActivityManager> carActivityManagerRef,
TransactionPool transactionPool) {
//这个方法关键注册到systemserver端,建立联系
List<TaskAppearedInfo> taskAppearedInfos = mTaskOrganizer.registerOrganizer();
}
public List<TaskAppearedInfo> registerOrganizer() {
try {
//这里就是正式的跨进程,把mInterface传递给systemserver方便回调
return mTaskOrganizerController.registerTaskOrganizer(mInterface).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
要約: 上記の最も重要な手順は、システムサーバーとの接続を確立し、関連するコールバック mInterface をシステムサーバーに渡して、タスクが変更されたときにシステムサーバーが mInterface を通じてクライアントに通知できるようにすることです。
2. 監視後にTaskViewに正確に通知する方法
上記の最初のステップでは、CarLauncher がタスク関連の動作を監視できることがわかりました。これはすべてのタスクの動作である必要があることに注意してください。ただし、TaskView 自体はマップ関連のタスクのみを考慮するため、ここで正確に通知するにはどうすればよいでしょうか?
ここでスタックを確認できます。
07-26 17:33:03.205 2931 2931 I lsm3333 : at com.android.car.carlauncher.CarTaskView.onTaskAppeared(CarTaskView.java:46)
07-26 17:33:03.205 2931 2931 I lsm3333 : at com.android.wm.shell.ShellTaskOrganizer.onTaskAppeared(ShellTaskOrganizer.java:440)
07-26 17:33:03.205 2931 2931 I lsm3333 : at com.android.wm.shell.ShellTaskOrganizer.onTaskAppeared(ShellTaskOrganizer.java:429)
07-26 17:33:03.205 2931 2931 I lsm3333 : at android.window.TaskOrganizer$1.lambda$onTaskAppeared$4$android-window-TaskOrganizer$1(TaskOrganizer.java:306)
システム サーバーがクロスプロセスで ShellTaskOrganizer の onTaskAppeared を呼び出していることがわかります。このメソッドでは、TaskView が考慮するタスクの変更を正確に照合して、通知します。
private void onTaskAppeared(TaskAppearedInfo info) {
final int taskId = info.getTaskInfo().taskId;
mTasks.put(taskId, info);
//这里进行对应的TaskListener获取,这里就是TaskView的listener获取,本身TaskView是有实现 ShellTaskOrganizer.TaskListener接口的
final TaskListener listener =
getTaskListener(info.getTaskInfo(), true /*removeLaunchCookieIfNeeded*/);
//获取了TaskView的Listener后就可以进行通知了,从而他知道TaskView的onTaskAppeared
if (listener != null) {
listener.onTaskAppeared(info.getTaskInfo(), info.getLeash());
}
}
次のステップは、この getTaskListener の分析に焦点を当て、現在の通知のタスク情報が TaskView に対応するタスク情報であることを正確に知る方法です。
private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo,
boolean removeLaunchCookieIfNeeded) {
//这里的launchCookies属于系统端回调带的
final ArrayList<IBinder> launchCookies = runningTaskInfo.launchCookies;
for (int i = launchCookies.size() - 1; i >= 0; --i) {
final IBinder cookie = launchCookies.get(i);
//拿系统端的回调的cookie与本地进行匹配,配上了就进行获取了相关的listener
listener = mLaunchCookieToListener.get(cookie);
if (removeLaunchCookieIfNeeded) {
// Remove the cookie and add the listener.
mLaunchCookieToListener.remove(cookie);
mTaskListeners.put(taskId, listener);
}
return listener;
}
return mTaskListeners.get(taskListenerType);
}
上記のことから、サーバーが TaskInfo に対応する Cookie を持ち、対応する Cookie セット マップがローカルに存在することが重要であることがわかり、正確な一致が実現されます。
では、このクッキーはどこから来たのでしょうか? 笑、実際、この Cookie はクライアントから取得され、アクティビティの開始時に構築および設定されます。
public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
@NonNull ActivityOptions options, @Nullable Rect launchBounds) {
//在这prepareActivityOptions进行了相关参数的设置
prepareActivityOptions(options, launchBounds);
//省略
}
prepareActivityOptions メソッドを見てみましょう。
private void prepareActivityOptions(ActivityOptions options, Rect launchBounds) {
//构造出了这个cookie,其实就是binder对象
final Binder launchCookie = new Binder();
mShellExecutor.execute(() -> {
//这里把这个cookie进行相关的存放到map
mTaskOrganizer.setPendingLaunchCookieListener(launchCookie, this);
});
options.setLaunchBounds(launchBounds);
//把cookie设置进了option,可以传递到systemserver端
options.setLaunchCookie(launchCookie);
options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
options.setRemoveWithTaskOrganizer(true);
}
public void setPendingLaunchCookieListener(IBinder cookie, TaskListener listener) {
synchronized (mLock) {
//把这里的cookie放到了mLaunchCookieToListener的集合中
mLaunchCookieToListener.put(cookie, listener);
}
}
この Cookie を使用すると、システムによって呼び出された TaskInfo を対応する TaskView と正確に照合できます。