Android应用性能优化最佳实践(txt+pdf+epub+mobi电子书下载)


发布时间:2021-02-24 01:41:57

点击下载

作者:罗彧成

出版社:机械工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Android应用性能优化最佳实践

Android应用性能优化最佳实践试读:

前言

为什么写这本书

一个好的应用,除了要有吸引人的功能和交互之外,在性能上也应该有高的要求,即使应用非常具有特色,或者功能和业务具有唯一性,在产品前期可能吸引了部分用户,但用户体验不好的话,也会给产品带来很差的口碑,如果有在体验上更好的竞品,用户也会很快转移。那么一个好的应用应该如何定义呢?主要有三方面:

·业务/功能

·符合逻辑的交互

·优秀的性能

众所周知,Android系统作为以移动设备为主的一款操作系统,硬件配置有一定的限制,虽然配置现在越来越高级,但仍然无法和PC相比,在CPU和内存上的使用不合理或者耗费资源多时,就会碰到内存不足导致的稳定性问题、CPU消耗太多导致的卡顿问题等。例如,我们发布一款产品后会收到很多的反馈,这些反馈来自很多渠道,有用户反馈,有应用发布平台的反馈通道等。

面对这些问题时,大家想到的都是联系用户,然后看日志,特别是有关性能类问题的反馈,原因也非常难找,日志大多用处不大,为什么呢?因为性能问题大部分是非必现的问题,定位时很难复现,而又没有关键的日志,当然就无法找到原因了。这些问题非常影响用户的体验和功能的使用,所以解决这些问题是非常重要的。当前市场上讲解性能优化的书太少,即使有些书讲到,很多也是一笔带过,没有深入分析和寻找解决方案,所以有必要用一本书来从多个维度讲解在性能上我们面临了什么问题,如何解决这些问题,并在实际的项目中来优化我们的应用,以提高用户体验。

本书面向的读者

本书适合所有Android应用开发从业人员及在校学生,特别是有一定Android应用开发经验的开发人员,高级开发人员也可以通过本书了解更多的性能调优知识。

本书特色

本书为进阶类图书,对于一些基础技术和基础理论知识不会做过多的阐述,特别是入门类的知识点,大家可以从其他书籍获取相关的知识。书中以性能优化为核心,深入剖析性能优化具体涉及的技术背景与优化方案,同时提供典型案例,帮助读者更深入地掌握Android应用开发技术,理解Android的运行机制和原理,掌握Android性能优化的思想,让开发者快速成长,打造高质量的Android应用。

本书的主要内容

可以把用户能体验到的性能问题主要总结为4个类别:

·流畅

·稳定

·省电

·省流量

性能问题的主要原因是什么,原因有相同的,也有不同的,但归根结底,不外乎内存使用、代码效率、合适的策略逻辑、代码质量这一类问题。本书讲解内容的目标和方向如下图所示。

从上图可以看到,打造一个高质量的应用应该以4个方向为目标:快、稳、省、小。

·快:使用时避免出现卡顿,响应速度快,减少用户的等待时间,满足用户预期。

·稳:降低crash率和ANR率,不要在用户使用过程中崩溃和无响应。

·省:节省流量和耗电,减小用户使用成本,避免使用时导致手机发烫。

·小:安装包小可以降低用户的安装成本。

这4类问题需要从根源上解决,也就是要解决图中第二个框里的问题:卡顿、内存使用不合理、代码质量差、代码逻辑不优秀、安装包过大。这些问题也是在开发过程中碰到最多的问题,在实现业务需求的同时,也需要考虑到这些点,多花时间去思考,避免功能完成后再来做优化和修复Bug,这个时候带来的成本会增加。如果是维护之前的代码,就需要使用一系列工具来发现问题点。

性能优化不是更新一两个版本可以解决的,是持续性的需求,结合到实际中,在一个新产品/项目开始时,由于人力和上线时间的限制,可以把优先级放低,但有些点是在写代码时就要考虑的,这就体现出程序员的技术功底。

本书强调性能调优的核心思想和方向如下:

发现问题→分析问题原因及背景→寻找最优解决方案→解决问题。

本书一共7章,在简单介绍了Android Studio的使用指南后,分别从绘制(UI)、内存、存储、稳定性、耗电以及安装包6个方面进行优化,从系统上深入分析绘制和内存的原理,一步步深入了解导致性能问题的本质原因,同时讲述了多种性能优化工具的使用,通过分析典型案例,得到有效的优化方案,从而实现更高质量的应用。书中所讲述的内容均基于Android 6.0系统。

勘误和资源下载

由于写作时间实在有限,在书稿交付时仍有些许不安,为此先为此书可能存在的错误或者描述不清楚的地方致以真诚的歉意,如果你发现此书存在瑕疵或者有任何建议,请发邮件到5482586@qq.com,我会尽快回复,非常期待大家的反馈。

本书代码的下载地址:https://github.com/lyc7898/AndroidTech。

致谢

由于时间的问题,本书写作时间非常长,非常感谢杨福川编辑对我的鼓励和宽容,并且分享了非常有用的碎片化写作方法,使我一直坚持把本书写完。同时感谢李艺编辑的校对和勘误,才完成了这本图文并茂、格式清晰的技术书籍。

感谢我的妻子李萍女士对我的理解和支持,在我几乎将所有的时间投入工作中时一直给予最大的宽容和鼓励,使我每天即使再忙再累时仍然可以回到温馨的家。同时感谢我的父母和岳父母,感谢他们对我无私的帮助,他们都是伟大的父母。

特别感谢我的爷爷罗志华老先生,在我的学习生涯中给予的无私帮助,在工作和生活上的谆谆教诲。还要感谢刘景瑜老师,在求学阶段的鼓励和教诲,告诉我有很多需要去做的事情。

最后感谢我现在工作的公司,在这里我得到了最快的成长,学习到非常多的东西,感谢公司领导及所有同事,在这里工作,能感受到大家每天都在成长。第1章Android Studio使用指南

假设我们要选择一个IDE来开发应用,目前主流的IDE有Eclipse、Android Studio、Idea,在2015年前,这三个IDE各有优缺点,但现在,Android Studio是首选,因为随着Google对Android Studio的大力完善和支持,优势已经越来越明显,但目前仍有不少开发人员在使用Eclipse。为什么要首选Android Studio,它有什么优势,具体要如何使用,本章将逐一揭示。提示 本书的例子都是使用Android Studio开发的。1.1 Android Studio的优势

为什么本书要推荐使用Android Studio呢,Android Studio的核心是一个智能代码编辑器,可进行高级代码完成、重构和调试。这款功能强大的代码编辑器可以帮助你成为更高产的Android应用开发人员。虽然Android发布初期有很多bug,功能也不完善,但随着版本的更新,已经在各方面领先其他IDE,下面列出了Android Studio的几点优势。

·稳定速度快:使用Eclipse的开发人员都会碰到突然假死、卡顿、内存占用高等一系列影响开发效率的老问题,Android Studio在这块性能上得到了明显的提升,并且Android Studio使用了单项目管理模式,在启动速度上明显比Eclipse快。

·功能强大的UI编辑器:集合了Eclipse+ADT的优点,并且能更实时地展示界面布局效果。

·完善的插件管理:Android Studio支持了多种插件,可直接在插件管理中下载所需的插件。

·完善地支持多种代码管理工具:不需要任何操作,直接支持SVN、Git等主流的代码管理工具。

·整合了Gradle构建工具:Gradle继承了Ant的灵活性和Maven的生命周期管理,不使用XML作为配置文件格式,采用了DSL格式,使得脚本更加灵活简洁。

·智能:智能保存,智能补齐,在实际的编辑代码中熟练使用后,可极大提高代码编写效率。

·内置终端:不需要自己打开一个终端来使用ADB等工具。

·谷歌官方支持:是Google官方专门为Android应用开发打造的利器,也是目前Google官方唯一推荐,并且不再支持其他IDE。

Android Studio的更多优势会在开发工作的细节中体现出来,可以参考一些Android Studio的使用书籍和文档,以便了解它的强大之处。1.2 Android Studio使用入门1.2.1 Android Studio安装

这里我们以在Windows系统上安装Android Studio为例,具体的安装步骤如下:

1)安装JDK,且为JDK 1.6及以上版本。

2)下载Android Studio安装包:developer.android.com/sdk/installing/studio.html。

3)单击安装包开始安装,首先进入选择组件界面,如图1-1所示。图1-1 选择安装组件

一般已经安装Eclipse或其他Android开发环境的,只需要安装默认的选项(Android Studio)即可。

4)单击Next,如果已经下载过SDK,并且在前面的组件没有选择安装SDK,会弹出一个设置本地SDK的界面,选择本地SDK目录,如图1-2所示。图1-2 选择SDK目录

5)一直单击Next,直到安装完成,单击Finish,首次启动Android Studio会有一个配置的过程,需要等待一下。

因为本书主要是讲性能优化,所以这里只是简单地介绍下基本的安装。需要了解更多安装细节,可以参考Android开发官网的详细介绍文档:

http://developer.android.com/intl/zh-cn/sdk/installing/index.html?pkg=studio。注意 如果首次启动出现错误导致启动失败,一般来说是因为联网下载一些配置文件失败,可以使用记事本打开studio的安装目录下/bin中的idea.properties文件,在最后一行添加disable.android.first.run=true。1.2.2 创建一个Android Studio工程

又到熟悉的HelloWold环节了,安装完成后,如果没有新建过项目,会出现一个欢迎对话框,选择新建项目。如果已经有项目,则可以通过File→New→New Project来新建一个项目,填写相关名称,包括应用名(Application name)、公司名(Company Domain)、包名(Package name)、项目本地路径(Project Location),如图1-3所示。

下一步选择开发平台,选择Android Phone,SDK使用Android 6.0,如图1-4所示。图1-3 新建项目图1-4 选择应用运行平台

后面两步都使用默认设置,最后单击Finish进入我们的项目。进入后可以看到,左侧的项目文件区域显示的文件树结果和Eclipse并不相同,而且本地目录的文件层级也不相同。因为Android Studio使用了Gradle项目构建工具,而Eclipse使用Ant构建项目。注意 如果不习惯这种结构,可以通过Gradle设置与Eclipse相同的目录结构。

进入后发现不能编译,这是因为还未设置编译工具。需要先配置Gradle,由于Android Studio没有自带Gradle插件,所以会自动下载Gradle,但需要设置代理,并且设置代理下载仍然会比较慢,这里介绍离线配置Gradle的方法,步骤如下:

1)从官网下载Gradle:http://gradle.org/gradle-download/,这里我们选择1.3版本。

2)进入C:\Users\.gradle\wrapper\dists\gradle-2.2-all\1vevkra640w3rb9hkuw50q5we,最后这个文件夹是随机生成的,所以直接进入,把下载好的gradle-1.3-all.zip放到这个文件夹内即可。

3)重新启动Android Studio,就可以正常编译了。注意 Gradle是一种依赖管理工具,基于Groovy语言,抛弃了基于XML的各种繁琐配置,取而代之的是一种基于Groovy的内部领域特定语言(DSL),掌握Gradle脚本的编译和打包是应用开发非常必要的。1.2.3 从Eclipse项目迁移到Android Studio

Eclipse项目和Android Studio相比,在项目结构、构建系统以及IDE的风格上都有了较大的变化。从Eclipse ADT项目迁移到Android Studio有两种方法,一是直接把Eclipse项目导入Android Studio中,二是从Eclipse导出Gradle项目,然后在Android Studio中可以直接打开导出的项目。

Android Studio是基于intelliJ IDEA开发的一款IDE,与Eclipse有很多不同的地方,特别是工程的目录结构,区别非常大。在Eclipse Android中的Project在Android Studio中是一个Module,并且Android Studio是单项目管理,每个窗口只能打开一个Project,这也是Android Studio打开更快的原因之一。在Android Studio中,有多种视图结构类型:Packages、Project Files、Scratches、Problems、Production、Tests,可以根据自己需求来使用不同的视图类型。

Eclipse和Android Studio中项目结构的对应关系如表1-1所示。表1-1 Eclipse与Android Studio项目结构对应的关系

从表1-1可以看出,结构发生了非常大的变化,如果直接按对应关系来迁移项目中的文件对应到Android Studio的目录结果,工作量不小并且容易出错。因此,为了简化迁移过程,Android的Studio提供了一个快速导入Eclipse项目的方法,可以将Eclipse和Ant构建脚本快速转换到Android Studio上。导入步骤如下:

1)在Android Studio菜单栏选择File→New→Import Project。如果是首次使用,在欢迎页可直接选择Import Project。

2)选择需要导入的Eclipse工程目录,AndroidManifest.xml必须是在根目录下。

3)在导入过程中会提示是否重新配置Eclipse中的所有第三方库和项目依赖关系,如果勾选,就会重新生成新的第三方管理库和依赖关系,依赖关系和第三方库也可以在build.gradle文件上修改。如图1-5所示,有三个选项可以选择,前两项目是jar包的引用规则,建议勾选,这样就不用管理这些jar包了。第三项是指是否需要把Module名创建为camelCase风格(首字母小写的命名规则)。图1-5 导出规则提示

4)单击Finish按钮,完成后会生成一个import-summary.txt文件,它是导入过程中产生的日志,这个文件比较重要,如果项目比较大,导入过程不一定顺利,可以通过这个文件来发现导入过程中出现的问题。

这样就把Eclipse Android项目导入Android Studio了,注意前面讲过,导入完成后生成了一个描述导入过程的日志文件:import-summary.txt,文件中包含以下几个内容:

·Ignored Files:描述在导入过程中忽略了哪些文件,这些文件是没有拷贝过来的,如果有需要,就要手动添加过来,一般会忽略Eclipse ADT相关的配置文件和一些自定义的文件/文件夹。

·Replaced Jars with Dependencies和Replaced Libraries with Dependencies:列出那些替换的JAR包,在Android Studio中,如果在仓库中有这个库或者SDK,就会被替换。

·Moved Files:文件移动路径列表。

·Next Steps:接下来需要做些什么,如果没有问题,一般告诉你可以编译了。

·Bugs:如果不能编译,就会列出当前项目存在的问题。

上面介绍的这种导入项目的方式也是Android官网推荐的,理由是简单高效,有问题也容易发现,最主要是不会改变原来的文件组织架构。

更多有关从Eclipse ADT迁移到Android Studio的内容可以从Android开发官网上查询:http://developer.android.com/intl/zh-cn/sdk/installing/migrate.html#prerequisites。注意 这样导入的项目还是会保留Eclipse的构建方式,比如在Eclipse上使用Ant构建,迁移后还是会使用Ant构建,如果先从Eclipse导出成Gradle项目就使用Gradle构建,当然也可以手动改构建方式。1.3 Android Studio实用技巧1.3.1 代码管理

Android Studio支持Git、SVN等主流的源码管理工具,让开发者可以不用离开Android Studio就可以提交和管理代码。我们熟悉的开源社区Github上的项目就是使用Git来管理的,下面是使用Android Studio把本地代码托管到Github上的流程。

1)在本地安装Git,可以从官网下载安装包:https://git-scm.com/downloads。

2)配置File→Setting→Version Control,分别配置Git目录(安装路径下Bin目录的GIt.exe文件)和GitHub账号(没有GitHub账号则需要先申请,开源项目都是免费使用的),如图1-6所示。配置完后可以单击测试按钮,配置没有问题就弹出测试成功提示框。图1-6 配置代码管理工具Git

3)初始化Git项目:选择菜单栏→CVS→Enable Control Integration,在弹出的配置对话框中选择Git,完成后工具栏会新增如图1-7所示的快捷工具。图1-7 VCS快捷工具

4)如果需要过滤掉不需要上传的文件或者目录,可以在File→Setting→VersionControl→Ignored Files中选择不需要同步的文件或文件夹,如图1-8所示,也可以通过修改项目目录下的.gitigonre文件来实现。建议使用前者,因为根据笔者实际的使用经验,修改.gitigonre文件有时会失效,而且管理也没有前者方便。图1-8 过滤不需要同步的文件/文件夹注意 可以通过选择VCS→Git→Compare With Branch,指定Branch(分支)和本地代码做比较。提交前可以双击需要提交的文件来对比改动代码行。

5)同步代码到Github:选择VCS→Import into Version Control→Share Project on GitHub,如图1-9所示。

6)在图1-9中,单击Share按钮,弹出提交文件列表(可以看到前面过滤的文件不在列表内),同步完成后就可以在Github上看到我们同步的项目了。图1-9 同步代码到Github

7)后面需要提交代码,首先提交,然后选择VCS→Git→Push,即可同步代码到GitHub。1.3.2 代码编辑技巧

如果您已经习惯了Eclipse的快捷键,可以通过File→Setting→Keymap来设置成Eclipse的快捷键,如图1-10所示。图1-10 设置成Eclipse快捷键

Android Studio是一款非常智能的编辑器,在编写代码过程中,如果能熟练掌握一些小技巧,就可以提高写代码的速度,这里介绍一些常用的技巧。

·内容补全:在写完一个方法名,或者一个判断语句(if、while、for)后,自动生成{}块并使光标停在{}中间,在写完方法名或需要用{}前,使用Ctrl+Shift+Enter组合键。

·列选择:可以选择列块,按住Alt键选择代码块。

·代码补全:使用键从光标处插入补全的代码,对原来的代码不做任何操作。而使用键时从光标处插入补全的代码,但是会删除后面的代码,直到遇到点号、圆括号、分号或空格为止。

·查看方法调用路径:按Ctrl+Alt+H组合键。

·布局文件编辑器:Android Studio提供了更加高级的布局文件编辑器,除了像Eclipse一样可以实时查看布局文件外,Android Studio的布局查看包括了很多种分辨率和手机模拟器,并且还可以自定义设备分辨率。

·预览某个方法或者类的实现:在不离开当前文件或者当前类的情况下,快速预览某个方法或者类的实现,减少频繁切换文件/类,使用Ctrl+Shift+I组合键。

·快速使用命令:对于没有设置快捷键或者忘记快捷键的菜单功能或者命令,可以通过输入其名字或者模糊匹配以快速调用。使用Ctrl+Shift+A组合键,比如输入‘commi’,会列出所有和commit相关的操作。

·自动导包:进入settings→Editor→General→Auto Import设置页,选中Show import popup、Show import pupup、Optimize imports on the fly和Add unambiguous imports on the fly,就可以自动导包,并且会自动去掉无用的包。提示 Android Studio快捷键的更多使用技巧,可以通过Help-->Tip of the day,每日学习一个,在开发过程中逐渐掌握。1.3.3 调试技巧

Android Studio另一个强大的地方,就是调试更加方便和高效,以下是调试过程中的常用场景。

·调试程序:Android Studio可以随时进入调试模式,即使未以调试方式启动应用。我们可以通过菜单→Build→Attach to Android Process,也可以直接通过工具栏快捷工具Attach to Android Process进入调试模式。

·条件断点:调试中非常有用的方式,可以自己直接赋值,减小调试时间,通过右键断点(也可以在Debug视图上的BreakPoint列表上)对一个断点加入条件,即填写Condition中的条件。只有当满足条件时,才会进入断点中。

·分析传入/传出数据流:Menu→Analyze→Analyze Data Flow to Here这个操作将会根据当前选中的变量、参数或者字段,分析出其传递到此处的路径。如果你想知道某个参数是怎么传递到一段陌生的代码时,这是一个非常有用的操作。传出数据流(Analyze data flow from here)则会分析当前选中的变量往下传递的路径,直到结束。

·日志断点(Logging Breakpoints):一种不是暂停的断点,只是打印日志,当想打印一些日志信息,但是不想添加log代码后重新部署时,则可以在断点上单击鼠标右键,取消选中Suspend,然后勾选Log evaluated Expression,并在输入框中输入要打印的日志信息。

·修改变量值:在调试过程中,修改变量值可以快速调试各个Case,提高异常处理的调试效率。在调试时进入断点后,如图1-11所示,如果需要修改level变量的值,右键单击level,在弹出的菜单中选择Set Value或者使用快捷键F2,就可以修改成需要的值,并且该值只对当次有效。图1-11 调试时修改变量值1.4 本章小结

Android Studio的安装并不复杂,首次使用可能会不适应并觉得效率甚至低于以前使用的其他IDE,但相信使用一段时间后,一定能体验到它的灵活和强大之处,同时Android Studio的技巧非常多,大家可以多从网上找到各种技巧。

古人云,“工欲善其事,必先利其器”,Android应用开发者的“器”则是指Android Studio,熟练运用开发工具,能极大程度提高开发效率。在掌握Android Studio这个开发神器后,再通过接下来的章节的学习,从UI性能、内存、稳定性等多个维度的优化,使Android应用程序更加高效、流畅地运行,从而打造出一款高质量的Android应用。第2章绘制优化

Android应用启动慢,使用时经常卡顿,是非常影响用户体验的,应该尽量避免出现。卡顿的场景有很多,按场景可以分成4类:UI绘制、应用启动、页面跳转、事件响应,如图2-1所示。在这四种场景下又有多个小分类,基本上覆盖了卡顿的各个场景。图2-1 卡顿主要场景

这4种卡顿场景的根本原因又可以分成两大类。

界面绘制:主要原因是绘制的层级深、页面复杂、刷新不合理,由于这些原因导致卡顿的场景更多出现在UI和启动后的初始界面以及跳转到页面的绘制上。

数据处理:导致这种卡顿场景的原因是数据处理量太大,一般分为三种情况,一是数据处理在UI线程(这种应该避免),二是数据处理占用CPU高,导致主线程拿不到时间片,三是内存增加导致GC频繁,从而引起卡顿。

本章主要通过优化UI界面编程来减少卡顿,以场景为纬度,通过工具深入分析症结所在,找到导致问题的根本原因,利用涉及的相关技术背景,以及了解当前业内主流解决方案,然后结合实例来找到最终的优化方案,使应用流畅。

引起卡顿的原因有很多,但不管怎么样的原因和场景,最终都是通过设备屏幕上的显示来到达用户,归根到底就是显示有问题,所以,要解决卡顿,就要先了解Android系统的显示原理。2.1 Android系统显示原理

说到显示原理,相信大家从网上或其他书籍上看过相关的知识,但大部分人看得云里雾里,是因为整个显示系统很复杂吗?确实很复杂,但我们只需要了解整体流程,抓住关键知识,从应用角度上来讲,需要掌握的不多,如果自己有兴趣,可以阅读专门介绍系统框架的书籍,结合源码来分析,这里就不过多地介绍系统层的知识了。下面我们首先介绍在应用开发上需要涉及的知识点和整体流程。

Android的显示过程可以简单概括为:Android应用程序把经过测量、布局、绘制后的surface缓存数据,通过SurfaceFlinger把数据渲染到显示屏幕上,通过Android的刷新机制来刷新数据。也就是说应用层负责绘制,系统层负责渲染,通过进程间通信把应用层需要绘制的数据传递到系统层服务,系统层服务通过刷新机制把数据更新到屏幕。

通过阅读Android系统的源码可以了解显示的流程,Android的图形显示系统采用的是Client/Server架构。SurfaceFlinger(Server)由C++代码编写。Client端代码分为两部分,一部分是由Java提供给应用使用的API,另一部分则是由C++写成的底层具体实现。下面通过介绍绘制原理和刷新机制来学习整个显示过程。2.1.1 绘制原理

绘制任务是由应用发起的,最终通过系统层绘制到硬件屏幕上,也就是说,应用进程绘制好后,通过跨进程通信机制把需要显示的数据传到系统层,由系统层中的SurfaceFlinger服务绘制到屏幕上。那么应用层和系统层中的流程是什么样的呢?接下来将进行具体介绍。

1.应用层

先来看一个UI界面的典型构成框架,也可以是一个Activity的构成。如图2-2所示,有很多不同层次的基本元素——View,整体是一个树型结构,有不同的嵌套,存在着父子关系,子View在父View中,这些View都经过一个相同的流程最终显示到屏幕上,这也意味着要完整地显示所有数据,就要对其中的View都进行一次绘制工作,并且针对每个View的操作都是一个递归过程。

在Android的每个View绘制中有三个核心步骤(见图2-3),通过Measure和Layout来确定当前需要绘制的View所在的大小和位置,通过绘制(Draw)到surface,在Android系统中整体的绘图源码是在ViewRootImp类的performTraversals()方法,通过这个方法可以看出Measure和Layout都是递归来获取View的大小和位置,并且以深度作为优先级。可以看出,层级越深,元素越多,耗时也就越长。图2-2 页面构成框架图2-3 View绘制流程(1)Measure

用深度优先原则递归得到所有视图(View)的宽、高;获取当前View的正确宽度childWidthMeasureSpec和高度childHeightMeasureSpec之后,可以调用它的成员函数Measure来设置它的大小。如果当前正在测量的子视图child是一个视图容器,那么它又会重复执行操作,直到它的所有子孙视图的大小都测量完成为止。(2)Layout

用深度优先原则递归得到所有视图(View)的位置;当一个子View在应用程序窗口左上角的位置确定之后,再结合它在前面测量过程中确定的宽度和高度,就可以完全确定它在应用程序窗口中的布局。(3)Draw

目前Android支持了两种绘制方式:软件绘制(CPU)和硬件加速(GPU),其中硬件加速在Android 3.0开始已经全面支持,很明显,硬件加速在UI的显示和绘制的效率远远高于CPU绘制,但硬件加速并非如大家所想的那么完善,它也存在明显的缺点:

·耗电:GPU的功耗比CPU高。

·兼容问题:某些接口和函数不支持硬件加速。

·内存大:使用OpenGL的接口至少需要8MB内存。

所以是否使用硬件加速,需要考虑一些接口是否支持硬件加速,同时结合产品的形态和平台,比如TV版本就不需要考虑功耗的问题,而且TV屏幕大,使用硬件加速容易实现更好的显示效果。

2.系统层

真正把需要显示的数据渲染到屏幕上,是通过系统级进程中的SurfaceFlinger服务来实现的,SurfaceFlinger的具体实现和工作原理因为和应用层关系不大,所以这里不做过多介绍,只需要了解它主要是做些什么工作。

·响应客户端事件,创建Layer与客户端的Surface建立连接。

·接收客户端数据及属性,修改Layer属性,如尺寸、颜色、透明度等。

·将创建的Layer内容刷新到屏幕上。

·维持Layer的序列,并对Layer最终输出做出裁剪计算。

既然是两个不同进程,那么肯定需要一个跨进程的通信机制来实现数据传输,在Android的显示系统,使用了Android的匿名共享内存:SharedClient,每一个应用和SurfaceFlinger之间都会创建一个SharedClient,如图2-4所示。从图2-4中可以看出,在每个SharedClient中,最多可以创建31个SharedBufferStack,每个Surface都对应一个SharedBufferStack,也就是一个window。

一个SharedClient对应一个Android应用程序,而一个Android应用程序可能包含多个窗口,即Surface。也就是说SharedClient包含的是SharedBufferStack的集合。因为最多可以创建31个SharedBufferStack,这也意味着一个Android应用程序最多可以包含31个窗口,同时每个SharedBufferStack中又包含了两个(低于4.1版本)或者三个(4.1及以上版本)缓冲区,即在后面的显示刷新机制中会提到的双缓冲和三重缓冲技术。图2-4 Android显示框架

最后总结起来显示整体流程分为三个模块:应用层绘制到缓存区,SurfaceFlinger把缓存区数据渲染到屏幕,由于是两个不同的进程,所以使用Android的匿名共享内存SharedClient缓存需要显示的数据来达到目的。

SurfaceFlinger把缓存区数据渲染到屏幕(流程如图2-5所示),主要是驱动层的事情,这里不做太多解释。

从图2-5中可以看出,绘制过程首先是CPU准备数据,通过Driver层把数据交给CPU渲染,其中CPU主要负责Measure、Layout、Record、Execute的数据计算工作,GPU负责Rasterization(栅格化)、渲染。由于图形API不允许CPU直接与GPU通信,而是通过中间的一个图形驱动层(Graphics Driver)来连接这两部分。图形驱动维护了一个队列,CPU把display list添加到队列中,GPU从这个队列取出数据进行绘制,最终才在显示屏上显示出来。图2-5 渲染数据流程图

知道了绘制的原理后,那么到底绘制一个单元多长时间才是合理的,首先需要了解一个名词:FPS。FPS(Frames Per Second)表示每秒传递的帧数。在理想情况下,60 FPS就感觉不到卡,这意味着每个绘制时长应该在16ms以内,如图2-6所示。图2-6 理想状态下的绘制操作

但是Android系统很有可能无法及时完成那些复杂的界面渲染操作。Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需的60FPS。即为了实现60FPS,就意味着程序的大多数绘制操作都必须在16ms内完成。

如果某个操作花费的时间是24ms,系统在得到VSYNC信号时就无法进行正常渲染,这样就发生了丢帧现象。那么用户在32ms内看到的会是同一帧画面。主要场景在执行动画或者滑动ListView时更容易感知到卡顿不流畅,是因为这里的操作相对复杂,容易发生丢帧的现象,从而感觉卡顿。有很多原因可以导致CPU或者GPU负载过重从而出现丢帧现象:可能是你的Layout太过复杂,无法在16ms内完成渲染;可能是UI上有层叠太多的绘制单元;还有可能是动画执行的次数过多。

最终的数据是刷新机制通过系统去刷新数据,刷新不及时也是引起卡顿的一个主要原因。接下来将详细介绍系统是怎么刷新的以及在什么情况下会导致卡顿发生。2.1.2 刷新机制

Google发布Android操作系统后,Android OS系统一直在不断优化、更新。但直到Android 4.0版本发布,有关UI显示不流畅的问题仍未得到根本解决。在整个Android版本升级过程中,Android在显示系统方面做了不少优化和改进,比如支持硬件加速等技术,但本质原因似乎和硬件关系并不大,也没有得到太多改善。而与高端硬件配置的Android机器价格相近的iPhone,其UI的流畅性强却是有目共睹的。

从Android 4.1(Jelly Bean)开始,Android OS开发团队便力图在每个版本中解决一个重要问题。作为严重影响Android口碑问题之一的UI流畅性差的问题,首先在Android 4.1版本中得到了有效处理。其解决方法即在4.1版本推出的Project Butter。Project Butter对Android Display系统进行了重构,引入三个核心元素:VSYNC、Triple Buffer和Choreographer。其中,VSYNC是理解Project Buffer的核心。VSYNC是Vertical Synchronization(垂直同步)的缩写,是一种在PC上已经很早就广泛使用的技术,读者可简单地把它认为是一种定时中断。Choreographer起调度的作用,将绘制工作统一到VSYNC的某个时间点上,使应用的绘制工作有序。接下来,本文将围绕VSYNC来介绍Android Display系统的工作方式。

在讲解刷新机制之前,先介绍几个名词以及VSYNC和Choreographer主要功能及工作方式。

