Android的UI卡顿

这篇文章我们主要从3个方面分析:

1.UI卡顿的原理

2.UI卡顿的原因分析

3.卡顿的总结

来看第一部分,UI卡顿的原理:

先来看下这样一个数字;

60fps -> 16ms

其实用户所感受到的卡顿的问题主要是来源于安卓的渲染性的问题。

我们的UI设计师,总是希望我们的UI有非常绚丽的动画,非常精美的设计。经常使用一些非常大的图片来展示时尚的元素。但是UI设计师是站在UI设计师的角度来考虑问题。她们不知道安卓系统可能无法完成那些复杂的界面渲染操作。

我们知道安卓的系统每隔16ms会发出信号触发对UI进行渲染。

如果每次渲染都能成功,这样就能达到流畅的画面所需要的60fps,也就是每秒60帧。这就意味着程序的大多数操作必须在16MS内来完成。也就是拿1000/60约等于16ms。在执行一些动画,或者大家常见的Listview滑动的时候,我们通常会感觉到有时会有一些卡顿,没有那么流畅。这就是因为这里的操作很复杂,然后产生了丢帧现象。最终导致了卡顿。其实有很多原因都会造成卡顿。就拿listview来说,如果listview的Item的layout如果太过复杂,它就无法完成在16ms内的渲染。也有可能是你的Item层叠了太多的background,它太多的imageview,甚至有可能是动画执行的次数过多。这些都会造成CPU和GPU的复杂过重。

为什么要把标准设置在60fps,首先我们需要知道人眼对于画面的连续性他是有一定的限制性的。对于手机来说,我们需要感知屏幕的连贯性,我们就必须在这16ms内处理完所有的cpu的计算以及GPU的渲染操作。所以说,其实UI的卡顿是可以量化的。每一次能否成功渲染是非常重要的。

同时我们需要知道每一次GC的时候,所有的线程都会暂停。等GC完了之后,所有的线程才能继续执行。也就是说,如果这个16ms内正在渲染的时候,正好遇到了大量的GC操作。这就会导致渲染时间不够。从而导致卡顿。所以对于对象内存的分配,需要谨慎考虑。

接下来我们来看UI卡顿的第二个概念:

overdraw

中文意思是过度绘制。

啥意思,就是在屏幕上某一帧的像素在屏幕上被绘制了很多次。经常出现在多层次的UI结构里。

如果有时候你把UI设置成invisible或者gone的时候,它也会做绘制的操作。

这就导致某些绘制区被绘制很多次。这样就浪费了CPU和GPU的资源。

在开发过程中,如果老大让你解决UI卡顿问题,你可以用手机当中的GPU选项,在开发者选项中,大家可以观察,overdraw的情况。有蓝色,淡绿色,淡红色等。我们的目标就是减少红色,尽量出现蓝色。

overdraw出现的原因也就是说你的UI布局中,有大量重叠的部分。

还有些时候,是一些非必要的重叠背景。

举个例子,比如在activity中他有自己的背景,如果这个layout又有自己的背景。同时这个layout的子view又有自己的背景。这个时候,仅仅是通过移除非必须的背景图片就能减少红色的overdraw。

总结UI卡顿的原理,这些性能的问题,主要主要的根源就是来自安卓系统的渲染性,做了太多耗时操作。

做太多耗时操作的原因,有可能是你的layout太复杂,也有可能是你的layout上层叠了太多其他的UI布局。还有就是你的动画,执行次数过多。

分析了UI卡顿的原理之后,我们来进行第二个部分,UI卡顿的原因分析。

1.人为的在主线程中做了轻微的耗时操作,导致了UI线程的卡顿。

2.布局Layout过于复杂,无法在16ms内完成渲染。

3.同一时间,动画执行的次数过多,导致了CPU和GPU的负载过重。

4.View的过度绘制,导致某些像素在同一时间内被绘制了很多次,从而使CPU或GPU负载过重。

5.View频繁的触发measure,layout。导致measure,layout累计耗时过多,以及整个view频繁的重新渲染。

6.内存频繁触发GC过多,导致暂时阻塞渲染操作。

7.冗余资源以及逻辑等导致加载和执行缓慢。

UI卡顿的优化总结

1.布局优化

我们可以使用常见的include,mearge,viewstub标签。这在google提供给我们的官方文档上都有。

include就是直接使用布局嵌套来重用。mearge就是减少布局嵌套。

viewstub默认是一个不可见的,高度为0,只有在运行的时候才加载,也就是懒加载的组件。

viewstub默认是gone,高度为0,此时也不会绘制。需要显示的时候,需要调用setvisibility或者inflate才会加载。

使用:

    <ViewStub
        android:id="@+id/vs_test"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:inflatedId="@+id/vs_inflate"
        android:layout="@layout/viewstub_item" />

其中的layout:

<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/lv_test"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</ListView>

需要的时候才会加载:

        ViewStub viewStub = findViewById(R.id.vs_test);
        // 加载
        viewStub.setVisibility(View.VISIBLE);
        // 通过inflatedId获取viewstub_item里面的listView
        ListView listView = findViewById(R.id.vs_inflate);

总结下就是

1.尽量不要多层嵌套,如果layout比较复杂的时候,可以考虑使用自定义view来实现。

2.列表和adapter优化

在滑动的时候,不要进行元素的更新。

3.背景和图片等的内存分配优化

尽量减少不必要的背景图

图片尽量要压缩。

4.避免ANR

猜你喜欢

转载自blog.csdn.net/howlaa/article/details/128804993