view绘制流程(Andoird WindowManager管理Window的流程)

:暂无数据 2026-08-10 23:30:06 :0

view绘制流程(Andoird WindowManager管理Window的流程)

大家好,view绘制流程相信很多的网友都不是很明白,包括Andoird WindowManager管理Window的流程也是一样,不过没有关系,接下来就来为大家分享关于view绘制流程和Andoird WindowManager管理Window的流程的一些知识点,大家可以关注收藏,免得下次来找不到哦,下面我们开始吧!

本文目录

Andoird WindowManager管理Window的流程

分析Windowmanager如何管理window的流程。

添加、更新、删除一个Window:

参数说明:
1、flag,控制Window的显示特性,参数很多可自行查询API,常用如下
FLAG_NOT_TOUCH_MODAL:Window区域内的点击事件自己处理,之外的传递给底层Window,通常打开此标记
FLAG_NOT_FOCUSALE:不需要焦点,直接传递到底层Window
FLAG_SHOW_WHEN_LOCKED:顾名思义,显示在锁屏上
2、type,三种类型。Window是分层的
应用Window:层级1-99,对应一个activity
子Window:层级1000-1999,需要在特定的父Window中,比如Dialog
系统Window:层级2000-2999,需添加权限SYSTEM_ALERT_WINDOW,比如状态栏

ViewManager只是个interface,搜索源码,实现类为WindowManagerImpl

然后交给代理类mGlobal即WindowManagerGlobal

mViews,mRoots,mParams是三个ArrayList,用来保存所有window对应的view、ViewRootImpl和布局参数
ViewRootImpl:WindowManager创建它来管理view
最后通过ViewRootImpl.setView来设置view

然后是mWindowSession.addToDisplay,其实现是Session.addToDisplay

所以其实是WindowManagerService的addWindow,这里addWindow就是WindowManagerService把window如何布置到屏幕上的具体深入细节了,就不跟了。

WindowManagerGlobal的removeView调用

即

ViewRootImpl.die

mHandler.sendEmptyMessage(MSG_DIE);延时调用,最终还是会直接调用doDie();

doRemoveView,刷新mViews,mRoots,mParams数据
dispatchDetachedFromWindow中主要行为三点:

WindowManagerGlobal的updateViewLayout

ViewRootImpl.setLayoutParams刷新LayoutParams

mTraversalRunnable调用performTraversals,这个就是所有view的绘制过程了,重绘时会调用relayoutWindow然后调用mWindowSession.relayout,即WindowManagerService的relayoutWindow方法

Windowmanager通过ViewRootImpl来添加、更新和删除window,最终的实现都是WindowmanagerService具体实现。本文目的也是掌握Windowmanager管理window的流程。

Visio是什么软件

Microsoft Visio是Windows 操作系统下运行的流程图软件,它现在是Microsoft Office软件的一个部分。

Visio可以制作的图表范围十分广泛,有些人利用Visio的强大绘图功能绘制地图、企业标志等,同时Visio支持将文件保存为svg、dwg等矢量图形通用格式,因此受到广泛欢迎。它的最新版本为2019。

扩展资料:

Microsoft Visio 发展历史。

Visio是微软公司出品的一款的软件,它有助于 IT 和商务专业人员轻松地可视化、分析和交流复杂信息。它能够将难以理解的复杂文本和表格转换为一目了然的图表。

1992年,位于西雅图的Visio公司发布了用于制作商业图标的专业绘图软件Visio1.0,该软件一经面世立即取得了巨大的成功,Visio公司研发人员在此基础上开发了Visio2.0、Visio3.0 、Visio4.0、Visio5.0等几个版本。

1999年微软并购了Visio公司,几乎在同一时间发布Visio2000,该版本分为标准版、技术版、专业版、企业版,在当时,Visio 2000成为世界上最快捷最容使用的流程图软件,同时也添加了更多的功能。

参考资料来源:百度百科——Microsoft Office Visio

Android:一篇文章带你完全梳理自定义View工作流程!

了解自定义View流程前,需了解一定的自定义View基础,具体请看文章: (1)自定义View基础 - 最易懂的自定义View原理系列

下面,我将详细讲解 View 绘制的三大流程: measure 过程、 layout 过程、 draw 过程

请看文章: 自定义View Layout过程 - 最易懂的自定义View原理系列(3)

至此,关于自定义 View 的工作流程讲解完毕。

结合原理 & 实现步骤,若需实现1个自定义View,请看文章: 手把手教你写一个完整的自定义View

Activity的绘制流程(四)

***隐藏网址***
(一)窗口的添加
(二)Choreographer
(三)VSync
(四)Surface
(五)RenderThread
(六)StartingWIndow
(七)窗口切换

