位置: 编程技术 - 正文

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
  • 物业管理行业税点
  • 企业所得税汇算清缴账务处理
  • 企业所得税营业利润怎么算
  • 无形资产属于什么行业
  • 一般纳税人企业要交哪些税
  • 本月没有发生额本月合计
  • 金税盘开票出现离线发票金额超出限制怎么设置?
  • 办税员不能领用发票是怎么回事
  • 公司低价处理旧汽车涉税账务处理
  • 付佣金怎么做分录
  • mac系统自带软件有哪些
  • 公司购入二手设备 如何开具发票
  • ElementPlusError:[ElPagination] 你使用了一些已被废弃的用法,请参考 el-pagination 的官方文档
  • 更新网页证书
  • 折旧计算表属于外来原始凭证吗
  • 用科目汇总表怎么登记总账
  • 车间闲置固定资产怎么算
  • php最好的教程
  • 小白学php需要多久能上手
  • php time
  • 科罗拉多州位置
  • yii2项目实战之restful api授权验证详解
  • 分红派息钱去哪里了
  • vue控制按钮是否禁用
  • php获取url内容
  • php include和include_once
  • php读取数据库复选框数据
  • 特定业务计算的应纳税所得额是指
  • mysql和php连接
  • [paper] lift,splat,shooting 论文浅析
  • 命令行延时
  • 三代手续费返还是否需要缴纳增值税
  • 补交上年所得税怎么调表
  • 个体户个税征收标准
  • 员工工资为零只缴纳社保,个税怎么处理
  • 预付款能不能开票入账
  • 深入了解工作优势怎么回答
  • 织梦模板官网
  • 视同销售是指什么
  • 股权转让需要开票吗怎么开
  • 其他应付款不用付了会计分录
  • 股东的报销款可以抵投资款吗
  • 私车公用必须是公司员工的车吗?
  • 收到员工社保
  • 老板怎么从公户拿钱
  • 坏账准备转回并收到货款会计分录
  • 劳动仲裁要出面吗
  • 对公账户明细入账怎么查
  • 出口单证不齐责令整改可以处
  • jdbc连接MySQL实验总结
  • win7 组织
  • centos7搭建nfs详细步骤
  • 电脑avg是什么软件
  • centos7批量创建用户
  • linux系统内核的功能
  • win10关机快捷键
  • w10怎么创建此电脑
  • win7系统安装软件乱码
  • nodejs模拟浏览器环境
  • 趣谈 linux 操作系统
  • 比较漂亮的一个字
  • ubuntu sudo apt-get install
  • 深入理解中国式现代化
  • 防止重复调用接口
  • 代码实现死锁
  • 辽宁地方税务局网上申报
  • 江苏电子税务局网站官网
  • 重庆国家税务局发票流向查询
  • 公司租房的房产税应计入哪个科目
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设