UI优化

2019-09-30  本文已影响0人  修塔寻千里

UI渲染基础

1、屏幕与适配


通过dp和自适应布局可以基本解决屏幕碎片化的问题,这也是Android推荐使用的屏幕兼容性适配方案,但它存在两个比较大的问题:

2、CPU与GPU

UI组件在绘制到屏幕之前,都需要经过Rasterization(栅格化)操作,而栅格化操作有是一个非常耗时的操作,GPU也就是图形处理器,它主要用于处理图形运算,可以帮助我们加快栅格化操作。


3、OpenGL和Vulkan

硬件绘制,我们通过调用OpenGL ES接口利用GPU完成绘制,OpenGL是一个跨平台的图形API,它为2D/3D图形处理硬件制定了标准软件接口,而OpenGL ES是OpenGL的子集,专为嵌入式设备设计。



Android 7.0把OpenGL ES升级到最新的3.2版本同时,还添加了对Vulkan的支持。Vulkan是用于高性能的3D图形的低开销、跨平台API。在改善功耗、多核优化提升绘图调用上有着非常明显的优势。

Android渲染


Android各组件的作用:

1、Android 4.0开启硬件加速


在Android3.0之前,或者没有启用硬件加速时,系统会使软件方式渲染UI,整个流程如上图所示:

2、Android 4.1:Project Butter

Project Butter主要包含两个组成部分:一个是VSYNC,一个是Triple Buffering。

VSYNC信号

VSYNC类似于时钟中断,每收到VSYNC中断,CPU会立即准备Buffer数据,由于大部分显示设备刷新频率都是60HZ(一秒刷新60次),也是一帧数据的准备工作要在16ms内完成。



这样应用总是在VSYNC边界上开始绘制,而SurfaceFlinger总是VSYNC边界上进行合成,这样可以消除卡顿,并提升图形的视觉表现。

三级缓冲机制Triple Buffering

在Android 4.1之前都使用双缓冲机制,不同的View或者Activity它们都会共用一个Window,也就是共用一个Surface。而每个Surface都会有一个BufferQueue缓存机制,但是这个队列会由SurfaceFlinger管理,通过匿名共享内存机制与App应用成交互。



整个流程如下:

数据测量

Android 4.1新增了Systrace性能数据采样和分析工具,Tracer for OpenGL ES也是Android 4.1新增的工具,可逐帧、逐函数的记录App用OpenGL ES的绘制过程。它提供了每个OpenGL函数调用的消耗时间,所以很多时候用来做性能分析。在Android 4.2系统增加了检测过度绘制工具。

3、Android 5.0:RenderThread

经过Project Buffer黄油计划之后,Android的渲染性能有了很大的改善,但是有一个问题,虽然我们利用了GPU的图形高性能运算,但是从计算DisplayList,到通过GPU绘制到Frame Buffer,整个计算和绘制都在UI主线程中完成。



UI主线程任务过于繁重,如果整个渲染过比较耗时,可能会造成用户无法响应的操作,进而出现卡顿。GPU对图形的绘制渲染能力更胜一筹,如果使用GPU并在不同的线程绘制渲染图形,那么整个流程会更加顺畅。
所以Android 5.0引入了两个比较大的改变。一是RenderNode,它对DisplayList及一些View的显示属性做了进一步的封装;另一个是RenderThread,所欲的GL命令执行都放到这个线程上,渲染线程在RenderNode中存有渲染帧的所有信息,可以做一些属性动画,这样即便主线程有耗时的操作也可以保证动画流程。
我们可以开启Profile GPU Rendering检查,在Android 6.0之后,会输出下面的计算和绘制每个阶段的耗时:



如果将上面的步骤转化为线程模型,可以得到下面的流水线模型。CPU将数据同步(sync)给GPU之后,一般不会阻塞等待GPU渲染完毕,而是通知结束后返回,而RenderThread承担了比较多的绘制工作,分担了主线程很多压力,提高了UI线程的响应速度。

UI渲染测量

1、gfxinfo

gfxinfo可以输出包含各阶段发生的动画及帧相关的性能信息,具体命令如下:

adb shell dumpsys gfxinfo 包名

除了渲染性能之外,gfxinfo还可以拿到渲染相关的内存和View hierachy信息,在Android 6.0之后,gfxinfo命令增加了framestats参数,可以拿到最近120帧每个绘制阶段的耗时信息。通过这个命令可以实现自动化统计应用的帧率,更进一步还可以实现自定义的“Profile GPU Rendering”工具。

adb shell dumpsys gfxinfo 包名 framestats

2、SurfaceFlinger

关于渲染使用的内存情况,可以使用下面的命令拿到SurfaceFlinger相关的信息:

adb shell dumpsys SurfaceFlinger

UI优化的常用手段


要求所有的渲染操作都必须在16ms(=1000ms / 60fps)内完成,UI优化就是拆解熏染的各个阶段的耗时,找到瓶颈点,再加以优化。

1、尽量使用硬件加速

硬件加速绘制的性能是远大于软件绘制,所以要尽可能的采用硬件加速。有些情况不能采用硬件加速,因为硬件加速不支持所有的Canvas API,具体API兼容列表,查看drawing-support,如果使用了不支持的API,系统就需要通过CPU软件模拟绘制,这也是渐变、磨砂、圆角等效果渲染性能比较低的原因。
SVG也是一个非常典型的例子,SVG很多指令硬件加速都不支持,但可以提前将SVG转换成Bitmap保存起来,这样系统就可以更好的使用硬件加速会绘制,同理对于圆角、渐变等场景,也可以改为Bitmap实现。

2、Create View优化

View的创建是在主线程完成的,对于一些负责的界面,耗时会增加,根据View的创建流程有如下优化点:

使用代码创建

XML进行UI编写十分方便且提供可视化预览,但是效率方面大打折扣,建议在对性能要求非常高,且修改不频繁的页面采用代码创建的方式。

异步创建

如果再线程提前创建View,会报如下的错误:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() at android.os.Handler.(Handler.java:121)

解决方案是先将线程的Looper的MessageQueue替换成UI线程Looper的Queue,最后在创建完View后把线程的Looper恢复成原来的。


View复用

正常来说,View会随着Activity的销毁而同时销毁,ListView、RecycleView通过View的缓存和重用大大提升渲染性能。因此可以实现一套可以在不同Activity或Fragment使用的View缓存机制。但是这里需要保证所有进入缓存的View不会保留之前的状态。


3、measure/layout优化

UI优化其他手段

1、Litho:异步布局

Litho是Facebook开源的声明式Android UI渲染框架,本身非常强大,内部做了很多优化。

Litho把measure和layout都放到了后台线程,只留下了必须要再主线程完成的draw,大大降低了UI线程的负载。


2、Flutter:自己的布局+渲染引擎

Flutter是谷歌推出的开源移动应用开发框架,可发着可以通过Dart语言开发App,一套代码同时运行在iOS和Android平台。在Android上Flutter完全没有基于系统的渲染引擎,而是把Skia引擎直接集成到App中,并且直接使用了Dart虚拟机。



开发Flutter应用总的来说简化了线程模型,框架给我们抽象出各司其职的Runner,包括UI、GPU、I/O、Platform Runner。Android平台上面没一个引擎实例启动的时候都会为UI Runner、GPU Runner、I/O Runner各自创建一个新的线程,所有Engine实例共享同一个Platform Runner和线程。

3、RenderThread与RenderScript

在Android 5.0系统增加了RenderThread对于ViewPropertyAnimator和CircularReveal动画,可以使用RenderThread实现动画的异步渲染。图片的变换涉及大量的计算任务,可以通过RenderScript提高性能,它是Android操作系统提供的一套API,基于异构计算思想,专门用于密集计算。RenderScript提供了三个基本工具:一个硬件无关的通用计算API;一个类似于CUDA、OpenGL和GLSL的计算API;一个类C99的脚本语言,语序开发者以较少的代码实现功能复杂且性能优越的应用程序。

上一篇下一篇

猜你喜欢

热点阅读