从前面的介绍中,我们得知了一个Activity对应一个Window,一个Window包含多个View,一个Window又对应一个Surface,Surface相当与一块画布,管理着图形缓冲区。
使用硬件渲染后,主线程的绘制工作主要是采用递归的方式遍历DecorView包含的所有的View,构建和更新一个DisplayList。DisplayList主要包含两个重要的对象,一个是deque队列mChildNodes,View对应一个RenderNode,RenderNode被封装在一个RenderNodeDrawable对象中,并加入到该队列,该队列用于后续同步时取得保存的子RenderNode。可以将这个队列想象成一个树结构来理解,每个节点为RenderNodeDrawable。另一个是DisplayListData对象mDisplayList,该对象中保存了各个Op,一个Op代表一个绘制操作,比如绘制多个点 DrawPoints,比如绘制子View DrawDrawable。
接着主线程会进入睡眠,直到RenderThread线程完成DisplayList的同步工作。然后RenderThread线程会从Surface对应的图形缓冲区队列BufferQueue中申请一块图形缓冲区GraphicBuffer(当然这里的GraphicBuffer只是将物理内存映射到应用的进程空间)进行绘制,简单来说就是将DisplayList中的各个绘制操作填充进去,最后将该GraphicBuffer放回队列中,至此完成了CPU上的绘制流程。
创建Surface,其实是在SurfaceFlinger端为Window创建了一个BufferQueue。BufferQueueCore是BufferQueue的实现类,BufferQueueCore初始化的时候并不是triple buffers,而是double buffers。
应用端通过BufferQueueProducer来allocateBuffers(分配GraphicBuffer)、dequeueBuffer(申请GraphicBuffer,映射到应用的进程空间)、queueBuffer(将GraphicBuffer放回BufferQueue)
SurfaceFlinger通过BufferQueueConsumer来acquireBuffer(申请GraphicBuffer)、releaseBuffer(释放GraphicBuffer)
所以BufferQueue、应用端、SurfaceFlinger构成了一个生产者-消费者模型,由应用端绘制Buffer,绘好的Buffer被SurfaceFlinger用于合成。

在调用BufferQueueLayer的onFirstRef函数时,会去判断系统属性ro.sf.disable_triple_buffer的值,默认为0,即设置为triple buffers,可以通过修改系统属性ro.sf.disable_triple_buffer修改为double buffers。

在Android Q中,若帧率大于66.6,即日常见到的90帧、120帧,会将triple buffers改为four buffers。

GraphicBufferAllocator是Android为上层提供的一个类,用于分配图形缓冲区。图形缓冲区可以从帧缓冲区,也可以从内存中分配,图形缓冲区从帧缓冲区中分配值用于SurfaceFlinger。应用进程申请分配图形缓冲区,由SurfaceFlinger从内存中分配,分配的时候会将物理内存映射到SurfaceFlinger的进程空间,后续当应用端通过BufferQueueProducer来dequeueBuffer函数时才把图形缓冲区映射到应用进程的进程空间。
在BufferQueueCore中有五个重要的列表:
set《int》 mFreeSlots(该set中下标的slot表示处于FREE状态,且没有GraphicBuffer)
list《int》 mUnusedSlots(该set中下标的slot表示未使用,数目为64 - mFreeSlots)
list《int》 mFreeBuffers(该set中下标的slot表示处于FREE状态,但是有GraphicBuffer)
set《int》 mActiveBuffers(该set中下标的slot表示处于NO FREE状态,且没有GraphicBuffer)
BufferSlot mSlots(保存了所有GraphicBuffer)
在BufferQueueProducer的allocateBuffers函数中,创建一个GraphicBuffer对象。在这里只会创建一个GraphicBuffer,而不是所有Buffer。然后从mFreeSlots取出一个值,mFreeBuffers放入一个值,代表创建了一个Buffer,并将Buffer保存到列表mSlots中。
将DisplayList中的各个绘制操作填充到GraphicBuffer,RenderThread线程通过调用Surface的queueBuffer函数binder通信SurfaceFlinger端,将该GraphicBuffer放回到对应的队列中,至此完成了CPU上的绘制流程。但是并不代表这一帧已经绘制完成,该帧的绘制需要GPU的参与,而CPU不等带GPU绘制完成,是为了让CPU和GPU能够并行操作,减少耗时。

CPU和GPU并行操作GraphicBuffer,必然会牵扯到同步的问题,即CPU如何得知GPU已经完成一帧的绘制,从而引入了Fence。
在queueBuffer时,会给GraphicBuffer添加一个Fence,当SurfaceFlinger端调用BufferQueueConsumer的acquireBuffer函数获取GraphicBuffer用于合成时,若GPU还没有执行到这条Fence指令,则会等待,直到GPU执行到Fence指令,才会从等待中被唤醒。
Fence有sync_pt、sync_timeline和sync_fence三个概念:

自定义view以及View的工作流程

View的工作流程主要指的是measure、Layout、draw三个流程,即测量、布局、绘制。measure测量view的宽高,Layout确定view的宽高和四个顶点,draw绘制到屏幕上。

自定义View答题分为四类:

1、继承View重写onDraw方法

这种方法主要自定义一些不规则的效果,即这种效果不方便通过布局的组合方式实现,用这种方式需要自己支持wrap_content,并且支持padding也需要自己处理

2、继承ViewGroup派生出特殊的Layout

这种方法主要用于实现特殊的布局,即除了LinearLayout、RelativeLayout、FrameLayout这几种系统布局之外,我们重新定义的一种新布局。采用这种方式比较复杂一些,需要适当测处理vIewGroup的量值和布局这两个过程并同时处理子元素的测量和布局过程。

3、继承特定的view(比如TextView)
这种比较常见,一般用于扩展某种已有的View功能比如TextView,这种方法比较容易实现,也不需要自己支持warp_content和padding等。

4、继承特定的ViewGroup(比如 LinearLayout)

这种方法比较常见,当某种效果看起来像几种View组合在一起的时候可以采用这种方式来实现。采用这种方式不需要自己处理ViewGroup的测量和布局这两个过程,一般方式2能实现的效果这个方式都能实现,两者的区别在于方式2更接近View底层。

