性能优化之绘制优化

系统显示原理

显示过程

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

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

卡顿原因

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

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

卡顿原因:

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

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

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

性能分析工具

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

功能:卡顿检测

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

图示:


性能优化之绘制优化_第1张图片
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布局的时候,如果检测到include标签,那么直接将该布局下的根布局添加到include下的父视图中。

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

merge

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

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

    



    

注意事项

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父控件中去,这就完成了视图动态替换,也就是延迟加载功能

    
        mStub.inflate();
//    mStub.setVisibility(View.VISIBLE);

注意事项

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

减少布局层次原则:

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

优化总结:

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

避免过度绘制

导致过度绘制的主要原因

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

检测工具

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

性能优化之绘制优化_第2张图片
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级别

 

Activity级别

 

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应用性能优化最佳实践

你可能感兴趣的:(性能优化之绘制优化)