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

android framework车载桌面CarLauncher的TaskView详细源码分析相关推荐

  1. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发机制...

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  2. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  3. Android多媒体框架(5)—— MediaMuxer.jara源码分析

    MediaMuxer.jara源码分析 音视频通过Codec(编码器)编码之后,还需要经过MediaMuxer(混合器)"混合".混合器在framework的实现就是MediaMu ...

  4. Android Retrofit 2.0(三)从源码分析原理

    Retrofit·特点 性能最好,处理最快 使用REST API时非常方便: 传输层默认就使用OkHttp: 支持NIO: 拥有出色的API文档和社区支持 速度上比volley更快: 如果你的应用程序 ...

  5. Android 7.0 虚拟按键(NavigationBar)源码分析 之 点击事件的实现流程

    第二部分: Let's go!!! [点击事件的实现流程] 1.初始化 虚拟按键点击效果的实现和实体按键相似,也是通过上报一个keyCode值,来判断哪个按钮被点击.不同的是,实体按键的keyCode ...

  6. android如何创建进度条,Android控件ProgressBar--自定义进度条及源码分析

    这里用SeekBar做演示,SeekBar继承自ProgressBar,拥有其一切特性,并且其支持拖动以及DPAD左右键的进退.一起学习吧! 一.自定义SeekBar进度条样式 原生SeekBar效果 ...

  7. jQuery deferred应用dom加载完毕详细源码分析(三)

    我承认上章ajax部分写得不好,不要怪我,它的ajax代码太多了,而且跨越大,方法跳跃多,实在不好排版与讲解,但如果你真正想研究源码并且仔细读了得话,你的 收获应该会很大,至少你明白了js的ajax是 ...

  8. Android研发中对String的思考(源码分析)

    1.常用创建方式思考: String text = "this is a test text "; 上面这一句话实际上是执行了三件事  1.声明变量 String text; 2. ...

  9. ConcurrentHashMap1.7 最最最最最详细源码分析

    声明:本文只有源码分析注释,提供正在研究ConcurrentHashMap源码的同学参考!!! 1.内部结构: 2.Segment 分析 static final class Segment<K ...

最新文章

  1. WINDOWS系统调用 和 SYSENTER系统服务调用过程
  2. springboot-web开发(静态资源)
  3. Python学习笔记:网络编程
  4. Pixhawk原生固件PX4之位姿控制算法解读
  5. 卡巴斯基亚太区总经理:不做免费杀毒厂商
  6. android studio防止反编译,防反编译利器-Android studio混淆代码压缩apk包体积
  7. JavaScript原生对象属性和方法详解——Array对象
  8. 重新审视虚拟桌面存储
  9. MongoDB 可视化管理工具 MongoCola-1.1.0 测试版发布
  10. python 调用海康sdk_Qt调用海康SDK实现摄像头视频播放
  11. wangeditor富文本编辑器的复制word到浏览器发生乱码
  12. Spark中组件Mllib的学习27之逻辑回归-多元逻辑回归,较大数据集,带预测准确度计算
  13. css 交集选择器 并集选择器 后代选择器
  14. 用Python画一棵分形树
  15. 初级软件测试工程师工资(薪资待遇)一般是多少?
  16. win10系统win10搜索框不能用 解决方案
  17. 内部模块化的命令行菜单
  18. 全球变暖,为何寒潮这么多?【北极变暖:冷空气有了出口】
  19. Java-小游戏-炸弹人-课程设计-搜索算法
  20. Shell中获取当前脚本路径

热门文章

  1. NOIP2018提高组游记
  2. 什么是 Embedding()
  3. 关于UML的一些学习
  4. mobi 第五项修炼_第五项修炼(pdf+epub+mobi+txt+azw3)
  5. FFmpeg 解码 AAC 格式的音频
  6. Echarts类似航班选座如何做一个实时监测设备状态的案例
  7. 风控建模七:拒绝推断
  8. 九月十月 阿里 百度 华为 校招笔试题
  9. Linux运维常问面试题总结
  10. Dell服务器安装系统中遇到的坑