安卓性能优化 |
您所在的位置:网站首页 › gpu渲染模式分析 › 安卓性能优化 |
小伙伴们应该都知道,UI绘制优化是性能优化中非常重要的一部分,因为用户在使用应用过程的中,优秀的交互体验是我们留住用户不可或缺的一部分。如果说设计美观舒适的交互是产品经理与UI设计师的工作,那么如何将交互实现则是攻城狮们要做的咯,而UI绘制优化的原因则是为了给用户带去丝滑般的使用体验而进行的。 一、绘制原理在Android中,每个View都有三个固定的核心步骤,并依次去执行。它们分别是measure、layout以及draw,通过Measure和Layout来确定当前需要绘制的View所在的大小和位置,再通过绘制(Draw)到surface。measure、layout和draw方法主要是运行在系统的应用框架层,而真正将数据渲染到屏幕上的则是系统Nativie层的SurfaceFlinger服务来完成的。 1.1 应用层 Measure用深度优先原则递归得到所有视图(View)的宽、高;获取当前View的正确宽度childWidthMeasureSpec和高度childHeightMeasureSpec之后,可以调用它的成员函数Measure来设置它的大小。如果当前正在测量的子视图child是一个视图容器,那么它又会重复执行操作,直到它的所有子孙视图的大小都测量完成为止。 Layout位置计算的递归原理跟测量的差不多,用深度优先原则递归得到所有视图(View)的位置;当一个子View在应用程序窗口左上角的位置确定之后,再结合它在前面测量过程中确定的宽度和高度,就可以完全确定它在应用程序窗口中的布局。 Draw目前Android支持了两种绘制方式:软件绘制(CPU)和硬件加速(GPU),其中硬件加速在Android 3.0开始已经全面支持,很明显,硬件加速在UI的显示和绘制的效率远远高于CPU绘制,但硬件加速并非如大家所想的那么完善,它也存在明显的缺点: 耗电:GPU的功耗比CPU高。 兼容问题:某些接口和函数不支持硬件加速。 内存大:使用OpenGL的接口至少需要8MB内存。 所以是否使用硬件加速,需要考虑一些接口是否支持硬件加速,同时结合产品的形态和平台,比如TV版本就不需要考虑功耗的问题,而且TV屏幕大,使用硬件加速容易实现更好的显示效果。而绘制也并不是直接绘制到屏幕上,而是由应用层先绘制到我们的缓冲区,然后再由系统层的SurfaceFlinger服务将数据渲染到屏幕上。 1.2 系统层真正把需要显示的数据渲染到屏幕上,是通过系统级进程中的SurfaceFlinger服务来实现的,SurfaceFlinger的主要工作内容如下: 响应客户端事件,创建Layer与客户端的Surface建立连接。 接收客户端数据及属性,修改Layer属性,如尺寸、颜色、透明度等。 将创建的Layer内容刷新到屏幕上。 维持Layer的序列,并对Layer最终输出做出裁剪计算。 SharedClientSharedClient是Android中的匿名共享内存,Android的显示系统使用该匿名共享内存机制实现应用与SurfaceFlinger间的跨进程的数据传输。每一个应用和SurfaceFlinger之间都会创建一个SharedClient,每个SharedClient对象中,最多可以创建31个SharedBufferStack,每个Surface对应一个SharedBufferStack,也就是一个Window。这也意味着一个应用程序最多只能创建31个窗口,同时SharedBufferStack又包含有两个缓冲区(低于4.1版本)或者三个缓存区(4.1及以上)。 Android显示框架
首先我们来看下安卓系统的渲染数据流程图 FPS(Frames Per Second)它是指画面每秒传输帧数,通俗来讲就是指动画或视频的画面数,最简单的举例就是我们玩游戏时,如果画面在60fps则不会感觉到卡顿,如果低于60fps,比如50fps则会感觉到卡顿,这个时候我们可能需要针对性的做一些优化。要想画面保持在60fps,则需要每个绘制时长在16ms以内,如下图所示。 在Android 4.1开始,Project Butter被推出,其主要的目的就是解决Android UI流畅性差的问题,Project Butter对Android Display系统进行了重构,引入三个核心元素:VSYNC、Triple Buffer和Choreographer。 2.2.1 VSYNC 信号VSYNC是Vertical Synchronization(垂直同步)的缩写,可以简单地把它认为是一种定时中断,一旦收到VSYNC中断,CPU就开始处理各帧数据。下面我们可以简单的看下未加入VSYNC信号及加入VSYNC信号后的区别 未加入VSYNC前
此时,系统每16ms会发送一次VSYNC信号,每收到 VSYNC 中断,CPU 会立即准备 Buffer 数据,由于大部分显示设备刷新频率都是 60Hz(一秒刷新 60 次),也就是说一帧数据的准备工作都要在 16ms 内完成。这样应用总是在 VSYNC 边界上开始绘制,而 SurfaceFlinger 总是 VSYNC 边界上进行合成。这样可以消除卡顿,并提升图形的视觉表现。 2.2.2 Triple Buffer三缓冲机制 双缓冲在了解三缓冲前,我们先了解下双缓冲。一般来说,不同的 View 或者 Activity 它们都会共用一个 Window,也就是共用同一个 Surface。而每个 Surface 都会有一个 BufferQueue 缓存队列,但是这个队列会由 SurfaceFlinger 管理,通过匿名共享内存机制(SharedClient)与 App 应用层交互。
首先我们来看下Choreographer的定义与结构,如下图: 优先级高低和处理顺序有关。当收到VSYNC中断时,Choreographer将首先处理INPUT类型的回调,然后是ANIMATION类型,最后才是TRAVERSAL类型。更多Choreographer相关的,可以参考链接(Android Project Butter) 2.3 Android 5.0:RenderThread虽然Project Butter的加入大大优化了Android系统的渲染效率,但是当渲染的任务比较耗时的时候,UI主线程依旧会因为任务繁重,进而出现卡顿的情况。而为了减轻UI主线程的任务,在5.0版本,引入了RenderThread技术,在进一步利用GPU对图形的绘制渲染能力的同时,将GPU绘制的任务从UI主线程中放到不同的线程去执行,从而减轻UI主线程的绘制任务,使得与用户的交互更加流畅。
我们知道,UI问题的最大体现则是屏幕卡顿现象,由前面的绘制原理与刷新机制我们可以简单得出,造成卡顿的主要原因有下面两点: 绘制任务太重,绘制一帧内容耗时太长。 主线程太忙了,导致VSync信号来时还没有准备好数据导致丢帧。 3.1.1 绘制耗时过长UI布局造成的耗时过长主要有以下几点: 界面布局层级过深:一个页面的Measure、Layout和draw过程都是通过递归来完成的,多叉树遍历的时间与树的高度h相关,其时间复杂度为O(h),如果层级太深,每增加一层则会增加更多的页面显示时间。 复杂的布局:对于一些存在View状态变化等情况的界面,当View发生变化时,都需要进行重新计算、创建DisplayList、渲染DisplayList,更新到屏幕上等一系列操作,如果布局很复杂,就很容易导致严重的性能问题。 过渡绘制:可能的原因有重叠的UI设置了多个background,亦或者自定义View中draw方法在同一区域进行了重复的绘制。 3.1.2 主线程繁忙主线程主要负责的工作内容有以下几点: UI生命周期控制 系统事件处理 消息处理 界面布局 界面绘制 界面刷新 而如果我们在主线程中加入了一些复杂的数据处理,网络请求等,那么可能会导致主线程被阻塞而不能及时响应VSync信号,出现卡顿丢帧的情况。 3.2 性能分析工具工欲善其事必先利其器,了解了绘制的原理,知道了造成UI问题的原因,那么我们还需要进一步的去把有问题的点找出来并加以优化。Android常用的绘制优化的定位工具一般有如下几种: Hierarchy View:查看Layout层次 Layout Inspector Android Studio自带的Profile CPU工具 静态代码检查工具Lint GPU 渲染速度和过度绘制 TraceView Systrace btrace 3.2.1 Hierarchy View笔者的开发环境是Mac版的,那么则主要以Mac下的版本为例去讲述,windows的基本相似。 Hierarchy View是Android SDK自带的一个布局可视化工具,但因为在Android studio3.2版本Android device monitor就被弃用了,所以我们可以通过在SDK的目录下去启动,路径是tools-->monitor,启动过程中可能由于一些配置问题,导致无法启动或者无法打开调试界面,链接是笔者遇到的一些问题,并得到了解决,如遇到其他问题可自行度娘等,或留言一起谈论。 Hierarchy View基本使用
查看层级与耗时的功能可以说是Hierarchy View最实用的一个功能,能够帮助我们大致的分析出当前界面的每个View的一个大致耗时情况,下面我们看下下图的分析情况: 点击选中需要分析的View,点击1处,"Obtain layout times for tree rooted at selected node"; 可以查看到当前的View下共有22个子View,同时我们可以看到实际的Measure、Layout以及Draw的实际耗时 可以看到当前View层级下每个子View的耗时性能情况,红色表示跟当前View下的其他View相比处理速度较慢、黄色次之,绿色的表示比当前View层级下50%以上的控件速度快。 3.2.2 Layout InspectorLayout Inspector是google提供给我们进行布局分析的一个工具,也是目前google在弃用Hierarchy View后推荐使用的一款布局分析工具,可以在Android studio的Tools-Layout Inspector中启用,具体的使用教程以及分析过程小伙伴们可以参考google提供的文档:Layout Inspector Android Studio 在3.0 及更高版本中的 Android Profiler 取代了 Android Monitor 工具。Android Profiler 工具可提供实时数据,帮助我们了解应用的 CPU、内存、网络和电池资源使用情况。Profile CPU分析工具是Android Profile性能分析器中的其中一个,我们可以使用 CPU 性能分析器分析 CPU 活动和跟踪记录,打开 CPU 性能剖析器,可按以下步骤操作: 依次选择 View > Tool Windows > Profiler 或点击工具栏中的 Profile 图标如上图所示,CPU 性能剖析器的默认视图包括以下时间轴: 事件时间轴:显示应用中的 Activity 在其生命周期内不断转换经历各种不同状态的过程,并指示用户与设备的交互,包括屏幕旋转事件。如需了解如何在搭载 Android 7.1(API 级别 25)及更低版本的设备上启用事件时间轴,请参阅启用高级性能剖析。 CPU 时间轴:显示应用的实时 CPU 使用率(以占总可用 CPU 时间的百分比表示)以及应用当前使用的线程总数。此时间轴还会显示其他进程(如系统进程或其他应用)的 CPU 使用率,以便可以将其与应用的 CPU 使用率进行对比。可以通过沿时间轴的横轴方向移动鼠标来检查历史 CPU 使用率数据。 线程活动时间轴:列出属于应用进程的每个线程,并使用下面列出的颜色在时间轴上指示它们的活动。记录跟踪数据后,可以从此时间轴上选择一个线程,以在跟踪数据窗格中检查其数据。 绿色:表示线程处于活动状态或准备使用 CPU。也就是说,线程处于正在运行或可运行状态。 黄色:表示线程处于活动状态,但它正在等待一项 I/O 操作(如磁盘或网络 I/O),然后才能完成它的工作。 灰色:表示线程正在休眠且没有消耗任何 CPU 时间。 当线程需要访问尚不可用的资源时,就会出现这种情况。在这种情况下,要么线程主动进入休眠状态,要么内核将线程置于休眠状态,直到所需的资源可用。CPU 性能剖析器还会报告 Android Studio 和 Android 平台添加到应用进程的线程的 CPU 使用率,这些线程包括 JDWP、Profile Saver、Studio:VMStats、Studio:Perfa 和 Studio:Heartbeat 等(不过,它们在线程活动时间轴上显示的确切名称可能有所不同)。Android Studio 报告此数据是为了方便确定线程活动和 CPU 使用率什么时候是由应用的代码实际引发的。 CPU性能剖析器还给我们提供了很多定位分析CPU的功能方法,更多高级的用法,小伙伴们可以去Google官网查阅-CPU性能剖析器性能分析 3.2.4 静态代码检查工具LintLint是一个静态扫描工具,可以在未编译时对工程进行扫描,帮我们识别出代码的一些潜在问题,而在我们工程项目较大,xml等布局文件多的时候,Lint可以快速的帮助我们把层级较深、单页面控件较多的布局文件定位出来,让我们能够快速的优化我们的布局。 扫描规则与缺陷级别在开始扫描前,我们可以通过设置扫描规则与缺陷级别来帮助我们去做优先扫描以及剔除一些不需要的规则,针对性的提高我们的扫描速度。Mac版Lint扫描规则设置窗口打开步骤 Preferences-->Inspections-->Lint,如下图所示: 与布局相关的扫描规则在Performance中
设置好Lint的扫描规则后,可以在Android studio的菜单栏中启动Lint,Analyze-->Inspect code,在扫描前我们可以选择对整个工程进行扫描,也可以去扫描指定module或者文件:
GPU 渲染模式分析工具以滚动直方图的形式直观地显示渲染界面窗口帧所花费的时间(以每帧 16 毫秒的速度作为对比基准)。 在性能较低的 GPU 上,可用的填充率(GPU 填充帧缓冲区的速度)可能很低。随着绘制一帧所需的像素数的增加,GPU 可能需要更长的时间来处理新命令,并要求系统的其余任务等待,直到它跟上进度。此分析工具可帮助确定 GPU 何时因尝试绘制像素而不堪重负,或何时因大量的过度绘制而被拖累。 启用分析器开始前,请确保使用的是搭载 Android 4.1(API 级别 16)或更高版本的设备,并启用开发者选项。如需在使用应用时开始分析设备 GPU 渲染,请执行以下操作(以下步骤可能因不同产商设备而有所不同): 在设备上,转到 设置 并点按 开发人员选项。 在 监控 部分,选择 GPU呈现模式分析。 在“GPU 呈现模式分析”对话框中,选择在屏幕上显示为条形图,以在设备的屏幕上叠加图形。 打开要分析的应用。 检查输出
这是开发者选项中的另一个功能,通过对界面进行彩色编码来帮助我们识别过度绘制。当应用在同一帧中多次绘制相同像素时,便会发生过度绘制。因此,此图可显示应用可能在何处执行不必要的渲染工作,这可能是 GPU 多此一举地渲染用户不可见的像素所导致的性能问题。因此,应尽可能修复过度绘制事件。 启用过渡绘制检测功能应先启用开发者选项(如果尚未执行此操作)。然后,如需在备上直观呈现过度绘制问题,请按以下步骤操作: 在设备上,转到 设置 并找到开发人员选项; 向下滚动到硬件加速渲染部分,并选择调试 GPU 过度绘制; 在调试 GPU 过度绘制对话框中,选择显示过度绘制区域; 检查输出
traceview是Android sdk中的一个工具,用于分析计算性能,跟踪方法耗时导致的卡顿问题。它将traceview文件转为图形,直观的反应出代码的执行时间、执行次数,便于我们分析。 生成方式 代码生成在需要调试的代码段前后分别加入下面两行代码: Debug.startMethodTracing("FirstKotlin-MainActivity") ... Debug.stopMethodTracing() 复制代码然后启动程序执行对应代码的功能后,对应的trace文件将会保存到应用程序的沙盒目录下:sdcard/Android/data/PackageName/files/FirstKotlin-MainActivity.trace,此时可以通过adb命令拉取到电脑上进行分析 Profile CPU分析工具生成
如果使用代码插桩生成的trace文件,在复制到电脑上之后,可以使用Android studio的Profile进行导入,在性能剖析器的 Sessions 窗格中点击 Start new profiler session 图标
Systrace是Android4.1中新增的性能数据采样和分析工具。它可帮助开发者收集Android关键子系统(如surfaceflinger、WindowManagerService等Framework部分关键模块、服务,View系统等)的运行信息,从而帮助开发者更直观的分析系统瓶颈,改进性能。 Systrace的功能包括跟踪系统的I/O操作、内核工作队列、CPU负载以及Android各个子系统的运行状况等。在Android平台中,它主要由3部分组成: 内核部分:Systrace利用了Linux Kernel中的ftrace功能。所以,如果要使用Systrace的话,必须开启kernel中和ftrace相关的模块。 数据采集部分:Android定义了一个Trace类。应用程序可利用该类把统计信息输出给ftrace。同时,Android还有一个atrace程序,它可以从ftrace中读取统计信息然后交给数据分析工具来处理。 数据分析工具:Android提供一个systrace.py(python脚本文件,位于Android SDK目录/platform-tools/systrace中,其内部将调用atrace程序)用来配置数据采集的方式(如采集数据的标签、输出文件名等)和收集ftrace统计数据并生成一个结果网页文件供用户查看。 从本质上说,Systrace是对Linux Kernel中ftrace的封装。应用进程需要利用Android提供的Trace类来使用Systrace. 简单使用一般我们可以使用命令行来输出html表单,在4.3版本及以上可以省略设置跟踪类别标签来获取默认值。命令如下: python systrace.py --time=10 -o trace.html 复制代码
上面的命令行代码是最为基本的使用,在实际开发检测中,我们可以使用-l指令去获取当前链接设备支持检测的系统进程: python systrace.py -l 复制代码
此时生成的分析文件则是针对GPU、RenderThread等与渲染图形的系统进程相关的 下图还提供了更多systrace相关的类型或选项的命令,令我们可以更有针对性的去生成分析文件: 关于如何分析Systrace的报告,这里就不再叙述了,小伙伴们可以去google官网阅读浏览 Systrace 报告 3.2.8 btracebtrace(又名 RheaTrace)是字节跳动开源的 一个基于 Systrace 实现的高性能 Android trace 工具,它支持在 App 编译期间自动注入自定义事件,并使用 bhook 额外提供 IO 等 native 事件。 关键特征: 支持自动注入自定义事件,在编译 Apk 期间为 App 方法自动注入Trace#beginSection(String) 和 Trace#endSection() 。 提供额外 IO 等 native 事件,方便定位耗时原因。 支持仅采集主线程 trace 事件。 使用便捷,稳定性高,性能优于 Systrace。 关于如何导入btrace以及使用btrace,小伙伴们可以在github上进行查阅btrace。更多与Systrace的异同点,小伙伴们也可以阅读字节跳动技术团队发表的文章btrace 开源!基于 Systrace 高性能 Trace 工具 四、优化方法 4.1 布局优化 4.1.1 减少UI布局层级 合理使用RelativeLayout和LinearLayout。 合理使用Merge。 使用 ConstraintLayout 替代 RelativeLayout 或者 weighted LinearLayout。 合理使用RelativeLayout和LinearLayoutRelativeLayout也存在性能低的问题,原因是RelativeLayout会对子View做两次测量。但如果在LinearLayout中有weight属性,也需要进行两次测量,但是因为没有更多的依赖关系,所以仍然会比RelativeLayout的效率高。 合理使用Mergemerge标签是用来帮助在视图树中减少重复布局的,当一个layout包含另外一个layout时。merge标签使用时有一些要点需要注意: merge必须放在布局文件的根节点上; merge并不是一个ViewGroup,也不是一个View,它相当于声明了一些视图,等待被添加; merge标签被添加到A容器下,那么merge下的所有视图将被添加到A容器下; 因为merge标签并不是View,所以在通过LayoutInflate.inflate方法渲染的时候, 第二个参数必须指定一个父容器,且第三个参数必须为true,也就是必须为merge下的视图指定一个父亲节点; 因为merge不是View,所以对merge标签设置的所有属性都是无效的; 使用 ConstraintLayout使用 ConstraintLayout 替代 RelativeLayout 或者 weighted LinearLayout。ConstraintLayout的效果与RelativeLayout相似,都是以各组件间的相互约束的条件来实现布局,但在较为复杂的界面中,ConstraintLayout不仅能有效的降低布局层级,也能减少布局嵌套,在性能上优于RelativeLayout与LinearLayout。而且ConstraintLayout可以在Android studio中以拖拽的方式快速搭建我们的布局界面,也能有效的提高开发效率 4.1.2 使用ViewStub提高显示速度ViewStub是一个轻量级的View,它是一个看不见的,并且不占布局位置,占用资源非常小的视图对象。可以为ViewStub指定一个布局,加载布局时,只有ViewStub会被初始化,然后当ViewStub被设置为可见时,或是调用了ViewStub.inflate()时,ViewStub所指向的布局会被加载和实例化,然后ViewStub的布局属性都会传给它指向的布局。这样,就可以使用ViewStub来设置是否显示某个布局。 使用ViewStub时需要注意以下几点: ViewStub只能加载一次,之后ViewStub对象会被置为空。换句话说,某个被ViewStub指定的布局被加载后,就不能再通过ViewStub来控制它了。所以它不适用于需要按需显示隐藏的情况。 ViewStub只能用来加载一个布局文件,而不是某个具体的View,当然也可以把View写在某个布局文件中。如果想操作一个具体的View,还是使用visibility属性。 VIewStub中不能嵌套Merge标签。 4.1.3 布局复用可以使用include标签去导入重复的布局 4.1.4 背景优化尽量不要重复去设置背景,这里需要注意的是主题背景(theme), theme 默认会是一个纯色背景,如果我们自定义了界面的背景,那么主题的背景我们来说是无用的。但是由于主题背景是设置在 DecorView 中,所以这里会带来重复绘制,也会带来绘制性能损耗。 4.1.5 小结提高布局效率的方法总体来说就是减少层级,提高绘制速度和布局复用。影响布局效率主要有以下几点: 布局的层级越少,加载速度越快。 减少同一层级控件的数量,加载速度会变快。 一个控件的属性越少,解析越快。 根据前面的分析,对优化的总结如下: 合理使用RelativeLayout或LinearLayout 复杂布局可使用 ConstraintLayout替 代RealtiveLayout与LinearLayout 使用标签减少布局的嵌套层次。 使用标签加载一些不需要立即显示的布局。 将可复用的组件抽取出来并通过标签使用。 尽可能少用wrap_content, wrap_content会增加布局measure时的计算成本,已知宽高为固定值时,不用wrap_content。 删除控件中的无用属性。 4.2 避免过渡绘制造成过渡绘制的原因一般有以下两点: XML布局:控件有重叠且都有设置背景。 自定义View:onDraw方法里面同一个区域被绘制多次。 4.2.1 如何检测过渡绘制可参考3.2.5 分析过渡绘制 4.2.2 如何避免过渡绘制 移除XML中非必需的背景,或根据条件设置。 有选择性地移除窗口背景:getWindow().setBackgroundDrawable(null)。 按需显示占位背景图片。 自定义View可以通过canvas.clipRect()来帮助系统识别那些可见的区域。这个方法可以指定一块矩形区域,只有在这个区域内才会被绘制,其他的区域会被忽视。canvas.clipRect()可以很好地帮助那些有多组重叠组件的自定义View来控制显示的区域。clipRect方法还可以帮助节约CPU与GPU资源,在clipRect区域之外的绘制指令都不会被执行,那些部分内容在矩形区域内的组件,仍然会得到绘制,并且可以使用canvas.quickreject()来判断是否没和某个矩形相交,从而跳过那些非矩形区域内的绘制操作。 4.3 硬件加速绘制从 Androd 3.0 开始,Android 开始支持硬件加速,到 Android 4.0 时,默认开启硬件加速。
如以ListView或者RecyclerView等滑动控件为例,在控件滑动时,可以暂停加载数据,在控件停止后再加载数据。 控制界面刷新区域如自定义View一般采用invalidate方法刷新,可以使用以下重载方法指定要刷新的区域,而不是直接invalidate()全屏刷新: invalidate(Rect dirty); invalidate(int left, int top, int right, int bottom); 4.5 提高动画性能Android平台提供了三个动画框架:帧动画(Frame Animation)、补间动画(Tween Animation)和属性动画(Property Animation)。 通过动画可以实现很多酷炫的动画,但带来的性能开销也有不同程度的影响。在实现动画的过程中,主要从以下三个纬度来对比性能: 流畅度:流畅度是动画的核心,控制每一帧动画在16ms以内完成。 内存:避免内存泄漏,减小内存开销。 耗电:减小运算量,优化算法,减小CPU占用。 4.5.1 帧动画帧动画需要依赖图片资源,当图片过多或者较大时,内存占用就非常大,效果也比较差。所以一般情况下,因为其消耗资源过多,性能最差,因此最好不使用帧动画去实现动画效果。 4.5.2 补间动画补间动画是通过对某个View进行一系列的操作来改变显示效果,对比逐帧动画,它的使用更简单方便。 优点: 不需要定义时间频率内的每一帧,只需要定义开始和结束关键帧的内容,两个关键帧之间的效果自动生成,使用时不需要另写代码控制 补间动画支持淡入淡出(AlphaAnimation)、缩放(ScaleAnimation)、平移(TranslationAnimation)和旋转(RotateAnimation)4种动画模式,并支持以上四种模式进行动画组合 缺点: 使用补间动画实现动画会导致View重绘非常频繁; 补间动画只能用于View对象,也就是只有继承于View或者View的控件,才能用补间动画实现动画效果; 补间动画是改变View的显示效果,但是不会真正改变View的属性; 4.5.3 属性动画属性动画由Android 3.0(API 11)及更高版本支持,通过修改动画的实际属性来实现动画效果。 优点: 可以定义多个特性:动画持续时间、时间插值、重复次数和行为、动画集合以及帧刷新延迟 相比于补间动画,属性动画重绘明显少很多,性能也明显更优秀 4.5.4 在动画上使用硬件加速通过硬件加速来渲染提高动画的性能,可以实现更为快速、平滑的效果。硬件纹理操作对一个View进行动画绘制,如果不调用invalidate()方法,就可以减少对View自身频繁的重绘。 离屏缓冲或者Layer能够更高效地处理复杂view的动画效果或者UI合成效果。Android 3.0开始对何时以及如何使用层有了更多的控制,也就是新的离屏缓冲方式View.setLayerType(type,paint)方法,这个API所带的两个参数一个是使用的层类型,另外一个是可选参数Paint。 一个View可以使用如下三种Layer类型之一: LAYER_TYPE_NONE:普通渲染方式,不会返回一个离屏的缓冲,默认值。 LAYER_TYPE_HARDWARE:如果这个应用使用了硬件加速,这个View将会在硬件中渲染为硬件纹理,如果应用程序并没有被硬件加速,则其效果和LAYER_TYPE_SOFTWARE相同。 LAYER_TYPE_SOFTWARE:此View通过软件渲染为一个Bitmap。虽然硬件加速可以带来更好的绘制效果,但也有一些问题,以下几点需要注意: 在软件渲染时,可以使用重用Bitmap的方法来节省内存,但是如果开启了硬件加速,这个方案就不起作用。 开启硬件加速的View在前台运行时,需要耗费额外的内存,加速的UI切换到后台时,产生的额外内存有可能不释放。 当UI中存在过渡绘制时,硬性加速会比较容易发生问题 五 卡顿的监控与实现 5.1 识别卡顿常用的识别卡顿方法有以下几种: 目视检查方法 Systrace 方法 自定义性能检测方法 目视检查方法目视检查有助于找出导致卡顿的用例,以下是关于进行目视检查的一些提示: 运行应用的发布版本(或至少是不可调试的版本)。为了支持调试功能,ART 运行时会停用几项重要的优化功能,因此请务必确保看到的内容与用户将会看到的内容类似。 启用 GPU 渲染模式分析功能:GPU 呈现模式分析功能会在屏幕上显示一些条形,以相对于每帧 16ms 的基准,快速直观地显示呈现界面窗口帧所花的时间。每个条形都有带颜色的区段,对应于呈现管道中的一个阶段,这样就可以看到哪个部分用时最长。例如,如果帧花费大量时间处理输入,应查看负责处理用户输入的应用代码。 某些组件(如 RecyclerView)是卡顿的常见来源。如果应用使用了这些组件,最好查看一下应用的这些部分。 有时,只有当应用通过冷启动进行启动时,才能重现卡顿。 可以尝试在速度较慢的设备上运行应用,以突显此问题。 Systrace方法Systrace 工具用于显示整个设备在做些什么,也可用于识别应用中的卡顿。Systrace 的系统开销非常小,因此可以在插桩测试期间体验实际卡顿情况。 在设备上执行卡顿的用例时,可以使用 Systrace 记录跟踪信息。 自定义性能监控目前比较流行的方案都是利用了Looper中的Printer来实现监控。 监控原理利用主线程的消息队列处理机制,通过自定义Printer,然后在Printer中获取到两次被调用的时间差,这个时间差就是执行时间。如果该时间超过设定的卡顿阈值(如1000ms)时,主线程卡顿发生,并抛出各种有用信息,供开发者分析。(此外,也可以在UI线程以外开启一个异步线程,定时向UI线程发送一个任务,并记下发送时间。任务的内容是将执行时间同步到发送线程,如果UI线程被阻塞,那么发送过去的任务不能被准时执行。但此方法会增加系统开销,不可取) 卡顿信息捕获发生卡顿时需要捕获如下四类信息,以提高定位卡顿问题的效率与精度。 1、基础信息:系统版本、机型、进程名、应用版本号、磁盘空间、UID等。 2、耗时信息:卡顿开始和结束时间。 3、CPU信息:CPU的信息、整体CPU使用率和本进程CPU使用率(可粗略判断是当前应用消耗CPU资源太多导致的卡顿,还是其他原因)等。 4、堆栈信息。 5.2 常见卡顿来源 可滚动列表ListView 和 RecyclerView (尤其是后者)常用于最易出现卡顿的复杂滚动列表。它们都包含 Systrace 标记,因此可以使用 Systrace 来判断它们是不是导致应用出现卡顿的因素。 布局性能如果 Systrace 表明 Choreographer#doFrame 的布局部分执行的工作过多或者执行工作的频率太高,则意味着遇到了布局性能问题。应用的布局性能取决于视图层次结构的哪个部分包含会发生改变的布局参数或输入。 呈现性能Android 界面工作分为两个阶段:界面线程上的 Record View#draw 和 RenderThread 上的 DrawFrame。第一阶段对每个失效的 View 运行draw(Canvas),并可调用自定义视图或代码。第二阶段在原生 RenderThread 上运行,但将根据 Record View#draw 阶段生成的工作运行。 线程调度延迟线程调度程序在 Android 操作系统中负责确定系统中的哪些线程应该运行、何时运行以及运行多长时间。有时,出现卡顿是因为应用的界面线程处于阻塞或未运行状态。Systrace 使用不同的颜色(见下图)来指明线程何时处于休眠状态(灰色)、可运行(蓝色:可以运行,但调度程序尚未选择让它运行)、正在运行(绿色)或处于不可中断休眠状态(红色或橙色)。这对于调试由线程调度延迟引起的卡顿问题非常有用。 自从 ART 在 Android 5.0 中作为默认运行时引入后,对象分配和垃圾回收 (GC) 问题已显著缓解,但这项额外的工作仍有可能加重线程的负担。我们可以针对每秒不会发生多次的罕见事件(例如用户点按一个按钮)进行分配,但请记住,每次分配都会产生开销。如果它处于被频繁调用的紧密循环中,应考虑避免分配以减轻 GC 上的负载。 Systrace 会显示 GC 是否频繁运行,而 Android Memory Profiler 可显示分配来源。如果尽可能避免分配(尤其是在紧密循环中),则应该不会遇到问题。
在较新版本的 Android 中,GC 通常在名为 HeapTaskDaemon 的后台线程上运行。请注意,大量的分配可能意味着在 GC 上耗费更多的 CPU 资源,如下图所示:
文章到这,对绘制优化的理解可以总结成以下几点: 了解绘制原理:了解绘制的原理,有利于我们更快的定位问题,帮助我们找到问题优化的切入口,寻找解决方案; 发现问题:除了使用时感官的感受卡顿,线上问题还应通过一些卡顿监控技术去发现问题与记录问题; 分析问题:利用好性能分析工具,包括Hierarchy View、Layout inspector布局检查工具,TraceView与Systrace性能耗时跟踪记录等; 解决问题:结合实际的应用场景,去优化解决现有的问题;UI绘制带来主要问题是卡顿,但造成卡顿问题的不仅只有UI绘制掉帧一个原因,还有主线程繁忙、被阻塞以及内存使用不合理等问题,严重的卡顿甚至会导致应用出现ANR现象,这种用户体验会更为糟糕。对于一个庞大的应用来说,性能优化是提高用户体验不可或缺的,也是一项任重道远的任务,让我们一起持续学习与成长,文章如有不足之处,请多谅解并指正,谢谢!!! 七 参考文献 《Android移动性能实战》 《Android应用性能优化最佳实践》 Android性能优化之绘制优化-JsonChao Android开发高手课-UI优化-张绍文 Android绘制优化-刘望舒 Android 卡顿方案研究 Android卡顿监控方案实践 Android Project Butter分析 RenderThread:异步渲染动画 再次感谢上述书籍与文章的作者,让我能够站在巨人的肩膀上成长,也感谢喜欢本文章的小伙伴们,希望能分享更多自己学习掌握的东西,与更多志同道合的人一起成长 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |