性能优化之绘制优化

2019-04-11  本文已影响0人  _Rice_

系统显示原理

显示过程

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

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

卡顿原因

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

如果某个操作花费24ms,系统在得到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的可见性控制

减少布局层次原则:

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

优化总结:

避免过度绘制

导致过度绘制的主要原因

检测工具

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

image.png

优化途径

xml上的优化

例如移除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全局变量保存。

相关抽象接口

启动流程

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

启动分类

优化方案

合理刷新

减少刷新次数

避免后台有高CPU线程运行

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

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

缩小刷新区域

提升动画性能

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

硬件加速

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

硬件加速控制级别

并不是所有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);

设计一个动画流程

硬件加速注意事项

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

注意事项
不支持硬件加速的相关接口

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

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

卡顿监控方案

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

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

上一篇下一篇

猜你喜欢

热点阅读