位置: 编程技术 - 正文

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

  • 应交税费借方和贷方各表示什么
  • 契税的纳税人是指在我国境内转移房屋土地权的
  • 收到商业承兑汇票到期,向银行办妥有关入账手续
  • 电子发票冲红是怎么回事
  • 企业所得税纳税人包括哪些类型
  • 拆迁车间
  • 个人开技术服务费税率是多少
  • 在建工程明细科目
  • 生产出来的次品怎么入账
  • 减税的案例
  • 贷款利息增值税发票
  • 零申报有期限吗
  • 出口退税对企业利润的影响
  • 股东入资印花税怎么计算
  • 税收协助
  • 影视制作公司是做什么的
  • 企业增值税去哪交
  • 哪些研发费用可以资本化
  • 公司备用金可以取多少
  • 非关联企业无偿拆借资金企业所得税
  • 平均净资产和净资产
  • 已认证的发票退税怎么退
  • 公司车辆报废收入怎么入账
  • 如何输入特殊符号带圈数字11
  • searchnavversion.exe - searchnavversion是什么进程 作用是什么
  • msp 什么意思
  • PHP:imagecreatefromxpm()的用法_GD库图像处理函数
  • c#openxml
  • phpsetcookie
  • 微信h5页面设计
  • php中session使用
  • web渗透违法吗
  • 国税申报成功未缴税
  • 员工多了怎么管理办法
  • 出口视同内销怎么申报
  • 员工业余自学
  • sqlserver2005安装步骤教程
  • python PyQt5如何实现窗口功能
  • 终止合约取得的合约
  • 短期借款属于经营活动还是筹资活动
  • 月末库存商品成本计算
  • 印花税征税范围及税率
  • 固定资产升级账务处理
  • 工程结转成本分录
  • 往酒吧送酒怎么样
  • 零余额账户用款额度是什么意思
  • 企业转让固定资产发生的费用可扣除吗
  • 公司宿舍楼出租让我当甲方违法吗
  • 代开发票取得的收入如何入账?
  • 主营业务收入核算项目组合表
  • 进项税认证怎么按税率统计明细
  • 预付账款为什么属于资产类
  • 零余额账户对账余额调节表
  • 增值税开票系统怎么改开票人名字信息
  • 利息与资金占用费
  • 免征增值税如何开票
  • 公司账户美金入账后怎么做分录
  • 内部审计人员独立性
  • sql server 执行语句
  • 查看运行中的程序快捷键
  • win7鼠标点了没反应
  • win8系统开机启动项怎么设置
  • airplay macbook
  • 0x81000204错误
  • win10更新后浏览器打不开网页
  • red hat linux安装
  • win8注销在哪里
  • 深入理解bootstrap
  • 基于重大误解实施的民事法律行为
  • 清理ie八
  • cmd 远程登录
  • jquery设置禁用
  • 原生js import
  • Android音乐播放器评论功能怎么实现
  • adb查看ip地址
  • imageview tint
  • Android boot.img制作
  • android缓存清理
  • 登录上海电子税务局显示获取公告失败
  • 公司买车购置税怎么做账
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设