Android界面渲染优化

上篇介绍了android界面渲染主要是Display的过程,只要在一个时间段也就是16ms中,CPU和GPU不能正常处理完数据就会产生卡顿.

  • CPU(中央处理器) :多缓存多分支,适用于复杂的逻辑运算,主要负责Measure,Layout,Record,Execute的计算操作
  • GPU(图像处理器):众核少缓存,适用于结构单一的数据处理,主要负责Rasterization(栅格化)操作

实际上最后还是回到Measure,Layout,和draw上.也就是说上述三个步骤中的某一个步骤出现了耗时较长的操作,就容易导致界面卡顿。一般来说多发生在draw上。

过度绘制(Overdraw)

什么是过度绘制?

Overdraw(过度绘制)描述的是屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次重叠的UI结构里面,如果不可见的UI也在做绘制的操作,会导致某些像素区域被绘制了多次。这样就会浪费大量的CPU以及GPU资源。

image

如何检测过度绘制

开发者选项->调试GPU过度绘制->显示过度绘制区域�

image

蓝色,淡绿,淡红,深红代表了4种不同程度的Overdraw情况,我们的目标就是尽量减少红色Overdraw,看到更多的蓝色区域

如何优化过度绘制

  • 移除布局中多余的背景

  • layout层级扁平化,或者该用约束布局(ConstraintLayout)

  • 减少透明度的使用

移除Window默认的Background

通常,我们使用的theme都会包含了一个windowBackground,比如theme的如下:

<item name="android:windowBackground">@color/background_material_light</item>

然后通常我们自己的layout下,会另外加一层背景,这就会引起过度绘制,

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:orientation="vertical">

一般的解决方式把theme下的Background移除

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowBackground">@null</item>
</style>

或者在Activity中的onCreate()下添加

getWindow().setBackgroundDrawable(null);

去除子控件的背景

在开发中,经常为了一些视觉效果,需要给子控件附加背景.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:orientation="vertical">

    <EditText
           ...
            android:background="@drawable/alert_edit_bg1"
           ... />
 <LinearLayout/>

如果按照过度绘制的思想来看这个问题的话,那这里也会村子啊过度绘制的情况,最合理的设想是如果子view有背景,并且跟父view背景不同,那么应该移除父view的背景,保留子view的背景,这样就能避免过度绘制了。

不过如果真的严格按照这个逻辑来写代码的话,会导致多写很多代码.况且以目前手机的性能来讲,x1级别的过度绘制不太会影响到性能。

将layout层级扁平化

使用Layout Inspector去查看layout的层次结构

老版本Android SDK提供的是一个Hierarchy Viewer工具,可以用来分析布局深度,

但是在Android studio3.1之后,换成了Layout Inspector,入口在Tools-->Layout Inspector

使用嵌套少的布局

相同层级下,Fragment的效率最高,其次是LinearLayout

