位置: 编程技术 - 正文

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实现

  • 小微企业所得税税率2023年最新税率表
  • 一般纳税人开劳务发票几个点
  • 视同销售的增值税怎么申报
  • 耕地占用税减半政策
  • 资产减值损失纳税调增还是调减
  • 开办费的帐务处理
  • 2019小微企业10万免税需要如何办理
  • 建筑公司核定征收是什么意思
  • 汇总记账凭证核算组织程序的缺点主要有
  • 三证合一不需要税务登记情况说明
  • 预收房款什么意思
  • 月工资3000元,工伤7级能赔偿多少钱
  • 详解非税收入
  • 全国失信人员信息
  • 技术转让开具什么发票
  • 房地产开发企业预收款预缴增值税
  • 企业为员工购买团体医疗险可以全额税前扣除
  • 有进项税额转出怎么结转增值税
  • 收到上月发票怎么写分录
  • 固定资产改造更新是否需要计提折旧
  • 幼儿园开发成本
  • win 10 请稍后
  • 增值税专用发票有几联?
  • 清除文件夹exe病毒工具
  • mac如何修改照片的尺寸大小
  • php随机生成1到3的数字
  • php preg_quote
  • windows 7怎么打开虚拟化
  • paddle!
  • Win10 20H2/2004 Beta Build 19042.789累积更新补丁KB4598291正式推送
  • 单位公车是什么意思
  • 格拉斯伯格
  • php stristr函数
  • 向农户收购农产品怎么打款
  • 小米随身wf
  • 房屋权属变更什么意思
  • 装饰工程开票项目
  • 办公经费包括工资吗
  • thinkphp框架的作用
  • vue–router
  • tailf命令 查看100行
  • 差旅费津贴与差旅费补助
  • 电子发票如何发邮箱给别人
  • 什么是支付宝账号如何查看
  • 速动资产扣除应纳税所得
  • 以前年度损益调整结转到本年利润吗
  • 收到以前年度银行手续费发票分录
  • 当月的银行日记账漏计怎么办
  • 无票收入如何做凭证
  • 购买金税盘未抵税怎么办
  • 银行的手续费开票怎么开
  • 转账支票一定要填写支付密码吗?
  • 零星采购多少不需要发票
  • 采购自产自销的商品
  • sql server 判断数据是否存在
  • sql server数据库文件的扩展名是
  • windowssystem32oobe找不到指定的路径
  • ubuntu怎么切换桌面
  • 如何通过mac找到具体地址
  • win10系统怎么一键返回桌面
  • wind移动版
  • 使用JQuery FancyBox插件实现图片展示特效
  • nodejs爬虫框架crawler
  • android实战项目
  • 拖曳对象时用的命令
  • bat vba
  • unity shaderlab新手宝典
  • nodejs的http
  • github上有什么
  • 关于android工程中的assets目录的正确说法
  • node.js中模块的文件格式有哪些
  • javascript核心技术
  • node运行js文件
  • javascript如何学
  • js设计模式有什么用
  • 主营业务收入占比高说明什么
  • 云南税务总局官网
  • 德阳契税2021年新规
  • 广西税务移动办税平台app
  • 2020年安徽省护理技能大赛视频播放
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设