android帧的绘制过程以及fps的获取

帧的渲染过程中一些关键组件的流程图

任何可以产生图形信息的组件都统称为图像的生产者,比如OpenGL ES, Canvas 2D, 和 媒体解码器等。

SurfaceFlinger是最常见的图像消费者,Window Manager将图形信息收集起来提供给SurfaceFlinger,SurfaceFlinger接受后经过合成再把图形信息传递给显示器。同时,SurfaceFlinger也是唯一一个能够改变显示器内容的服务。SurfaceFlinger使用OpenGL和Hardware Composer来生成surface.

某些OpenGL ES 应用同样也能够充当图像消费者,比如相机可以直接使用相机的预览界面图像流,一些非GL应用也可以是消费者,比如ImageReader 类。

Window Manager是一个用于控制window的系统服务,包含一系列的View。每个Window都会有一个surface,Window Manager会监视window的许多信息,比如生命周期、输入和焦点事件、屏幕方向、转换、动画、位置、转换、z-order等,然后将这些信息(统称window metadata)发送给SurfaceFlinger,这样,SurfaceFlinger就能将window metadata合成为显示器上的surface。

为硬件抽象层(HAL)的子系统。SurfaceFlinger可以将某些合成工作委托给Hardware Composer,从而减轻OpenGL和GPU的工作。此时,SurfaceFlinger扮演的是另一个OpenGL ES客户端,当SurfaceFlinger将一个缓冲区或两个缓冲区合成到第三个缓冲区时,它使用的是OpenGL ES。这种方式会比GPU更为高效。

一般应用开发都要将UI数据使用Activity这个载体去展示,典型的Activity显示流程为:

一般app而言,在任何屏幕上起码有三个layer:

那么android是如何使用这两种合成机制的呢?这里就是Hardware Composer的功劳。处理流程为:

***隐藏网址***

即 Frame Rate,单位 fps,是指 gpu 生成帧的速率,如 33 fps,60fps,越高越好。
但是对于快速变化的游戏而言,你的FPS很难一直保持同样的数值,他会随着你所看到的显示卡所要描画的画面的复杂程度而变化。

安卓系统中有 2 种 VSync 信号:

如上图,CPU/GPU 向 Buffer 中生成图像,屏幕从 Buffer 中取图像、刷新后显示。这是一个典型的生产者——消费者模型。理想的情况是帧率和刷新频率相等,每绘制一帧,屏幕显示一帧。而实际情况是,二者之间没有必然的大小关系,如果没有锁来控制同步,很容易出现问题。

所谓”撕裂”就是一种画面分离的现象,这样得到的画像虽然相似但是上半部和下半部确实明显的不同。这种情况是由于帧绘制的频率和屏幕显示频率不同步导致的,比如显示器的刷新率是75Hz,而某个游戏的FPS是100. 这就意味着显示器每秒更新75次画面,而显示卡每秒更新100次,比你的显示器快33%。

两个缓存区分别为 Back Buffer 和 Frame Buffer。GPU 向 Back Buffer 中写数据,屏幕从 Frame Buffer 中读数据。VSync 信号负责调度从 Back Buffer 到 Frame Buffer 的复制操作,可认为该复制操作在瞬间完成。

双缓冲的模型下,工作流程这样的:

应用和SurfaceFlinger的渲染回路必须同步到硬件的VSYNC,在一个VSYNC事件中,显示器将显示第N帧,SurfaceFlinger合成第N+1帧,app合成第N+2帧。

使用VSYNC同步可以保证延迟的一致性,减少了app和SurfaceFlinger的错误,以及显示在各个阶段之间的偏移。然而,前提是app和SurfaceFlinger每帧时间的变化并不大。因此,从输入到显示的延迟至少有两帧。
为了解决这个问题,您可以使用VSYNC偏移量来减少输入到显示的延迟,其方法为将app和SurfaceFlinger的合成信号与硬件的VSYNC关联起来。因为通常app的合成耗时是小于两帧的(33ms左右)。
VSYNC偏移信号细分为以下3种,它们都保持相同的周期和偏移向量:

注意,当 VSync 信号发出时,如果 GPU/CPU 正在生产帧数据,此时不会发生复制操作。屏幕进入下一个刷新周期时,从 Frame Buffer 中取出的是“老”数据,而非正在产生的帧数据,即两个刷新周期显示的是同一帧数据。这是我们称发生了“掉帧”(Dropped Frame,Skipped Frame,Jank)现象。

第一列t1: when the app started to draw (开始绘制图像的瞬时时间)
第二列t2: the vsync immediately preceding SF submitting the frame to the h/w (VSYNC信令将软件SF帧传递给硬件HW之前的垂直同步时间),也就是对应上面所说的软件Vsync
第三列t3: timestamp immediately after SF submitted that frame to the h/w (SF将帧传递给HW的瞬时时间,及完成绘制的瞬时时间)

每dumpsys SurfaceFlinger一次计算汇总出一个fps,计算规则为:
frame的总数N:127行中的非0行数
绘制的时间T:设t=当前行t2 - 上一行的t2,求出所有行的和∑t
fps=N/T (要注意时间转化为秒)

一次dumpsys SurfaceFlinger会输出127帧的信息,但是这127帧可能是这个样子:

如果t3-t1》16.7ms,则认为发生一次卡顿

设目标fps为target_fps,目标每帧耗时为target_ftime=1000/target_fps
从以下几个维度衡量流畅度:

参考文章:

***隐藏网址***

自定义View(1)

经历过前面三篇啰啰嗦嗦的基础篇之后,终于到了进阶篇,正式进入解析自定义View的阶段。

至于本章节为什么要叫进阶篇(虽然讲的是基础的内容),因为从本篇开始,将会逐渐揭开自定义View的神秘面纱,每一篇都将比上一篇内容更加深入,利用所学的知识能够制作更加炫酷自定义View,就像在台阶上一样,每一篇都更上一层, 帮助大家一步步走向人生巅峰,出任CEO,赢取白富美。 误,是帮助大家更加了解那些炫酷的自定义View是如何制作的,达到举一反三的效果。

作为一个《b》有(hui)追(zhuang)求(B)《/b》的程序员,肯定想做一些让人眼前一亮的程序效果,但是系统提供的那些一般很难满足,为了《b》梦(zhuang)想(B)《/b》就必须要学习一些自定义View。下面我们就了解一些自定义View相关的东西。

自定义ViewGroup一般是利用现有的组件根据特定的布局方式来组成新的组件,大多继承自ViewGroup或各种Layout,包含有子View。

例如:一个应用内的底部导航条中的条目,一般都是上面为图标,下面是文字,那么这两个就可以用自定义ViewGroup组合成为一个Veiw,提供两个属性分别用来设置文字和图片即可,这样使用起来会方便很多。

在没有现成的View,需要自己实现的时候,就使用自定义View,一般继承自View,SurfaceView或其他的View,不包含子View。

例如:定义一个支持自动加载网络图片的ImageView,或制作一种特殊的动画效果。

《b》一般来说,自定义View在大多数情况下都有替代方案,利用图片或者组合动画来实现,但是使用后者可能会面临内存耗费过大,制作麻烦更诸多问题。《/b》

View的构造函数有四种重载分别如下

可以看出,关于View构造函数的参数有多有少,先排除几个不常用的,留下常用的再研究。

《b》有四个参数的构造函数在API21的时候才添加上,我一般不使用,暂不考虑。《/b》

有三个参数的构造函数中第三个参数是默认的Style,这里的默认的Style是指它在当前Application或Activity所用的Theme中的默认Style,且只有在明确调用的时候才会生效,以系统中的ImageButton为例说明:

《b》注意:即使你在View中使用了Style这个属性也不会调用三个参数的构造函数,所调用的依旧是两个参数的构造函数。《/b》

《b》由于三个参数的构造函数第三个参数一般不用,暂不考虑,第三个参数的具体用法会在以后用到的时候详细介绍。《/b》

排除了两个之后,只剩下一个参数和两个参数的构造函数,他们的详情如下:

以下方法调用的是《b》一个参数《/b》的构造函数:

以下方法调用的是《b》两个参数《/b》的构造函数:

关于构造函数先讲这么多,关于如何自定义属性和使用attrs中的内容,在后面会详细讲解,目前只需要知道这两个构造函数在何时调用即可。

========

测量View大小使用的是onMeasure函数,我们可以从这两个参数取出宽高的相关数据:

从上面可以看出 onMeasure 函数中有 widthMeasureSpec 和 heightMeasureSpec 这两个 int 类型的参数, 毫无疑问他们是和宽高相关的, 《b》但它们其实不是宽和高, 而是由宽、高和各自方向上对应的模式来合成的一个值:《/b》

在int类型的32位二进制位中,31-30这两位表示模式,29~0这三十位表示宽和高的实际值。

以数值1080(二进制为: 1111011000)为例(其中模式和实际数值是连在一起的,为了展示我将他们分开了):

实际上关于上面的东西了解即可,在实际运用之中只需要记住有三种模式,用 MeasureSpec 的 getSize是获取数值, getMode是获取模式即可。

如果对View的宽高进行修改了,《b》不要调用super.onMeasure(widthMeasureSpec,heightMeasureSpec);《/b》
要调用《b》setMeasuredDimension(widthsize,heightsize);《/b》 这个函数。

======

这个函数在视图大小发生改变时调用:

onSizeChanged如下:

可以看出,它又四个参数,分别为 宽度,高度,上一次宽度,上一次高度。

这个函数比较简单,我们只需关注 宽度(w), 高度(h) 即可,这两个参数就是View最终的大小。

=========

《b》确定布局的函数是onLayout,它用于确定子View的位置,在自定义ViewGroup中会用到,他调用的是子View的layout函数。《/b》

不过关于View的layout函数我们一般无需关注,因为在一般情况下我们只需关注View自身的坐标系即可,除非View状态与在父VIew所处位置相关。

在自定义ViewGroup中,onLayout一般是循环取出子View,然后经过计算得出各个子View位置的坐标值,然后用以下函数设置子View位置。

四个参数分别为:

具体可以参考 坐标系 这篇文章:

PS:关于onLayout这个函数在讲解自定义ViewGroup的时候会详细讲解。

========

onDraw是实际绘制的部分,也就是我们真正关心的部分,使用的是Canvas绘图。

关于Canvas绘图另分一章吧,本来想写一些关于Canvas基本操作的的,可是篇幅太长了QAQ, 留个尾巴下一篇再写吧,毕竟Canvas绘图也是一个比较庞大的东西,也不是三言两语就能讲明白的,就到这里吧。

======

自定义完View之后,一般会对外暴露一些接口,用于操作View的相关属性,控制View的状态等,或者需要监听View的变化,具体还是稍后再讲吧(继续挖坑)。

