当前位置: 首页 > news >正文

DrawState与wms绘制流程梳理

APP端添加窗口->Srv端添加窗口

// app端获取wms
mWindowManager = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
// app端调用wms 会话进行addview完成窗口添加
mWindowManager.addView(mWindowViewLayout, mLayoutParams);

看代码,串一下流程

流程图汇总

在这里插入图片描述
从图中可以看到,整体app端到srv端的窗口添加是通过session调用过去的,通过app端的viewRootImpl
而Srv端的添加逻辑主要就是在wms中的addWindow了;这里详细展开看下

srv端代码展开分析

public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,int displayId, int requestUserId, @InsetsType int requestedVisibleTypes,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,float[] outSizeCompatScale) {outActiveControls.set(null);int[] appOp = new int[1];final boolean isRoundedCornerOverlay = (attrs.privateFlags& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,appOp);if (res != ADD_OKAY) {return res;}WindowState parentWindow = null;final int callingUid = Binder.getCallingUid();final int callingPid = Binder.getCallingPid();final long origId = Binder.clearCallingIdentity();final int type = attrs.type;synchronized (mGlobalLock) {if (!mDisplayReady) {throw new IllegalStateException("Display has not been initialialized");}final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);if (displayContent == null) {ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does "+ "not exist: %d. Aborting.", displayId);return WindowManagerGlobal.ADD_INVALID_DISPLAY;}if (!displayContent.hasAccess(session.mUid)) {ProtoLog.w(WM_ERROR,"Attempted to add window to a display for which the application "+ "does not have access: %d.  Aborting.",displayContent.getDisplayId());return WindowManagerGlobal.ADD_INVALID_DISPLAY;}if (mWindowMap.containsKey(client.asBinder())) {ProtoLog.w(WM_ERROR, "Window %s is already added", client);return WindowManagerGlobal.ADD_DUPLICATE_ADD;}这里去找了下是否存在 父window,条件是为当前add的window类型是什么暂时不清楚,但是不在这个范围内
具体window类型定义在此文件中:frameworks/base/core/java/android/view/WindowManager.java
这里的限定范围是 (1000)  - 1999;
因此这里并没有对parentWindow进行赋值,此时仍然是null;if (type >= FIRST_SUB_WINDOW&& type <= LAST_SUB_WINDOW) {parentWindow = windowForClientLocked(null, attrs.token, false);if (parentWindow == null) {ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: "+ "%s.  Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;}if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: "+ "%s.  Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;}}if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {ProtoLog.w(WM_ERROR,"Attempted to add private presentation window to a non-private display.  "+ "Aborting.");return WindowManagerGlobal.ADD_PERMISSION_DENIED;}if (type == TYPE_PRESENTATION && !displayContent.getDisplay().isPublicPresentation()) {ProtoLog.w(WM_ERROR,"Attempted to add presentation window to a non-suitable display.  "+ "Aborting.");return WindowManagerGlobal.ADD_INVALID_DISPLAY;}int userId = UserHandle.getUserId(session.mUid);if (requestUserId != userId) {try {mAmInternal.handleIncomingUser(callingPid, callingUid, requestUserId,false /*allowAll*/, ALLOW_NON_FULL, null, null);} catch (Exception exp) {ProtoLog.w(WM_ERROR, "Trying to add window with invalid user=%d",requestUserId);return WindowManagerGlobal.ADD_INVALID_USER;}// It's fine to use this userIduserId = requestUserId;}hasParent为false;ActivityRecord activity = null;final boolean hasParent = parentWindow != null;// Use existing parent window token for child windows since they go in the same token// as there parent window so we can apply the same policy on them.对子窗口使用现有的父窗口令牌,因为它们与父窗口使用相同的令牌,因此我们可以对它们应用相同的策略对于当前添加的悬浮窗,并没有一个parentwindow,因此这里使用的是传入的layout的attrs对应的token去通过getWindowToken 拿到map中是否包含了这个token,显然是没有的,所以这里token是null;真正的创建token还在下面WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);// If this is a child window, we want to apply the same type checking rules as the// parent window type.final int rootType = hasParent ? parentWindow.mAttrs.type : type;boolean addToastWindowRequiresToken = false;final IBinder windowContextToken = attrs.mWindowContextToken;if (token == null) {if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,rootType, attrs.token, attrs.packageName)) {return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (hasParent) {// Use existing parent window token for child windows.token = parentWindow.mToken;} else if (mWindowContextListenerController.hasListener(windowContextToken)) {// Respect the window context token if the user provided it.final IBinder binder = attrs.token != null ? attrs.token : windowContextToken;final Bundle options = mWindowContextListenerController.getOptions(windowContextToken);token = new WindowToken.Builder(this, binder, type).setDisplayContent(displayContent).setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow).setRoundedCornerOverlay(isRoundedCornerOverlay).setFromClientToken(true).setOptions(options).build();} else {这里把传入的client作为参数,转为binder对象,并new了一个window token,把这个binder作为关键信息传了进去;这里的client是函数的入参,类型为IWindow client;追根溯源,其是viewrootImpl类传入的一个成员变量,是一个很重要的角色,是一个binder的stub对象,可以和server端进行通信;final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();token = new WindowToken.Builder(this, binder, type).setDisplayContent(displayContent).setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow).setRoundedCornerOverlay(isRoundedCornerOverlay).build();}} else if (rootType >= FIRST_APPLICATION_WINDOW&& rootType <= LAST_APPLICATION_WINDOW) {activity = token.asActivityRecord();if (activity == null) {ProtoLog.w(WM_ERROR, "Attempted to add window with non-application token "+ ".%s Aborting.", token);return WindowManagerGlobal.ADD_NOT_APP_TOKEN;} else if (activity.getParent() == null) {ProtoLog.w(WM_ERROR, "Attempted to add window with exiting application token "+ ".%s Aborting.", token);return WindowManagerGlobal.ADD_APP_EXITING;} else if (type == TYPE_APPLICATION_STARTING) {if (activity.mStartingWindow != null) {ProtoLog.w(WM_ERROR, "Attempted to add starting window to "+ "token with already existing starting window");return WindowManagerGlobal.ADD_DUPLICATE_ADD;}if (activity.mStartingData == null) {ProtoLog.w(WM_ERROR, "Attempted to add starting window to "+ "token but already cleaned");return WindowManagerGlobal.ADD_DUPLICATE_ADD;}}} else if (rootType == TYPE_INPUT_METHOD) {if (token.windowType != TYPE_INPUT_METHOD) {ProtoLog.w(WM_ERROR, "Attempted to add input method window with bad token "+ "%s.  Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (rootType == TYPE_VOICE_INTERACTION) {if (token.windowType != TYPE_VOICE_INTERACTION) {ProtoLog.w(WM_ERROR, "Attempted to add voice interaction window with bad token "+ "%s.  Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (rootType == TYPE_WALLPAPER) {if (token.windowType != TYPE_WALLPAPER) {ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with bad token "+ "%s.  Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {ProtoLog.w(WM_ERROR,"Attempted to add Accessibility overlay window with bad token "+ "%s.  Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (type == TYPE_TOAST) {// Apps targeting SDK above N MR1 cannot arbitrary add toast windows.addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,callingUid, parentWindow);if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {ProtoLog.w(WM_ERROR, "Attempted to add a toast window with bad token "+ "%s.  Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (type == TYPE_QS_DIALOG) {if (token.windowType != TYPE_QS_DIALOG) {ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with bad token "+ "%s.  Aborting.", attrs.token);return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (token.asActivityRecord() != null) {ProtoLog.w(WM_ERROR, "Non-null activity for system window of rootType=%d",rootType);// It is not valid to use an app token with other system types; we will// instead make a new token for it (as if null had been passed in for the token).attrs.token = null;token = new WindowToken.Builder(this, client.asBinder(), type).setDisplayContent(displayContent).setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow).build();}这里是很关键的地方,创建了这个windowtoken对应的windowState,final WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);if (win.mDeathRecipient == null) {// Client has apparently died, so there is no reason to// continue.ProtoLog.w(WM_ERROR, "Adding window client %s"+ " that is dead, aborting.", client.asBinder());return WindowManagerGlobal.ADD_APP_EXITING;}if (win.getDisplayContent() == null) {ProtoLog.w(WM_ERROR, "Adding window to Display that has been removed.");return WindowManagerGlobal.ADD_INVALID_DISPLAY;}final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();displayPolicy.adjustWindowParamsLw(win, win.mAttrs);attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), callingUid, callingPid);attrs.inputFeatures = sanitizeSpyWindow(attrs.inputFeatures, win.getName(), callingUid,callingPid);win.setRequestedVisibleTypes(requestedVisibleTypes);res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);if (res != ADD_OKAY) {return res;}这里很关键,创建了对应的inputChannel链路;outInputChannel是入参,有viewrootImpl创建的;final boolean openInputChannels = (outInputChannel != null&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);if  (openInputChannels) {win.openInputChannel(outInputChannel);}// If adding a toast requires a token for this app we always schedule hiding// toast windows to make sure they don't stick around longer then necessary.// We hide instead of remove such windows as apps aren't prepared to handle// windows being removed under them.//// If the app is older it can add toasts without a token and hence overlay// other apps. To be maximally compatible with these apps we will hide the// window after the toast timeout only if the focused window is from another// UID, otherwise we allow unlimited duration. When a UID looses focus we// schedule hiding all of its toast windows.if (type == TYPE_TOAST) {if (!displayContent.canAddToastWindowForUid(callingUid)) {ProtoLog.w(WM_ERROR, "Adding more than one toast window for UID at a time.");return WindowManagerGlobal.ADD_DUPLICATE_ADD;}// Make sure this happens before we moved focus as one can make the// toast focusable to force it not being hidden after the timeout.// Focusable toasts are always timed out to prevent a focused app to// show a focusable toasts while it has focus which will be kept on// the screen after the activity goes away.if (addToastWindowRequiresToken|| (attrs.flags & FLAG_NOT_FOCUSABLE) == 0|| displayContent.mCurrentFocus == null|| displayContent.mCurrentFocus.mOwnerUid != callingUid) {mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),win.mAttrs.hideTimeoutMilliseconds);}}// Switch to listen to the {@link WindowToken token}'s configuration changes when// adding a window to the window context. Filter sub window type here because the sub// window must be attached to the parent window, which is attached to the window context// created window token.if (!win.isChildWindow()&& mWindowContextListenerController.hasListener(windowContextToken)) {final int windowContextType = mWindowContextListenerController.getWindowType(windowContextToken);final Bundle options = mWindowContextListenerController.getOptions(windowContextToken);if (type != windowContextType) {ProtoLog.w(WM_ERROR, "Window types in WindowContext and"+ " LayoutParams.type should match! Type from LayoutParams is %d,"+ " but type from WindowContext is %d", type, windowContextType);// We allow WindowProviderService to add window other than windowContextType,// but the WindowProviderService won't be associated with the window's// WindowToken.if (!isWindowProviderService(options)) {return WindowManagerGlobal.ADD_INVALID_TYPE;}} else {mWindowContextListenerController.updateContainerForWindowContextListener(windowContextToken, token);}}// From now on, no exceptions or errors allowed!if (displayContent.mCurrentFocus == null) {displayContent.mWinAddedSinceNullFocus.add(win);}if (excludeWindowTypeFromTapOutTask(type)) {displayContent.mTapExcludedWindows.add(win);}这里终于将当前windowState的信息放到了mWindowMap中,key是client(也就是viewrootImpl的binder stub)后面就先不看了,到此为止win.attach();mWindowMap.put(client.asBinder(), win);win.initAppOpsState();
。。。。}Binder.restoreCallingIdentity(origId);return res;}

老大一堆,用一句话总结就是,完成mWindowMap变量的成员添加,key是app端的binder stub,value是创建出来的windowState

小总结一下

对于add到server端的一个window来说,addWindow时主要完成以下4个关键步骤

  1. 创建这个窗口的WindowToken,并将其挂载到层级结构树中(一般windowTokend会挂载到leaf下,作为倒数第二层,最后一层往往是个windowState对象)
  2. 创建一个WindowState对象,与当前window进行绑定(使用当前window的信息进行创建)
  3. 将当前窗口的input链路进行打通,通过openInputChannel完成input链路初始化,创建了对应的 Pair<socket,socket>
  4. 将当前窗口的windowState挂载到层级结构树中,使其结构完成;

relayout

概述

应用端发起addwindow到wms,wms创建windowtoken和windowstate,并挂载到了层级结构树上面;
此时应用端会发起relayoutWindow的操作

其实上面addWindow时,也只是new出来两个 windowToken和windowState,此时两者只是初始化,并没有surface进行绘制的能力;

其实真正进行surfaceControl创建就是在这个relayout过程中;

代码

绘制阶段

5个绘制阶段,表示当前window所处的绘制状态
在这里插入图片描述
应用调用到wms的relayout的过程和之前的 addWindow类似,都是通过viewRootImpl中的session的binder调用过来的,具体应用侧的流程还没有梳理,大概就是doTraversal-> performTraversals的过程;下面主要看下srv端的逻辑