·双缓冲:显示内容的数据内存,为什么要用双缓冲,我们知道在Linux上通常使用Framebuffer来做显示输出,当用户进程更新Framebuffer中的数据后,显示驱动会把Framebuffer中每个像素点的值更新到屏幕,但这样会带来一个问题,如果上一帧的数据还没有显示完,Framebuffer中的数据又更新了,就会带来残影的问题,给用户直观的感觉就会有闪烁感,所以普遍采用了双缓冲技术。双缓冲意味着要使用两个缓冲区(在SharedBufferStack中),其中一个称为Front Buffer,另外一个称为Back Buffer。UI总是先在Back Buffer中绘制,然后再和Front Buffer交换,渲染到显示设备中。即只有当另一个buffer的数据准备好后,通过io_ctrl来通知显示设备切换Buffer。

·VSYNC:从前面的双缓冲介绍中可以了解到,只有当另一个buffer准备好后,才能通知刷新,这就需要CPU以主动查询的方式来保证数据是否准备好,因为这种机制效率很低,所以引入了VSYNC。VSYNC是Vertical Synchronization(垂直同步)的缩写,可以简单地把它认为是一种定时中断,一旦收到VSYNC中断,CPU就开始处理各帧数据。

·Choreographer:收到VSYNC信号时,调用用户设置的回调函数。一共有以下三种类型的回调:

·CALLBACK_INPUT:优先级最高,与输入事件有关。

·CALLBACK_ANIMATION:第二优先级,与动画有关。

·CALLBACK_TRAVERSAL:最低优先级,与UI控件绘制有关。

接下来通过时序图来分析刷新的过程,这些时序图是Google在2012 Google I/O讲解新的显示系统提供的,图2-7所示的时序图有三个元素:Display(显示设备),CPU-CPU准备数据,GPU-GPU准备数据。最下面的时间为显示时间,根据理想的60FPS,以16ms为一个显示周期。图2-7 没有VSync信息的刷新(1)没有VSync信号同步

我们以16ms为单位来进行分析:

1)从第一个16ms开始看,Display显示第0帧,CPU处理完第一帧后,GPU紧接其后处理继续第一帧。三者都在正常工作。

2)时间进入第二个16ms:因为在上一个16ms时间内,第1帧已经由CPU、GPU处理完毕。所以Display可以正常显示第1帧。显示没有问题,但在本16ms期间,CPU和GPU并未及时绘制第2帧数据(前面的空白区在忙别事情去了),而是在本周期快结束时,CPU/GPU才去处理第2帧数据。

3)时间进入第3个16ms,此时Display应该显示第2帧数据,但由于CPU和GPU还没有处理完第2帧数据,故Display只能继续显示第一帧的数据,结果使得第1帧多画了一次(对应时间段上标注了一个Jank),这就导致错过了显示第二帧。

通过上述分析可知,在第二个16ms时,发生Jank的关键问题在于,为何在第1个16ms段内,CPU/GPU没有及时处理第2帧数据?从第二个16ms开始有一段空白的时间,可以说明原因所在,那就是CPU可能是在忙别的事情,不知道该到处理UI绘制的时间了。可CPU一旦想起来要去处理第2帧数据,时间又错过了。为解决这个问题,4.1版本推出了Project Butter,核心目的就是解决刷新不同步的问题。(2)有VSync信号同步

加入VSync后,从图2-8可以看到,一旦收到VSync中断,CPU就开始处理各帧的数据。大部分的Android显示设备刷新率是60Hz(图2-7的时间轴也是60ms),这也就意味着每一帧最多只能有1/60=16ms左右的准备时间。假如CPU/GPU的FPS高于这个值,显示效果将更好。但是,这时又出现了一个新问题:CPU和GPU处理数据的速度都能在16ms内完成,而且还有时间空余,但必须等到VSYNC信号到来后,才处理下一帧数据,因此CPU/GPU的FPS被拉低到与Display的FPS相同。

从图2-9采用双缓冲区的显示效果来看:在双缓冲下,CPU/GPU FPS大于刷新频率同时采用了双缓冲技术以及VSync,可以看到整个过程还是相当不错的,虽然CPU/GPU处理所用的时间时短时长,但总体来说都在16ms以内,因而不影响显示效果。A和B分别代表两个缓冲区,它们不断交换来正确显示画面。但如果CPU/GPU的FPS小于Display的FPS,情况又不同了,如图2-10所示。图2-8 有VSync的绘制图2-9 双缓冲下的时序图图2-10 双缓冲下CPU/GPU FPS小于刷新频率时序图

从图2-10可以看到,当CPU/GPU的处理时间超过16ms时,第一个VSync就已经到来,但缓冲区B中的数据却还没有准备好,这样就只能继续显示之前A缓冲区中的内容。而后面B完成后,又因为还没

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载