LinearLayout对比RelativeLayout
  • LinearLayout在没有设置weight属性的时候,只会对子视图进行一次measure,而RelativeLayout会在水平方向和竖直方向分别进行一次measure
  • 另外,系统的Viewmeasure方法里对绘制过程做了一个优化,如果我们或者我们的子View没有要求强制刷新,而父View给子View的传入值也没有变化(也就是说子View的位置没变化),就不会做无谓的measure。但是RelativeLayout会做两次measure,而在做横向的测量时,纵向的测量结果尚未完成,只好暂时使用myHeight传入子View系统,假如子ViewHeight不等于(设置了marginmyHeight的高度,那么measure中上面代码所做得优化将不起作用,所以尽量使用padding代替margin

RelativeLayout和LinearLayout性能比较

使用抽象布局 include、merge 、ViewStub

include、merge 、ViewStub这三个布局都是官方提供的用于布局优化的

布局重用 include

标签复用,比起性能上的优化,更大的作用我感觉在xml文件的优化上

减少布局层级 merge

<merge/>多用于替换FrameLayout或者当一个布局包含另一个时,<merge/>标签消除视图层次结构中多余的视图组。

  • 如果xml的根部局是FrameLayout,直接替换成merge就能减少一个布局深度。

  • 如果你的主布局文件是垂直布局,引入了一个垂直布局的include,这个时候就可以将include的根布局替换成merge

延迟加载 ViewStub

ViewStub是一个不可见的,大小为0的View,能为xml加载减小不少压力。常用于 进度条、显示错误消息等不需要第一时间显示的视图,inflate()之后会被对应的layout所代替

注:ViewStub目前有个缺陷就是还不支持 <merge /> 标签。

使用lint来优化布局的层次结构

Analyze->Inspect code

布局性能方面的信息位于Android> Lint> Performance下,我们可以点开它来看下一些优化建议。

下面是lint的一些优化技巧:

  1. 使用复合图片
    如果一个线性布局中包含一个 ImageView 和一个 TextView,可以使用复合图片来替换掉
  2. 合并根节点
    如果一个FrameLayout 是整个布局的根节点,并且也没有提供背景、留白等等,那么可以使用<merge>标签来替换掉,因为DecorView本身就是一个FrameLayout
  3. 移除布局中无用的叶子
    布局是一个树形的结构,如果一个布局没有子 View 或者背景,那么可以把它移除掉(这布局本身就不可见了)。
  4. 移除无用的父布局
    如果一个布局没有兄弟,也不是ScrollView 或者根 View,并且也没有背景,那么可以把这个父布局移除掉,然后把它的子view移到它的父布局下。
  5. 避免过深的层次结构
    过多的布局嵌套不利于性能,可以使用更扁平化的布局,如RelativeLayoutGridLayoutConstraintLayout等布局来提高性能。布局默认的最大深度为10

减少透明度的使用

对于不透明的view,只需要渲染一次即可把它显示出来。但是如果这个view设置了alpha值,则至少需要渲染两次。这是因为使用了alphaview需要先知道混合view的下一层元素是什么,然后再结合上层的view进行Blend混色处理。透明动画、淡入淡出和阴影等效果都涉及到某种透明度,这就会造成了过度绘制。可以通过减少渲染这些透明对象来改善过度绘制。比如:在TextView上设置带透明度alpha值的黑色文本可以实现灰色的效果。但是,直接通过设置灰色的话能够获得更好的性能。

减少自定义View的过度绘制,使用clipRect()和QuickReject()

  • clipRect():可以指定一块矩形区域,只有在这个区域内才会被绘制,其他的区域会被忽视,可以有效的节约CPU与GPU的资源

    canvas.clipRect(0, 0,200,200);  //指定(0,0)到(200,200)的矩形区域为绘制区域
    
  • QuickReject():判断画布是否与你指定的矩形相交,借此来决定是否需要绘制该画布

boolean skipDraw=canvas.quickReject(new RectF(), Canvas.EdgeType.AA);

onDraw()中不要创建新的局部变量以及不要做耗时操作

onDraw()中不要创建新的局部变量,因为onDraw()方法可能会被频繁调用,大量的临时对象会导致内存抖动,会造成频繁的GC,从而使UI线程被频繁阻塞,导致画面卡顿。这种问题一般android studio的lint静态检查都会报警告,平时多注意些android studio标黄的警告,有助于性能调优

尝试使用ConstraintLayout

  • 较高的性能优势

    比起LinearLayout 、RelativeLayout等,ConstraintLayout可以用更少的布局层级来达到相同的效果

  • 完美的屏幕适配

    ConstraintLayout的大小、距离都可以使用比例来设置,所以其适配性更好。

当然不是任何时候都推荐使用ConstraintLayout的,在简单的布局下还是建议使用LinearLayoutRelativeLayout,因为对于相同层级的布局下,ConstraintLayoutmeasure,layout,draw等过程会显得更加重.

参考文档

Android性能优化之渲染篇

那些 Android 程序员必会的视图优化策略

Android 约束布局 ConstraintLayout图形化操作

约束布局ConstraintLayout用法全解析

官方文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容

  • 太长不看版:在 Android UI 布局过程中,遵守一些惯用、有效的布局原则,可以制作出高效且复用性高的 UI。...
    Mupceet阅读 3,831评论 0 14
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,943评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,709评论 2 59
  • 注意事项: 布局优化;尽量使用include、merge、ViewStub标签,尽量不存在冗余嵌套及过于复杂布局(...
    HarryXR阅读 5,158评论 1 19