안드로이드 프레임워크 자동차 데스크탑 CarLauncher의 TaskView 상세 소스코드 분석

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. 먼저 Task의 표시를 모니터링 하기 위해서는
해당 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를 분석하는데 집중하여 현재 알림의 Task 정보가 TaskView에 해당하는 Task 정보임을 정확히 알 수 있는 방법입니다.


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에 해당 쿠키를 보유하고 로컬에 해당 쿠키 세트 맵이 있으므로 정확한 일치를 달성할 수 있다는 것입니다.
이 쿠키는 어디에서 왔습니까? ㅎㅎ 사실 이 쿠키는 클라이언트에서 오는 쿠키인데 활동이 시작될 때 구성되고 설정됩니다.

 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);
        }
    }

이 쿠키를 통해 시스템에서 호출한 TaskInfo를 해당 TaskView에 정확하게 일치시킬 수 있습니다.

추천

출처blog.csdn.net/learnframework/article/details/131944173