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

news2024/9/27 7:15:36

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、构建出对应的TaskViewManager,主要是注册和初始化TaskOrganizer
2、创建出相关的TaskView即SurfaceView,并放置到CarLauncher的ViewGroup

时序图如下:
在这里插入图片描述

2、相关Activity的启动情况

启动Activity相关堆栈

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回调:
相关堆栈:

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、Task启动过程通知流程详细分析

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

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

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

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

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

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变化,然后进行通知


    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中有对应的cookie,然后本地也有对应的cookie集合map,这样实现的准确匹配。
那么这个cookie来自哪里呢?哈哈,其实这个cookie都是来自客户端的,在启动activity时候就构造设置了

 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/805363.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

管理类联考——写作——记忆篇——论证有效性分析——析错口诀

析错口诀 概念不明确&#xff0c;我就说它概念模糊&#xff0c;并做不利它的解释。【有概念模糊之嫌&#xff0c;A是理解1&#xff1f;还是理解2&#xff1f;】概念有变换&#xff0c;我就说它混淆概念&#xff0c;并指出混淆的环节。&#xff08;概念推概念&#xff0c;我就看…

类方法(成员方法)与构造方法的区别与联系

&#xff08;粗略理解为&#xff1a;除了构造方法以外的所有方法都是成员方法&#xff09; 区别&#xff1a; 构造方法在创建对象时用new调用&#xff0c;是为了给对象的数据进行初始化&#xff0c;没有返回值。 成员方法是实现对类中成员变量的操作&#xff0c;提供某些功能…

【图论】kruskal算法

一.介绍 Kruskal&#xff08;克鲁斯卡尔&#xff09;算法是一种用于解决最小生成树问题的贪心算法。最小生成树是指在一个连通无向图中&#xff0c;选择一棵包含所有顶点且边权重之和最小的树。 下面是Kruskal算法的基本步骤&#xff1a; 将图中的所有边按照权重从小到大进行…

JUC并发工具类

一、ReentrantLock 特点&#xff1a;独占、可重入、公平/非公平、可中断、支持多个条件变量 1、常用api ReentrantLock实现了Lock接口&#xff0c;Lock类规范定义了如下方法 lock()&#xff1a;获取锁&#xff0c;调用该方法的线程会获取锁&#xff0c;当锁获得后&#xff0…

想做上位机,学C#还是QT?

学习C#还是Qt&#xff0c;取决于你的具体需求和偏好。 如果你计划开发跨平台的桌面应用程序&#xff0c;并且希望使用一种更轻量级、直观的界面框架&#xff0c;那么Qt可能是一个不错的选择。Qt是一个功能丰富且成熟的跨平台框架&#xff0c;支持多种开发语言&#xff08;包括…

第J2周:ResNet50V2算法实战与解析

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f366; 参考文章&#xff1a;365天深度学习训练营-第J2周&#xff1a;ResNet50V2算法实战与解析&#x1f356; 原作者&#xff1a;K同学啊|接辅导、项目定制 目录 一、论文解读1. ResNetV2结构与…

【需求响应】一种新的需求响应机制DR-VCG研究

目录 1 主要内容 2 部分代码 3 程序结果 4 程序链接 1 主要内容 该程序对应文章《Contract Design for Energy Demand Response》&#xff0c;电力系统需求响应&#xff08;DR&#xff09;用来调节用户对电能的需求&#xff0c;即在预测的需求高于电能供应时&#xff0c;希…

VS Code调试Darknet

