android framework车载桌面CarLauncher的TaskView详细源码分析

1、构建相关的TaskView,装载到对应的ViewGroup

b站免费视频教程讲解:
https://www.bilibili.com/video/BV1wj411o7A9/

cpp 复制代码
//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、构建出对应的TaskViewManager,主要是注册和初始化TaskOrganizer

2、创建出相关的TaskView即SurfaceView,并放置到CarLauncher的ViewGroup

时序图如下:

2、相关Activity的启动情况

启动Activity相关堆栈

bash 复制代码
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、Task启动后,进行相关的surface重新挂载操作

Task启动完成后进行相关的onTaskAppeared回调:

相关堆栈:

bash 复制代码
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)

相关业务代码的执行:

cpp 复制代码
 @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、Task启动过程通知流程详细分析

1、首先要监听Task相关的显示情况,必须要注册相关的监听方法

进行相关的ShellTaskOrganizer构造

cpp 复制代码
//省略相关方法
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();
        }
    }

总结:上面几步最重要就是与systemserver端建立了联系,而且把相关的回调的mInterface传递到了systemserver端,这样systemserver端就可以在Task有变化情况下通过mInterface通知到客户端

2、监听后怎么准确的通知到TaskView

上面第一步已经实现了CarLauncher可以监听Task的相关行为,注意这里肯定是所有的Task行为,但是TaskView它自身只关心Map相关的Task,那么这里是怎么精准通知的呢?

这里可以通过一个堆栈看看:

bash 复制代码
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)

可以明显看出这里systemserver跨进程回调到了ShellTaskOrganizer的onTaskAppeared,在这个方法进行精准匹配到TaskView关心的Task变化,然后进行通知

cpp 复制代码
    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信息

cpp 复制代码
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集合map,这样实现的准确匹配。

那么这个cookie来自哪里呢?哈哈,其实这个cookie都是来自客户端的,在启动activity时候就构造设置了

cpp 复制代码
 public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
            @NonNull ActivityOptions options, @Nullable Rect launchBounds) {
        //在这prepareActivityOptions进行了相关参数的设置
        prepareActivityOptions(options, launchBounds);
   //省略
    }

来看看prepareActivityOptions方法:

cpp 复制代码
 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

相关推荐
m0_7482359531 分钟前
CentOS 7使用RPM安装MySQL
android·mysql·centos
ac-er88884 小时前
Yii框架中的队列:如何实现异步操作
android·开发语言·php
流氓也是种气质 _Cookie6 小时前
uniapp 在线更新应用
android·uniapp
zhangphil8 小时前
Android ValueAnimator ImageView animate() rotation,Kotlin
android·kotlin
徊忆羽菲8 小时前
CentOS7使用源码安装PHP8教程整理
android
编程、小哥哥10 小时前
python操作mysql
android·python
Couvrir洪荒猛兽10 小时前
Android实训十 数据存储和访问
android
五味香12 小时前
Java学习,List 元素替换
android·java·开发语言·python·学习·golang·kotlin
十二测试录13 小时前
【自动化测试】—— Appium使用保姆教程
android·经验分享·测试工具·程序人生·adb·appium·自动化
Couvrir洪荒猛兽14 小时前
Android实训九 数据存储和访问
android