位置: 编程技术 - 正文

Android应用程序UI硬件加速渲染技术简要介绍和学习计划(Android应用程序结构)

编辑:rootadmin

推荐整理分享Android应用程序UI硬件加速渲染技术简要介绍和学习计划(Android应用程序结构),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:Android应用程序设计第三版答案,Android应用程序的主要语言是Java,Android应用程序商店,Android应用程序开发,Android应用程序开发,Android应用程序设计第三版答案,Android应用程序开发,Android应用程序开发,内容如对您有帮助,希望把文章链接给更多的朋友!

Android系统的流畅性一直被拿来与iOS比较,并且认为不如后者。这一方面与Android设备硬件质量参差不齐有关,另一方面也与Android系统的实现有关。例如在3.0前,Android应用程序UI绘制不支持硬件加速。不过从4.0开始,Android系统一直以“run fast, smooth, and responsively”为目标对UI进行优化。本文对这些优化进行简要介绍和制定学习计划。

老罗的新浪微博: 注意,上面我们说Android系统不支持硬件加速的UI 绘制,针对的是Android应用程序2D UI绘制。对于3D UI,例如游戏,一直是支持硬件加速渲染的。此外,从前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划、Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划和Android应用程序窗口(Activity)实现框架简要介绍和学习计划这三个系列的文章可以知道,Android系统的UI从绘制到显示到屏幕是分两步进行的:第一步是在Android应用程序进程这一侧进行的;第二步是在SurfaceFlinger进程这一侧进行的。前一步将UI绘制一个图形缓冲区中,并且将该图形缓冲区交给后一步进行合成以及显示在屏幕中。其中,后一步的UI合成一直都是以硬件加速方式完成的。

在支持Android应用程序UI硬件加速渲染之前,Android应用程序UI的绘制是以软件方式进行的,为了更好地理解Android应用程序UI硬件加速渲染技术,我们先回顾在Android应用程序窗口(Activity)实现框架简要介绍和学习计划这个系列的文章提及的软件渲染技术,如图1所示:

图1 Android应用程序UI软件渲染过程

在Android应用程序进程这一侧,每一个窗口都关联有一个Surface。每当窗口需要绘制UI时,就会调用其关联的Surface的成员函数lock获得一个Canvas,其本质上是向SurfaceFlinger服务Dequeue一个Graphic Buffer。Canvas封装了由Skia提供的2D UI绘制接口,并且都是在前面获得的Graphic Buffer上面进行绘制的。绘制完成之后,Android应用程序进程再调用前面获得的Canvas的成员函数unlockAndPost请求显示在屏幕中,其本质上是向SurfaceFlinger服务Queue一个Graphic Buffer,以便SurfaceFlinger服务可以对Graphic Buffer的内容进行合成,以及显示到屏幕上去。

接下来我们再来看Android应用程序UI硬件加速渲染技术,如图2所示:

图2 Android应用程序UI硬件加速渲染过程

这这里我们首先要明确什么是硬件加速渲染,其实就是通过GPU来进行渲染。GPU作为一个硬件,用户空间是不可以直接使用的,它是由GPU厂商按照Open GL规范实现的驱动间接进行使用的。也就是说,如果一个设备支持GPU硬件加速渲染,那么当Android应用程序调用Open GL接口来绘制UI时,Android应用程序的UI就是通过硬件加速技术进行渲染的。因此,在接下来的描述中,我们提及到GPU、硬件加速和Open GL时,它们表达的意思都是等价的。

从图2可以看到,硬件加速渲染和软件渲染一样,在开始渲染之前,都是要先向SurfaceFlinger服务Dequeue一个Graphic Buffer。不过对硬件加速渲染来说,这个Graphic Buffer会被封装成一个ANativeWindow,并且传递给Open GL进行硬件加速渲染环境初始化。在Android系统中,ANativeWindow和Surface可以是认为等价的,只不过是ANativeWindow常用于Native层中,而Surface常用于Java层中。另外,我们还可以将ANativeWindow和Surface看作是像Skia和Open GL这样图形渲染库与操作系统底层的图形系统建立连接的一个桥梁。

Open GL获得了一个ANativeWindow,并且进行了硬件加速渲染环境初始化工作之后,Android应用程序就可以调用Open GL提供的API进行UI绘制了,绘制出来内容就保存在前面获得的Graphic Buffer中。当绘制完毕,Android应用程序再调用libegl库提供的一个eglSwapBuffer接口请求将绘制好的UI显示到屏幕中,其本质上与软件渲染过程是一样的,都是向SurfaceFlinger服务Queue一个Graphic Buffer,以便SurfaceFlinger服务可以对Graphic Buffer的内容进行合成,以及显示到屏幕上去。

关于Android应用程序UI的硬件加速渲染过程中涉及到Open GL环境初始化和绘制的简化版本,可以参考前面Android系统的开机画面显示过程分析一文提到的Android系统开机动画的实现。在Android系统的开机画面显示过程分析这篇文章中,开机动画其实是由一个/system/bin/bootanimation程序实现的。这个程序可以看成是一个没有使用Android SDK来开发的一个Native应用程序。

在这个系列的文章中,我们将通过Android 5.0的源码来分析Android应用程序UI的硬件加速渲染技术。不过为了更好地理解Android 5.0的硬件加速渲染实现,我们有必要先了解从Android 3.0以来,Android应用程序UI硬件加速渲染的进化历史:

Android应用程序UI硬件加速渲染技术简要介绍和学习计划(Android应用程序结构)

1. Android 3.0,也就是Honeycomb版本,开始引用OpenGLRenderer图形渲染库,支持Android应用程序UI可选地使用硬件加速渲染。

2. Android 4.0,也就是Ice Cream Sandwich版本,要求设备默认支持Android应用程序UI硬件加速渲染,并且增加一个TextureView控件,该控件直接支持以Open GL纹理的形式来绘制UI。

3. Android 4.1、4.2和4.3,也就是Jelly Bean版本,加入了Project Butter(黄油计划)的特性,包括:A. 通过Vsync信号来同步UI绘制和动画,使得它们可以获得一个达到fps的固定的帧率;B. 三缓冲支持,改善GPU和CPU之间绘制节奏不一致的问题;C. 将用户输入,例如touch event,同步到下一个Vsync信号到来时再处理;D. 预测用户的touch行为,以获得更好的交互响应;E. 每次用户touch屏幕时,进行CPU Input Boost,以便减少处理延时。

4. Android 4.4,也就是KitKat版本,一方面通过优化内存使用,另一方面是可选地支持使用ART运行时替换Dalvik虚拟机,来提高应用程序的运行效率,使得其UI更流畅。

5. Android 5.0,也就是Lollipop版本,ART运行时引进了Compacting GC,进一步优化了Android应用程序的内存使用,并且ART运行时正式替换了Dalvik虚拟机,同时,Android应用程序增加了一个Render Thread,专门负责UI渲染和动画显示。

从Android应用程序UI硬件加速渲染的进化历史可以看出,Android系统确实是在践行"run fast, smooth, and responsively"的宏伟计划,并且也是做到了。

有了前面的基础知识之后,我们接下来再来Android 5.0的窗口和动画是如何通过硬件加速技术来渲染的,如图3所示:

图3 Android应用程序窗口和动画的硬件加速渲染框架

在Android应用程序窗口中,每一个View都抽象为一个Render Node,而且如果一个View设置有Background,这个Background也被抽象为一个Render Node。这是由于在OpenGLRenderer库中,并没有View的概念,所有的一切可绘制的元素都抽象为一个Render Node。

每一个Render Node都关联有一个Display List Renderer。这里又涉及到另外一个概念——Display List。注意,这个Display List不是Open GL里面的Display List,不过它们在概念上是差不多的。Display List是一个绘制命令缓冲区。也就是说,当View的成员函数onDraw被调用时,我们调用通过参数传递进来的Canvas的drawXXX成员函数绘制图形时,我们实际上只是将对应的绘制命令以及参数保存在一个Display List中。接下来再通过Display List Renderer执行这个Display List的命令,这个过程称为Display List Replay。

引进Display List的概念有什么好处呢?主要是两个好处。第一个好处是在下一帧绘制中,如果一个View的内容不需要更新,那么就不用重建它的Display List,也就是不需要调用它的onDraw成员函数。第二个好处是在下一帧中,如果一个View仅仅是一些简单的属性发生变化,例如位置和Alpha值发生变化,那么也无需要重建它的Display List,只需要在上一次建立的Display List中修改一下对应的属性就可以了,这也意味着不需要调用它的onDraw成员函数。这两个好处使用在绘制应用程序窗口的一帧时,省去很多应用程序代码的执行,也就是大大地节省了CPU的执行时间。

注意,只有使用硬件加速渲染的View,才会关联有Render Node,也就才会使用到Display List。我们知道,目前并不是所有的2D UI绘制命令都是GPU可以支持的。这一点具体可以参考官方说明文档: UI绘制命令的View,只能通过软件方式来渲染。具体的做法是将创建一个新的Canvas,这个Canvas的底层是一个Bitmap,也就是说,绘制都发生在这个Bitmap上。绘制完成之后,这个Bitmap再被记录在其Parent View的Display List中。而当Parent View的Display List的命令被执行时,记录在里面的Bitmap再通过Open GL命令来绘制。

另一方面,对于前面提到的在Android 4.0引进的TextureView,它也不是通过Display List来绘制。由于它的底层实现直接就是一个Open GL纹理,因此就可以跳过Display List这一中间层,从而提高效率。这个Open GL纹理的绘制通过一个Layer Renderer来封装。Layer Renderer和Display List Renderer可以看作是同一级别的概念,它们都是通过Open GL命令来绘制UI元素的。只不过前者操作的是Open GL纹理,而后者操作的是Display List。

我们知道,Android应用程序窗口的View是通过树形结构来组织的。这些View不管是通过硬件加速渲染还是软件渲染,或者是一个特殊的TextureView,在它们的成员函数onDraw被调用期间,它们都是将自己的UI绘制在Parent View的Display List中。其中,最顶层的Parent View是一个Root View,它关联的Root Node称为Root Render Node。也就是说,最终Root Render Node的Display List将会包含有一个窗口的所有绘制命令。在绘制窗口的下一帧时,Root Render Node的Display List都会通过一个Open GL Renderer真正地通过Open GL命令绘制在一个Graphic Buffer中。最后这个Graphic Buffer被交给SurfaceFlinger服务进行合成和显示。