一、安装插件 二、连接服务器 三、调试darknet工程 {"version": "2.0.0","options": {"cwd": "${workspaceFolder}"},"tasks": [{"label": "clean","type": "shell",&qu…

数据结构之动态顺序表(附带完整程序)

&#x1f388;基本概念 &#x1f308;一.线性表、顺序表的定义 ☀️&#xff08;1&#xff09;线性表&#xff1a; 是n个具有相同特性的数据元素的有限序列。线性表在逻辑上是线性结构&#xff0c;但在物理上存储时&#xff0c;通常以数组和链式结构的形式存储。 ☀️&…

C# 关于使用newlife包将webapi接口寄宿于一个控制台程序、winform程序、wpf程序运行

C# 关于使用newlife包将webapi接口寄宿于一个控制台程序、winform程序、wpf程序运行 安装newlife包 Program的Main()函数源码 using ConsoleApp3; using NewLife.Log;var server new NewLife.Http.HttpServer {Port 8080,Log XTrace.Log,SessionLog XTrace.Log }; serv…

hcip——ospf综合

要求 1. 搭建toop 2.地址规划 协议范围路由器地址 RIP 172.16.0.0 17 R12 loop0&#xff1a;172.16.0.0 24 loop1&#xff1a;172.16.1.0 24 OSPF 172.16.128.0 17 area1 172.16.144.0 20 R1 g0:172.16.144.1 24 loop0:172.16.145.1 24 R2 g0:172.16.144.2 24 loop:172…

iOS - Apple开发者账户添加新测试设备

获取UUID 首先将设备连接XCode&#xff0c;打开Window -> Devices and Simulators&#xff0c;通过下方位置查看 之后登录(苹果开发者网站)[https://developer.apple.com/account/] &#xff0c;点击设备 点击加号添加新设备 填写信息之后点击Continue&#xff0c;并一路继续…

Golang Devops项目开发(1)

1.1 GO语言基础 1 初识Go语言 1.1.1 开发环境搭建 参考文档&#xff1a;《Windows Go语言环境搭建》 1.2.1 Go语言特性-垃圾回收 a. 内存自动回收&#xff0c;再也不需要开发人员管理内存 b. 开发人员专注业务实现&#xff0c;降低了心智负担 c. 只需要new分配内存&#xff0c;…

Android系统服务之AMS

目录 概述 重点和难点问题 启动方式 main入口&#xff1a; run方法&#xff1a; BootstrapSevices 小结&#xff1a; 与其他线程的通信原理 参考文档&#xff1a; 概述 AMS是Android系统主要负责四大组件的启动&#xff0c;切换&#xff0c;调度以及应用程序进程管理和调度等工…

watch避坑,使用computed进行处理数据

业务场景&#xff1a;在vue中监听el-input 中的字数有没有超过60&#xff0c;如果超过60字时将60后面的字变为 “>>” 符号&#xff0c;以此实现预览苹果手机推送摘要场景。 错误&#xff1a;开始的逻辑是使用watch监听&#xff0c;检查length超过60直接 加上符号&#x…

选好NAS网络储存解决方案,是安全储存的关键

随着网络信息的发展&#xff0c;NAS也越来越受到企业的关注&#xff0c;NAS网络存储除了提供简单的存储服务外&#xff0c;还可以提供更好的数据安全性、更方便的文件共享方式。但市面上的产品种类繁多&#xff0c;我们该如何选择合适的产品&#xff0c;通过企业云盘&#xff0…

spring5源码篇(12)——spring-mvc请求流程

spring-framework 版本&#xff1a;v5.3.19 文章目录 一、请求流程1、处理器映射器1.1、 RequestMappingHandlerMapping1.2、获取对应的映射方法1.3、添加拦截器 2、获取合适的处理器适配器3、通过处理器适配器执行处理器方法3.1、拦截器的前置后置3.2、处理器的执行3.2.1 参数…

Unity 性能优化二:内存问题

目录 策略导致的内存问题 GFX内存 纹理资源 压缩格式 Mipmap 网格资源 Read/Write 顶点数据 骨骼 静态合批 Shader资源 Reserved Memory RenderTexture 动画资源 音频资源 字体资源 粒子系统资源 Mono堆内存 策略导致的内存问题 1. Assetbundle 打包的时候…

【C++】C++ STL标准模板库知识点总结(秋招篇)

文章目录 前言STL的六大组件是&#xff1f;容器(container) 算法(algorithm) 迭代器(iterator) 三者的关系&#xff1f;容器分为几种&#xff1f;分别有哪些&#xff1f;关联性容器和非关联性容器有什么区别&#xff1f;Vector容器是怎么调整大小的&#xff1f;&#xff08;内存…

VirtualEnv 20.24.0 发布

导读VirtualEnv 20.24.0 现已发布&#xff0c;VirtualEnv 用于在一台机器上创建多个独立的 Python 运行环境&#xff0c;可隔离项目之间的第三方包依赖&#xff0c;为部署应用提供方便&#xff0c;把开发环境的虚拟环境打包到生产环境即可&#xff0c;不需要在服务器上再折腾一…