PS :实际上ViewGroup是View的一个子类。

View
ViewGroup
View.MeasureSpec
onMeasure,MeasureSpec源码 流程 思路详解
Android中自定义样式与View的构造函数中的第三个参数defStyle的意义
android view构造函数研究
Android View构造方法第三参数使用方法详解
Android 自定义View onMeasure方法的实现
Android API指南(二)自定义控件02之 onMeasure
Android中View的绘制过程 onMeasure方法简述

Android 重学系列 View的绘制流程(六) 硬件渲染(上)

本文开始聊聊Android中的硬件渲染。如果跟着我的文章顺序,从SF进程到App进程的绘制流程一直阅读,我们到这里已经有了一定的基础,可以试着进行横向比对如Chrome浏览器渲染流程,看看软件渲染,硬件渲染,SF合成都做了什么程度的优化。

先让我们回顾一下负责硬件渲染的主体对象ThreadedRenderer在整个绘制流程中做了哪几个步骤。

在硬件渲染的过程中,有一个很核心的对象RenderNode,作为每一个View绘制的节点对象。

当每一次进行准备进行绘制的时候,都会雷打不动执行如下三个步骤:

***隐藏网址***

实际上整个硬件渲染的设计还是比较庞大。因此本文先聊聊ThreadedRender整个体系中主要对象的构造以及相关的原理。

首先来认识下面几个重要的对象有一个大体的印象。

在Java层中面向Framework中,只有这么多,下面是一一映射的简图。

能看到实际上RenderNode也会跟着View 树的构建同时一起构建整个显示层级。也是因此ThreadedRender也能以RenderNode为线索构建出一套和软件渲染一样的渲染流程。

仅仅这样?如果只是这么简单,知道我习惯的都知道,我喜欢把相关总结写在最后。如果把总揽写在正文开头是因为设计比较繁多。因为我们如果以流水线的形式进行剖析容易造成迷失细节的困境。

让我继续介绍一下,在硬件渲染中native层的核心对象。

如下是一个思维导图:

有这么一个大体印象后,就不容易迷失在源码中。我们先来把这些对象的实例化以及上面列举的ThreadedRenderer在ViewRootImpl中执行行为的顺序和大家来聊聊其原理,先来看看ThreadedRenderer的实例化。

当发现mSurfaceHolder为空的时候会调用如下函数:

而这个方法则调用如下的方法对ThreadedRenderer进行创建:

文件:/ frameworks / base / core / java / android / view / ThreadedRenderer.java

能不能创建的了ThreadedRenderer则决定于全局配置。如果ro.kernel.qemu的配置为0,说明支持OpenGL 则可以直接返回true。如果qemu.gles为-1说明不支持OpenGL es返回false,只能使用软件渲染。如果设置了qemu.gles并大于0,才能打开硬件渲染。

我们能看到ThreadedRenderer在初始化,做了三件事情:

关键是看1-3点中ThreadRenderer都做了什么。

文件:/ frameworks / base / core / jni / android_view_ThreadedRenderer.cpp

能看到这里是直接实例化一个RootRenderNode对象,并把指针的地址直接返回。

能看到RootRenderNode继承了RenderNode对象,并且保存一个JavaVM也就是我们所说的Java虚拟机对象,一个java进程全局只有一个。同时通过getForThread方法,获取ThreadLocal中的Looper对象。这里实际上拿的就是UI线程的Looper。

在这个构造函数有一个mDisplayList十分重要,记住之后会频繁出现。接着来看看RenderNode的头文件:
文件:/ frameworks / base / libs / hwui / RenderNode.h

实际上我把几个重要的对象留下来:

文件:/ frameworks / base / core / java / android / view / RenderNode.java

能看到很简单,就是包裹一个native层的RenderNode返回一个Java层对应的对象开放Java层的操作API。

能看到这个过程生成了两个对象:

这个对象实际上让RenderProxy持有一个创建动画上下文的工厂。RenderProxy可以通过ContextFactoryImpl为每一个RenderNode创建一个动画执行对象的上下文AnimationContextBridge。

文件:/ frameworks / base / libs / hwui / renderthread / RenderProxy.cpp

在这里有几个十分重要的对象被实例化,当然这几个对象在聊TextureView有聊过( SurfaceView和TextureView 源码浅析 ):

我们依次看看他们初始化都做了什么。

文件:/ frameworks / base / libs / hwui / renderthread / RenderThread.cpp

能看到其实就是简单的调用RenderThread的构造函数进行实例化,并且返回对象的指针。

RenderThread是一个线程对象。先来看看其头文件继承的对象:
文件:/ frameworks / base / libs / hwui / renderthread / RenderThread.h

其中RenderThread的中进行排队处理的任务队列实际上是来自ThreadBase的WorkQueue对象。

文件:/ frameworks / base / libs / hwui / thread / ThreadBase.h

ThreadBase则是继承于Thread对象。当调用start方法时候其实就是调用Thread的run方法启动线程。

另一个更加关键的对象,就是实例化一个Looper对象到WorkQueue中。而直接实例化Looper实际上就是新建一个Looper。但是这个Looper并没有获取当先线程的Looper,这个Looper做什么的呢?下文就会揭晓。

WorkQueue把一个Looper的方法指针设置到其中,其作用可能是完成了某一件任务后唤醒Looper继续工作。

而start方法会启动Thread的run方法。而run方法最终会走到threadLoop方法中,至于是怎么走进来的,之后有机会会解剖虚拟机的源码线程篇章进行讲解。

在threadloop中关键的步骤有如下四个:

在这个过程中创建了几个核心对象:

另一个核心的方法就是initializeDisplayEventReceiver,这个方法为WorkQueue的Looper注册了监听:

能看到在这个Looper中注册了对DisplayEventReceiver的监听,也就是Vsync信号的监听,回调方法为displayEventReceiverCallback。

我们暂时先对RenderThread的initializeDisplayEventReceiver方法探索到这里,我们稍后继续看看回调后的逻辑。

文件:/ frameworks / base / libs / hwui / thread / ThreadBase.h

能看到这里的逻辑很简单实际上就是调用Looper的pollOnce方法,阻塞Looper中的循环,直到Vsync的信号到来才会继续往下执行。详细的可以阅读我写的 Handler与相关系统调用的剖析 系列文章。

文件:/ frameworks / base / libs / hwui / thread / ThreadBase.h

实际上调用的是WorkQueue的process方法。

文件:/ frameworks / base / libs / hwui / thread / WorkQueue.h

能看到这个过程中很简单,几乎和Message的loop的逻辑一致。如果Looper的阻塞打开了,则首先找到预计执行时间比当前时刻都大的WorkItem。并且从mWorkQueue移除,最后添加到toProcess中,并且执行每一个WorkItem的work方法。而每一个WorkItem其实就是通过从某一个压入方法添加到mWorkQueue中。

到这里,我们就明白了RenderThread中是如何消费渲染任务的。那么这些渲染任务又是哪里诞生呢?

上文聊到了在RenderThread中的Looper会监听Vsync信号,当信号回调后将会执行下面的回调。

能看到这个方法的核心实际上就是调用drainDisplayEventQueue方法,对ui渲染任务队列进行处理。

能到在这里mVsyncRequested设置为false,且mFrameCallbackTaskPending将会设置为true,并且调用queue的postAt的方法执行ui渲染方法。

还记得queue实际是是指WorkQueue,而WorkQueue的postAt方法实际实现如下:
/ frameworks / base / libs / hwui / thread / WorkQueue.h

情景带入,当一个Vsync信号达到Looper的监听者,此时就会通过WorkQueue的drainDisplayEventQueue 压入一个任务到队列中。

每一个默认的任务都是执行dispatchFrameCallback方法。这里的判断mWorkQueue中是否存在比当前时间更迟的时刻,并返回这个WorkItem。如果这个对象在头部needsWakeup为true,说明可以进行唤醒了。而mWakeFunc这个方法指针就是上面传下来:

把阻塞的Looper唤醒。当唤醒后就继续执行WorkQueue的process方法。也就是执行dispatchFrameCallbacks方法。

在这里执行了两个事情:

先添加到mPendingRegistrationFrameCallbacks集合中,在上面提到过的threadLoop中,会执行如下逻辑:

如果mPendingRegistrationFrameCallbacks大小不为0,则的把mPendingRegistrationFrameCallbacks中的IFrameCallback全部迁移到mFrameCallbacks中。

而这个方法什么时候调用呢?稍后就会介绍。其实这部分的逻辑在TextureView的解析中提到过。

接下来将会初始化一个重要对象:

这个对象名字叫做画布的上下文,具体是什么上下文呢?我们现在就来看看其实例化方法。
文件:/ frameworks / base / libs / hwui / renderthread / CanvasContext.cpp

文件:/ device / generic / goldfish / init.ranchu.rc

在init.rc中默认是opengl,那么我们就来看看下面的逻辑:

首先实例化一个OpenGLPipeline管道,接着OpenGLPipeline作为参数实例化CanvasContext。

文件:/ frameworks / base / libs / hwui / renderthread / OpenGLPipeline.cpp

能看到在OpenGLPipeline中,实际上就是存储了RenderThread对象,以及RenderThread中的mEglManager。透过OpenGLPipeline来控制mEglManager进而进一步操作OpenGL。

做了如下操作:

文件:/ frameworks / base / libs / hwui / renderstate / RenderState.cpp

文件:/ frameworks / base / libs / hwui / renderthread / DrawFrameTask.cpp

实际上就是保存这三对象RenderThread;CanvasContext;RenderNode。

文件:/ frameworks / base / core / jni / android_view_ThreadedRenderer.cpp

能看到实际上就是调用RenderProxy的setName方法给当前硬件渲染对象设置名字。

文件:/ frameworks / base / libs / hwui / renderthread / RenderProxy.cpp

能看到在setName方法中,实际上就是调用RenderThread的WorkQueue,把一个任务队列设置进去,并且调用runSync执行。

能看到这个方法实际上也是调用post执行排队执行任务,不同的是,这里使用了线程的Future方式,阻塞了执行,等待CanvasContext的setName工作完毕。

如何用3dmax做建筑效果图

(1)前期准备在作图之前首先要确定所用的单位是毫米,然后用Shift+I将CAD导3DM中。将导入的CAD图形移动到世界坐标(0,0,0),然后全选导入的线将其群组为底图,冻结底图。
(2)接着调入其他的
CAD平面。打开二维捕捉,将捕捉点设置为垂直和中点,这样便于前期绘图操作。将导入的图层外轮廓叠放在一起,然后将上面图层在TOP图中沿Y轴移动作为参考。同理导入建筑的各立面图,将其群组,使其外轮廓线与平面外轮廓对齐,再冻结,作为参考。沿X
轴90°旋转立面。如果发现无法捕捉到冻结后的点的话,一定要查看捕捉设置。
(3)生成实体。有了底图的帮衬,在上面拉墙体就极为容易了,这时候重
要的是要保证新建的墙体的面最少,计算量尽可能的少。最好是重新用线妙出平面墙的形
状,这样画出来的线才是各整体。此时,注意要将“Start New Shape”后面的对号勾掉,画出的线才可能是整体的。然后给墙体体线添加”Extrude”命令,设置建筑的外墙宽度370m或240mm.同时为了 节省面,立面的窗框也作为整体处理。这时候给强体赋上材质。
(4)
3D
建模过程中有很多的细小构件,一定要在每建完一个模型后都即使
的给它赋上材质,
同时每个材质在材质球上最好都标上名字,
将来如果需要
进一步调整,就可以选择材质球,将使用同一材质的物体选出来,通过
Alt
+
W
键来对单独视窗里的物体进行。

在确定修改之后,对使用同一材
质的物体添加
editable
mesh
命令,进行塌陷处理为同一物体,这样就可以
尽可能的减少面与线的运算,提高效率。

5)摄像机。在完成塌陷后就可以对物体追加目标聚光灯了。在任一视窗
内按C键,就将其切换成摄像机窗口了。
然后调整摄像机及目标点的高度,
一般是在前视图种将其向
Y
方向移动
1600mm
即可。此时为了便于观察,一
般都会对窗口版面做一调整。
6)在
Viewport Configuration/layout
中选择左右平铺的方式,它能保
证你有最大的视窗和最实用的操作界面。
在对摄像窗口进行渲染的时候,
会
出现一些不良的视角,这就需要对摄像机的焦距进行调整了,一般取
28mm
或
35mm
。同时通过鼠标对摄像边界的调整和移动来矫正渲染视窗。选择
view/Blowup
的渲染模式更是可以得到好的构图效果。

如果你还想了解更多这方面的信息,记得收藏关注本站。

view绘制流程(Andoird WindowManager管理Window的流程)

本文编辑:admin

本文相关文章:


undergraduate怎么读(这3个英语yawn ;undergraduate; teenager ;怎么读的)

undergraduate怎么读(这3个英语yawn ;undergraduate; teenager ;怎么读的)

各位老铁们,大家好,今天由我来为大家分享undergraduate怎么读,以及这3个英语yawn ;undergraduate; teenager ;怎么读的的相关问题知识,希望对大家有所帮助。如果可以帮助到大家,还望关注收藏下本站,您的支

2026年10月2日 03:30

discovering(discover是什么意思)

discovering(discover是什么意思)

大家好,如果您还对discovering不太了解,没有关系,今天就由本站为大家分享discovering的知识,包括discover是什么意思的问题都会给大家分析到,还望可以解决大家的问题,下面我们就开始吧!

2026年10月9日 04:40

sql2000怎么新建查询(sql server 新建查询功能怎么实现)

sql2000怎么新建查询(sql server 新建查询功能怎么实现)

“sql2000怎么新建查询”相关信息最新大全有哪些,这是大家都非常关心的,接下来就一起看看sql2000怎么新建查询(sql server 新建查询功能怎么实现)!

2026年10月5日 14:10

local是什么文件夹(C:\Users\hp\AppData\Local这文件夹是做什么用的)

local是什么文件夹(C:\Users\hp\AppData\Local这文件夹是做什么用的)

大家好,如果您还对local是什么文件夹不太了解,没有关系,今天就由本站为大家分享local是什么文件夹的知识,包括C:\Users\hp\AppData\Local这文件夹是做什么用的的问题都会给大家分析到,还望可以解决大家的问题,下面我

2026年10月3日 21:50

microsoft silverlight v3(Microsoft Silverlight安装失败)

microsoft silverlight v3(Microsoft Silverlight安装失败)

大家好,如果您还对microsoft silverlight v3不太了解,没有关系,今天就由本站为大家分享microsoft silverlight v3的知识,包括Microsoft Silverlight安装失败的问题都会给大家分析到

2026年9月28日 22:50

win10如何安装activex控件(win10怎么安装Adobe Flash Player Activex 我没收到微软的补丁 结果Flash没法用了)

win10如何安装activex控件(win10怎么安装Adobe Flash Player Activex 我没收到微软的补丁 结果Flash没法用了)

这篇文章给大家聊聊关于win10如何安装activex控件,以及win10怎么安装Adobe Flash Player Activex 我没收到微软的补丁 结果Flash没法用了对应的知识点,希望对各位有所帮助,不要忘了收藏本站哦。

2026年9月26日 17:40

mousewithoutborders(mouse without borders在win7不能启动)

mousewithoutborders(mouse without borders在win7不能启动)

今天给各位分享mouse without borders在win7不能启动的知识,其中也会对mouse without borders在win7不能启动进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

2026年9月19日 14:10

bootstrapper已停止工作(打开steam的时候突然蹦出steam client bootstrapper 已停止工作是怎么回事)

bootstrapper已停止工作(打开steam的时候突然蹦出steam client bootstrapper 已停止工作是怎么回事)

大家好,今天小编来为大家解答以下的问题,关于bootstrapper已停止工作,打开steam的时候突然蹦出steam client bootstrapper 已停止工作是怎么回事这个很多人还不知道,现在让我们一起来看看吧!

2026年9月7日 03:20

sql server2008支持什么系统(sql server 2008 都可以安装在什么操作系统中)

sql server2008支持什么系统(sql server 2008 都可以安装在什么操作系统中)

本篇文章给大家谈谈sql server2008支持什么系统,以及sql server 2008 都可以安装在什么操作系统中对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。

2026年9月5日 23:10

assertequals断言的使用(python断言assertequals是什么意思)

assertequals断言的使用(python断言assertequals是什么意思)

大家好,assertequals断言的使用相信很多的网友都不是很明白,包括python断言assertequals是什么意思也是一样,不过没有关系,接下来就来为大家分享关于assertequals断言的使用和python断言asserteq

2026年9月4日 09:30

更多文章:


form表单制作(为什么制作的form表单会在网页显示中多出一行)

form表单制作(为什么制作的form表单会在网页显示中多出一行)

大家好,如果您还对form表单制作不太了解,没有关系,今天就由本站为大家分享form表单制作的知识,包括为什么制作的form表单会在网页显示中多出一行的问题都会给大家分析到,还望可以解决大家的问题,下面我们就开始吧!

2026年10月11日 09:10

teammate(teammate,company,partner)

teammate(teammate,company,partner)

各位老铁们,大家好,今天由我来为大家分享teammate,以及teammate,company,partner的相关问题知识,希望对大家有所帮助。如果可以帮助到大家,还望关注收藏下本站,您的支持是我们最大的动力,谢谢大家了哈,下面我们开始吧

2026年10月11日 06:10

javascript arraybuffer(javascript可以把base64编码转换成二进制代码吗求示例代码!)

javascript arraybuffer(javascript可以把base64编码转换成二进制代码吗求示例代码!)

其实javascript arraybuffer的问题并不复杂,但是又很多的朋友都不太了解javascript可以把base64编码转换成二进制代码吗求示例代码!,因此呢,今天小编就来为大家分享javascript arraybuffer的

2026年10月11日 04:00

text函数公式(excel中round和text函数的区别是什么)

text函数公式(excel中round和text函数的区别是什么)

“text函数公式”相关信息最新大全有哪些,这是大家都非常关心的,接下来就一起看看text函数公式(excel中round和text函数的区别是什么)!

2026年10月11日 03:50

pascal编程软件(介绍一下pascal语言!)

pascal编程软件(介绍一下pascal语言!)

大家好,如果您还对pascal编程软件不太了解,没有关系,今天就由本站为大家分享pascal编程软件的知识,包括介绍一下pascal语言!的问题都会给大家分析到,还望可以解决大家的问题,下面我们就开始吧!

2026年10月11日 02:40

google chrome打不开(chrome浏览器打不开怎么回事 浏览器打不开的处理方法)

google chrome打不开(chrome浏览器打不开怎么回事 浏览器打不开的处理方法)

本篇文章给大家谈谈google chrome打不开,以及chrome浏览器打不开怎么回事 浏览器打不开的处理方法对应的知识点,文章可能有点长,但是希望大家可以阅读完,增长自己的知识,最重要的是希望对各位有所帮助,可以解决了您的问题,不要忘了

2026年10月11日 02:00

websocket整合springboot(Springboot整合Websocket遇到的坑)

websocket整合springboot(Springboot整合Websocket遇到的坑)

大家好,websocket整合springboot相信很多的网友都不是很明白,包括Springboot整合Websocket遇到的坑也是一样,不过没有关系,接下来就来为大家分享关于websocket整合springboot和Springbo

2026年10月11日 01:40

小米官方首爆miui14(miui14耗电严重官方回应)

小米官方首爆miui14(miui14耗电严重官方回应)

各位老铁们好,相信很多人对小米官方首爆miui14都不是特别的了解,因此呢,今天就来为大家分享下关于小米官方首爆miui14以及miui14耗电严重官方回应的问题知识,还望可以帮助大家,解决大家的一些困惑,下面一起来看看吧!

2026年10月11日 00:40

drawerlayout(android 怎样让drawerlayout设置的侧滑菜单的内容充满屏幕)

drawerlayout(android 怎样让drawerlayout设置的侧滑菜单的内容充满屏幕)

本篇文章给大家谈谈drawerlayout,以及android 怎样让drawerlayout设置的侧滑菜单的内容充满屏幕对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。

2026年10月10日 19:20

xor四位数怎么运算(单片机怎样用C语言实现4个数字间的异或)

xor四位数怎么运算(单片机怎样用C语言实现4个数字间的异或)

大家好,今天小编来为大家解答以下的问题,关于xor四位数怎么运算,单片机怎样用C语言实现4个数字间的异或这个很多人还不知道,现在让我们一起来看看吧!

2026年10月10日 17:50

最近更新

thinkpade470c加内存条(thinkpad e470c内存条是什么牌子如果要添加4g内存什么牌子比较好)
2026-10-11 09:00:03 浏览:0
frontpage的主要功能(frontpage是什么)
2026-10-11 08:40:18 浏览:0
ideapad15alc7能玩什么游戏(联想ideapad15可以玩刺客信条启示录吗)
2026-10-11 08:10:05 浏览:0
热门文章

打印机m7400(m7400打印机清零方法)
2026-08-29 07:50:01 浏览:5
acrobat各版本区别(Acrobat XI Pro与 Acrobat PRO DC什么区别)
2026-08-29 22:30:20 浏览:2
联想y510p怎么升级(联想y510p换cpu)
2026-08-17 03:30:04 浏览:2
标签列表