android framework车载桌面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() {
//ignoresetContentView(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后,接下来就是构造出对应的TaskViewsetUpTaskView(mapsCard);}}//ignore}//进行TaskView相关的设置private void setUpTaskView(ViewGroup parent) {//把TaskViewManager进行构造mTaskViewManager = new TaskViewManager(this,new HandlerExecutor(getMainThreadHandler()), mCarActivityManagerRef);//创建对应的TaskViewmTaskViewManager.createTaskView(taskView -> {taskView.setListener(getMainExecutor(), mTaskViewListener);//把TaskView添加到了mapsCardparent.addView(taskView);mTaskView = taskView;});}//packages/apps/Car/Launcher/src/com/android/car/carlauncher/TaskViewManager.javapublic TaskViewManager(@UiContext Context context, HandlerExecutor handlerExecutor,AtomicReference<CarActivityManager> carActivityManagerRef) {mContext = context;mExecutor = handlerExecutor;//构造对应的ShellTaskOrganizermTaskOrganizer = new ShellTaskOrganizer(mExecutor, mContext);//把TaskOrganizer进行对应的注册initTaskOrganizer(carActivityManagerRef, transactionPool);}//构造出TaskViewvoid 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)

相关业务代码的执行:

@Overridepublic 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) {//构造对应的TaskOrganizermTaskOrganizer = 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的onTaskAppearedif (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与本地进行匹配,配上了就进行获取了相关的listenerlistener = 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进行相关的存放到mapmTaskOrganizer.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.mzph.cn/news/13120.shtml

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

相关文章

macos下安装john the ripper并配置zip2john+破解加密zip文件

为了破解加密的zip文件&#xff0c;需要用到john进行爆破密码。 1、首先使用homebrew安装john&#xff0c;可以安装它的增强版john-jumbo: brew install john-jumbo 2、安装后可以使用 john 命令验证&#xff1a; john 3、配置zip2john的环境——.zshrc下&#xff0c;&#x…

城市气象数据可视化:洞察气候变化,构建智慧城市

随着城市化进程的加速&#xff0c;城市气象数据的采集和分析变得越来越重要。气象数据不仅影响着人们的生活和出行&#xff0c;还与城市的发展和规划息息相关。在数字化时代&#xff0c;如何将城市中各个气象数据进行可视化&#xff0c;让复杂的数据变得简单易懂&#xff0c;成…

【JavaEE初阶】HTTP请求的构造及HTTPS

文章目录 1.HTTP请求的构造1.1 from表单请求构造1.2 ajax构造HTTP请求1.3 Postman的使用 2. HTTPS2.1 什么是HTTPS?2.2 HTTPS中的加密机制(SSL/TLS)2.2.1 HTTP的安全问题2.2.2 对称加密2.2.3 非对称加密2.2.3 中间人问题2.2.5 证书 1.HTTP请求的构造 常见的构造HTTP 请求的方…

简单工厂模式——集中式工厂的实现

1、简介 1.1、概述 简单工厂模式并不属于GoF 23个经典设计模式&#xff0c;但通常将它作为学习其他工厂模式的基础&#xff0c;它的设计思想很简单&#xff0c;其基本流程如下&#xff1a; 首先将需要创建的各种不同对象的相关代码封装到不同的类中&#xff0c;这些类称为具体…

【IDEA】idea不自动生成target

文章目录 1. 不生成target2. 仅部分文件不生成target2.1. 一般原因就是资源没有设置2.2. 配置编译src/main/java文件夹下的资源文件2.3. 清理缓存&#xff08;王炸&#xff09; 3. 参考资料 本文描述idea不生成target的几种情况以及处理方法 1. 不生成target 像下图这样根本就…

一篇文章搞定Java泛型

目录 介绍 优点 泛型类 语法定义 代码示例 泛型类注意事项 抽奖示例 泛型类派生子类 定义 代码示例 子类是泛型 子类不是泛型 泛型接口 定义 泛型方法 定义 代码示例 泛型方法与可变参数 泛型方法总结 ​编辑类型通配符 定义 代码示例 通配符的上限 定义 …

致敬图灵!HashData拥抱数据智能新时代!

图1&#xff1a;2023ACM中国图灵大会现场 生于1912年的艾伦图灵被称为“计算机科学之父”、“人工智能之父”。1966年&#xff0c;国际计算机协会&#xff08;ACM&#xff09;为了纪念这位卓越的科学家&#xff0c;设立了以其名字命名的ACM图灵奖&#xff0c;以表彰在计算机领…

入门redis你一定需要知道的命令

1、各种数据类型的特点 字符串(string)&#xff1a;普通字符串&#xff0c;Redis中最简单的数据类型 哈希(hash)&#xff1a;也叫散列&#xff0c;类似于Java中的HashMap结构 列表(list)&#xff1a;按照插入顺序排序&#xff0c;可以有重复元素&#xff0c;类似于Java中的Li…

【【51单片机11.0592晶振红外遥控】】

51单片机11.0592晶振红外遥控 红外遥控&#xff0c;51单片机完结 这是初步实现的架构 怎么实现内部的详细逻辑 我们用状态机的方法 0状态时一个空闲状态 当它接收到下降沿开始计时然后转为1状态 1状态下 寻找start 或者repeat的信号 再来下降沿读出定时器的值 如果是start 那…

M 芯片的 macos 系统安装虚拟机 centos7 网络配置

centos 安装之前把网络配置配好或者是把网线插好 第一步找到这个 第二步打开网络适配器 选择图中所指位置 设置好之后 开机启动 centos 第三步 开机以后 编写网卡文件保存 重启网卡就可以了&#xff0c;如果重启网卡不管用&#xff0c;则重启虚拟机即可 “ ifcfg-ens160 ” 这…

黑苹果如何在macOS Sonoma中驱动博通网卡

准备资源&#xff08;百度&#xff1a;黑果魏叔 下载&#xff09; 资源包中包含&#xff1a;AirportBrcmFixup.kext/IOSkywalkFamily.kext/IO80211FamilyLegacy.kext/OpenCore-Patcher 使用方法&#xff1a; 1.将 csr-active-config 设置为 03080000 全选代码 复制 2.在 …

c++数据锁链

题目描述&#xff1a; 创建一个结构体为Node&#xff0c;具有value , next 两个属性&#xff1b; value为整型&#xff0c;用来储存结构体数值&#xff1b; next为Node类型指针&#xff0c;用来指向下一组数据地址&#xff1b; 第1组数据value 5&#xff1b; 第2组数据value …

【C++】STL——list的介绍和使用、list增删查改函数的介绍和使用、push_back、pop_back

文章目录 1.list的使用2.list的增删查改函数&#xff08;1&#xff09;push_front 在list首元素前插入值为val的元素&#xff08;2&#xff09;pop_front 删除list中第一个元素&#xff08;3&#xff09;push_back 在list尾部插入值为val的元素&#xff08;4&#xff09;pop_ba…

TCP三次握手/四次挥手

三次握手 三次握手过程 TCP服务器进程先创建传输控制块TCB&#xff0c;时刻准备接受客户进程的连接请求&#xff0c;此时服务器就进入了LISTEN&#xff08;监听&#xff09;状态&#xff1b;TCP客户进程也是先创建传输控制块TCB&#xff0c;然后向服务器发出连接请求报文&…

【QT 网络云盘客户端】——登录界面功能的实现

目录 1.注册账号 2.服务器ip地址和端口号设置 3. 登录功能 4.读取配置文件 5.显示主界面 1.注册账号 1.点击注册页面&#xff0c;将数据 输入 到 用户名&#xff0c;昵称&#xff0c;密码&#xff0c;确认密码&#xff0c;手机&#xff0c;邮箱 的输入框中&#xff0c; 点…

【C语言学习——————动态内存管理】

文章目录 一、什么是动态内存管理二、动态内存函数的介绍 1.malloc函数的介绍2.calloc函数的介绍3.realloc函数的介绍三、free函数的介绍 一.什么是动态内存管理 我们知道数据都是在内存中进行储存的&#xff0c;但是如果我们需要调用内存&#xff0c;我们可以通过定义一个变量…

网络安全系统中的守护者:如何借助威胁情报 (TI) 提高安全性

在这篇哈巴尔网站上的推文中&#xff0c;我们将解释 TI 缩写背后的含义、为什么需要它、Positive Technologies 收集哪些网络威胁数据以及如何帮助企业预防网络威胁。我们将以四种情况为例&#xff0c;说明公司如何使用 PT Threat Intelligence Feeds 来发现恶意活动并预防攻击…

vue3+ts+elementui-plus二次封装树形表格

复制粘贴即可&#xff1a; 一、定义table组件 <template><div classmain><div><el-table ref"multipleTableRef" :height"height" :default-expand-all"isExpend" :data"treeTableData"style"width: 100%…

从官网认识 JDK,JRE,JVM 三者的关系

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ JVM 是一些大厂面试必问点&#xff0c;要想解决 OOM、性能调优方面的问题&#xff0c;掌握 JVM 知识必不可少&#xff0c;从今天开始&#xff0c;将为大家介绍 JVM 的常用知…

【Docker】Docker应用部署之Docekr容器安装Nginx

目录 一、搜索镜像 二、拉取镜像 三、创建容器 四、测试使用 一、搜索镜像 docker search nginx 二、拉取镜像 docker pull nginx # 不加冒号版本号 默认拉取最新版 三、创建容器 首先我们需要在宿主机创建数据卷目录 mkdir nginx # 创建目录 cd nginx # 进入目录 mkd…