代码梳理

创建surfacecontrol

public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,int requestedWidth, int requestedHeight, int viewVisibility, int flags,ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,SurfaceControl outSurfaceControl, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls, Bundle outSyncIdBundle) { 。。。// 1. 通过刚刚存储的变量,拿到对应的binder和windowstatefinal WindowState win = windowForClientLocked(session, client, false);
。。。// 2. 创建surfacecontrolif (shouldRelayout) {try {result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);} catch (Exception e) {
。。。}}// We may be deferring layout passes at the moment, but since the client is interested// in the new out values right now we need to force a layout.// 3. performSurfacePlacement进行relayout操作!!!!!mWindowPlacerLocked.performSurfacePlacement(true /* force */);//ignoreif (!win.isGoneForLayout()) {win.mResizedWhileGone = false;}// 4. 相关配置数据win.fillClientWindowFramesAndConfiguration(outFrames, mergedConfiguration,false /* useLatestConfig */, shouldRelayout);outInsetsState.set(win.getCompatInsetsState(), win.isClientLocal());
。。。}
。。。return result;}

finishDraw

todo

surface

todo

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • RabbitMQ延迟队列
  • H5 优化手段
  • 《新一代数据可视化分析工具应用指南》正式开放下载
  • go语言创建协程
  • 4章7节:用R做数据重塑,行列命名和数据类型转换
  • 【IT行业研究报告】Internet Technology
  • Android网络库:Volley、Retrofit和OkHttp的比较与应用
  • ARM/Linux嵌入式面经(二一):诺瓦科技
  • Spring Boot 的Web开发
  • Linux 下 ETCD 安装、配置与命令使用总结
  • 【Spark集群部署系列二】Spark StandAlone模式介绍和搭建以及使用
  • Docker运行Cassandra集群
  • 如何选择工厂模式或策略模式:Java设计模式实践指南
  • mmdebstrap:创建 Debian 系统 chroot 环境的利器 ️
  • LeetCode138-随机链表的复制--经典OJ题
  • 【Linux系统编程】快速查找errno错误码信息
  • 30天自制操作系统-2
  • 78. Subsets
  • Create React App 使用
  • input的行数自动增减
  • JavaScript-Array类型
  • Java-详解HashMap
  • Protobuf3语言指南
  • Redis在Web项目中的应用与实践
  • vuex 学习笔记 01
  • 编写高质量JavaScript代码之并发
  • 对JS继承的一点思考
  • 关于Flux,Vuex,Redux的思考
  • 基于webpack 的 vue 多页架构
  • 力扣(LeetCode)22
  • 力扣(LeetCode)357
  • 每天一个设计模式之命令模式
  • 微信小程序设置上一页数据
  • 用 Swift 编写面向协议的视图
  • 自制字幕遮挡器
  • 没有任何编程基础可以直接学习python语言吗?学会后能够做什么? ...
  • ​批处理文件中的errorlevel用法
  • #《AI中文版》V3 第 1 章 概述
  • (07)Hive——窗口函数详解
  • (1)无线电失控保护(二)
  • (二)什么是Vite——Vite 和 Webpack 区别(冷启动)
  • (力扣)1314.矩阵区域和
  • (力扣)循环队列的实现与详解(C语言)
  • (四)模仿学习-完成后台管理页面查询
  • (算法)大数的进制转换
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • *算法训练(leetcode)第四十七天 | 并查集理论基础、107. 寻找存在的路径
  • .“空心村”成因分析及解决对策122344
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
  • .NET I/O 学习笔记:对文件和目录进行解压缩操作
  • .NET 给NuGet包添加Readme
  • .NetCore 如何动态路由
  • .net经典笔试题
  • .NET框架设计—常被忽视的C#设计技巧