上面分析的应用程序UI绘制机制还没有涉及到动画。当一个View需要以动画的形式显示时,我们可以通过调用这个View的成员函数animate获得一个ViewPropertyAnimator。ViewPropertyAnimator像View一样,也被抽象为一个Render Node。不过这个Render Node的处理方式与View的Render Node的处理方式不同,它们会被注册到Android应用程序的Render Thread中,然后由Render Thread负责执行它所蕴含着的动画,直到动画结束为止。这样就不需要Android应用程序的主线程处理动画了,使得Android应用程序的主线程可以更专注地处理用户输入,从而使用Android应用程序UI具有更好的响应性。

更进一步地,如果我们调用了ViewPropertyAnimator的成员函数withLayer,那么目标View的动画可以得到更一步的优化。回忆TextureView的特点,它是直接通过Open GL纹理来绘制,这样可以省去Display List这一中间步骤。同样的,当我们调用了ViewPropertyAnimator的成员函数withLayer时,目标View的Layer Type将被临时修改为LAYER_TYPE_HARDWARE。对于Layer Type为LAYER_TYPE_HARDWARE的View,它将直接通过Open GL的Frame Buffer Object(FBO)来实现,这样也是可以提高渲染效率。等到动画结束的时候,目标View的Layer Type将恢复为原来设置的类型。

标签: Android应用程序结构

本文链接地址:https://www.jiuchutong.com/biancheng/371103.html 转载请保留说明!

上一篇:为什么调用 FragmentPagerAdapter.notifyDataSetChanged() 并不能更新其 Fragment? 【转载】(为什么调用不了函数)

下一篇:QT for Android HelloWorld实现

  • 个人独资企业税种有哪些
  • 进项税和销项税怎么理解
  • 其他综合收益算营业利润吗
  • 对方开具红字发票信息表后我们怎么开负数
  • 小企业会计准则调整以前年度费用分录
  • 物流公司主营业务范围
  • 应交税费负数调整
  • 往来账审计存在问题及建议
  • 年底没计提年终奖,下一年发放要怎么做
  • 建筑业咨询费有哪些
  • 会计中制造费用是什么意思
  • 用友如何取消月末结转
  • 装修工程分期开票分期付款如何作账?
  • 多层股权结构设计控制法
  • 视同销售可以抵扣销项税吗?
  • 汽车配件税收分类编码
  • 文化事业费征收
  • 应交税费借方余额填列资产负债表
  • 全资子公司合并抵消
  • 总分类账的账簿启用表怎么填
  • 房产评估增值部门有哪些
  • 业务招待费增值税是进项税吗
  • 6%技术服务费是普票还是专票
  • 小规模收入未达50万
  • 样品寄送时运费支付的对策
  • 营业外支出的税额要进项转出吗
  • 快启动u盘装系统教程
  • 分配结转本月发出材料成本会计分录
  • linux xmrig
  • PHP:pcntl_sigwaitinfo()的用法_PCNTL函数
  • php imagettftext
  • 迷迭香怎么养殖方法
  • php中imagecreatefromjpeg
  • echarts柱形图
  • 印花税应税凭证数量是什么意思
  • 微信小程序基于什么技术
  • pytorch csdn
  • 数据模型 excel
  • php 重写 重载
  • php读写xml
  • 城建税减半吗
  • vue加载时如何避免出现代码
  • 所得税费用当月计提吗
  • 固定资产计提完折旧报废的账务处理
  • 织梦添加文章如何修改高级参数
  • js数组entries
  • 汽车修理厂利润
  • 长期待摊费用的最新账务处理
  • 自查补税行为有哪些方面
  • 小微企业买商品房怎么买
  • 光盘盒属于什么税收分类
  • 销售旧货的增值税是销项税吗
  • sql乘函数
  • 财务费用账户属于什么账户
  • 用信用卡消费扣谁的手续费
  • 屠宰税属于什么税
  • mysql存储过程菜鸟教程
  • centos 命令
  • windows7更新80072efe
  • ubuntu设置u盘启动项
  • explorer.exe进程被结束怎么恢复
  • hwid.exe
  • win8.1的ie浏览器
  • win7开机提示音在哪里关闭
  • cocos2dx开发鸿蒙app
  • 二分法查找 js
  • jquery添加css样式
  • Node.js中的事件循环是什么
  • python模拟用户登录过程,输入账号及密码
  • linux shell脚本攻略(第3版)
  • 详解Python装饰器由浅入深
  • javascript编写
  • python 字典的字典
  • jquery中加载文档的方法
  • python中lxml模块
  • 内蒙古电子税务局app官方下载
  • 税务局打印发票的软件是哪个
  • 房屋契税和维修基金是开发商交还是业主交
  • 四川国税发票查验真伪
  • 广西区国家税务局
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设