美文网首页
性能优化之绘制优化

性能优化之绘制优化

作者: _Rice_ | 来源:发表于2019-04-11 15:37 被阅读0次

    系统显示原理

    显示过程

    安卓应用通过测量,布局,绘制后的surface缓存数据,通过SurfaceFlinger把数据渲染到显示屏幕上,通过安卓刷新机制来刷新数据。

    扩展测量、布局、绘制原理

    卡顿原因

    理想情况下,60FPS(每秒传递的帧数)感觉不到卡,也就是每次绘制时长应该在16ms以内。安卓系统每隔16ms发出VSYNC信号,触发对UI进行渲染。

    如果某个操作花费24ms,系统在得到VSYNC信号就无法正常渲染,就会发生丢帧现象。如果在动画或者滑动等场景,就会感觉到卡顿不顺畅。

    卡顿原因:

    • 绘制任务太重,绘制一帧内容耗时太长
    • 主线程太忙,导致VSYNC信号来时还没准备好数据导致丢帧

    主线程主要做以下几个方面的工作

    • 系统事件处理
    • 消息处理
    • 界面布局
    • 界面绘制
    • 界面刷新

    性能分析工具

    Profile GPU Rendering(GPU呈现模式分析)

    功能:卡顿检测

    开启方式:开发人员选项-GPU呈现模式分析-在屏幕上显示为条状图

    图示:


    image.png
    • 蓝色:绘制时间
    • 共色:执行时间
    • 橙色:处理时间

    超过绿色警戒线,就可能丢帧。所以保存UI流畅关键是让这些垂直柱状条尽可能保存在绿线以下。

    Android Profiler分析器

    功能:分析cpu、内存 、网络使用情况

    开启方式:View > Tool Windows > Android Profiler

    使用教程:https://blog.csdn.net/niubitianping/article/details/72617864

    Analyze

    功能:代码、布局检查

    配置:File-Settings-Inspections-Android Lint

    检测:Analyze-Inspect Code

    布局优化

    include

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".performance.LayoutOptimizationActivity">
    
        <include android:id="@+id/include"
            layout="@layout/include_layout"/>
    </LinearLayout>
    

    原理:在解析xml布局的时候,如果检测到include标签,那么直接将该布局下的根布局添加到include下的父视图中。

    注意:include布局下的根布局id会动态修改,会被设置成include标签中的id值。

    merge

    子布局和父布局都是同一种类型,如FrameLayout,那么可以使用merge进行布局优化

    作用:合并UI布局,使用merge标签能降低UI布局的嵌套层次

        <include android:id="@+id/include"
            layout="@layout/include_layout"/>
    
    <?xml version="1.0" encoding="utf-8"?>
    <merge xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        tools:context=".performance.LayoutOptimizationActivity">
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="我是include_layout"/>
    
    </merge>
    

    注意事项

    merge只能用在布局xml文件的根元素
    使用merge加载一个布局时,必须制定一个viewGroup作为其父元素,并且要设置加载的attachToRoot参数是true(参照inflate(int,viewGroup,boolean))
    不能在ViewStub中使用Merge标签,因为ViewStub的inflate方法中根本就没有attachToRoot的设置

    ViewStub

    ViewStub是一个不可见和能在运行期间延迟加载目标视图的、宽高都为0的View,在使用inflate()或者设置visible之前,是占用布局空间和系统资源的,它只是一个为目标视图占了个位置而已

    原理:当手动调用inflate()或者设置visible(实际上也是调用inflate函数),会将ViewStub从父控件移除,并加装目标控件,然后将目标控件添加到ViewStub父控件中去,这就完成了视图动态替换,也就是延迟加载功能

        <ViewStub
            android:id="@+id/stub"
            android:layout_marginTop="50dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout="@layout/view_stub_layout"
            />
    
            mStub.inflate();
    //    mStub.setVisibility(View.VISIBLE);
    

    注意事项

    ViewStub只加载一次,之后会被置空,如果要控制某个布局的显示或者隐藏,只能使用View的可见性控制

    减少布局层次原则:

    布局层次越少、控件越少加载速度越快,属性越少,解析越快。

    优化总结:

    • 尽量多使用RelativeLayout,避免使用LinearLayout的layout_weight属性,不要使用绝对布局AbsoluteLayout
    • 将可复用的组件抽取出来并通过<include/>标签使用
    • <ViewStub/>来加载不常用的布局
    • <merge/>减少布局层次
    • 尽可能少用wrap_content,wrap_content会增加布局measure时的计算成本
    • 删除控件中无用属性(属性越少,解析越快)

    避免过度绘制

    导致过度绘制的主要原因

    • xml布局,控件有重叠且都有设置背景
    • View自绘,onDraw里面对同个区域绘制多次

    检测工具

    开发人员选项-调试GPU过度绘制-显示过度绘制区域

    image.png
    • 无色-每个像素绘制1次
    • 蓝色-每个像素多绘制1次
    • 绿色-每个像素多绘制2次
    • 粉色-每个像素多绘制3次,不超过1/4可接受
    • 红色-每个像素多绘制4次或者更多,需优化

    优化途径

    xml上的优化

    • 减少背景叠加,移除xml中不必要的背景,或者根据条件设置
    • 移除Window默认背景
    • 按需显示占位背景图片(???)

    例如移除window默认背景:

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            getWindow().setBackgroundDrawable(null);
        }
    

    自定义View优化

    关键在于canvas.clipRect()和canvas.quickreject()的使用

    (在自定义View复习阶段补充学习)

    启动优化

    要分析启动新能,需要熟悉Application和Activity的工作流程和生命周期

    Application

    启动Application时,系统会创建一个PID,即进程ID,其生命周期是最长的,等于这个应用程序的生命周期,因为它是全局单例。可以避免使用静态变量来存储永久保存值,而是作为Application全局变量保存。

    相关抽象接口

    • attachBaseContext 得到应用上下文的Context,在应用创建时首次调用
    • onCreate 晚于attachBaseContext调用,在应用创建时首次调用
    • onTerminate 应用结束时调用
    • onConfigurationChanged 系统配置发生变化时调用
    • onLowMemory 系统低内存时调用
    • onTrimMemory 系统要求应用释放内存时调用

    启动流程

    启动-Application-attachBaseContext()-onCreate-Activity生命周期

    启动分类

    • 冷启动:创建一个新的进程,创建和初始化Application类
    • 热启动:在已有进程中启动,不会再创建和初始化Application类,Application只初始化一次

    优化方案

    • 在闪屏页,做数据准备,如底层模块的初始化、数据的预拉取
    • UI优化,减少布局层次,避免过度绘制
    • 加载逻辑优化
      1. 必要且耗时:启动初始化,考虑用线程来初始化
      2. 必要且不耗时:首页绘制
      3. 非必要且耗时:数据上报,插件初始化(考虑用线程)
      4. 非必要且不耗时:直接去掉,需要时再加载

    合理刷新

    减少刷新次数

    • 进度条,数据变化没有1%,没必要刷新
    • 不可见的控件,没必要刷新

    避免后台有高CPU线程运行

    如果后台线程开销过大,占用GPU过高,导致系统GC频繁和CPU时间片资源紧张,还是有可能导致页面卡顿。

    例如:列表边滑动,边加载图片,可进行优化成:滑动时停止加载图片,滑动结束继续加载图片

    缩小刷新区域

    • invalidate(Rect dirty)
    • 列表item发生变化,调用notifyDataSetChanged()刷新

    提升动画性能

    性能:属性动画>补件动画>帧动画

    硬件加速

    硬件加速可渲染提高动画性能,使动画更平滑、流畅

    硬件加速控制级别

    并不是所有2D绘制的操作都支持硬件加速,所以我们要合理选择硬件加速控制级别

    Application级别

     <application
            ...
            android:hardwareAccelerated="true"
           ...
     >
    

    Activity级别

     <activity
                android:name=".MainActivity"
                android:hardwareAccelerated="true">
    

    Window级别

    getWindow().setFlags(
                    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
                    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
    

    View级别

    view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    
    • LAYER_TYPE_NONE 默认行为,正常渲染,不会在off-screen buffer上备份
    • LAYER_TYPE_HARDWARE 如果应用开启了硬件加速view渲染在硬件上,否则与LAYER_TYPE_SOFTWARE行为一样
    • LAYER_TYPE_SOFTWARE 用软件渲染在一个bitmap上

    设计一个动画流程

    • LayerType设置成LAYER_TYPE_HARDWARE
    • 更新view属性
    • 动画结束将LayerType设置成LAYER_TYPE_NONE

    硬件加速注意事项

    硬件加速属于双缓冲机制,使用显存进行页面渲染(使用较少的物理内存),导致更频繁的显存操作,低版本手机可能引起以下现象:
    白屏、花屏、闪屏;

    注意事项
    • 软件渲染,可以使用重用Bitmap的方法节省内存,硬件加速不适用
    • 开启硬件加速的View在前台运行,会耗费额外的内存,切换到内存,产生的额外内存有可能不释放
    • 减少过度绘制
    不支持硬件加速的相关接口

    Canvas不支持硬件加速的二维绘图接口:

    • clipPath()
    • clipRegion()
    • drawPicture()
    • drawPosText()
    • drawTextOnPath()
    • drawVertices()

    Paint不支持硬件加速的接口:

    • setLinearText()
    • setMaskFilter()
    • setRasterizer()

    卡顿监控方案

    利用Looper中的Printer来实现监控,重写Printer方法(判断start和end的时间差值),通过Looper.getMainLooper().setMessageLogging(LogPrinter)设置自定义的Printer

    参考:Android应用性能优化最佳实践

    相关文章

      网友评论

          本文标题:性能优化之绘制优化

          本文链接:https://www.haomeiwen.com/subject/xnntwqtx.html