Android性能优化系列——UI优化

作为大部分的Android开发来说,很多时候都在拉界面,写业务,做优化。

在写我们的界面和业务的时候,UI的优化便会显得至关重要。因为当你的UI代码写得很垃圾的时候,就会造成卡顿,丢帧等现象(造成卡顿的原因很多,UI只是其中一中)。

Android 应用的卡顿、丢帧等,这些影响用户体验的因素绝大部分都与16ms这个值有关。Android 设备的刷新率也是 60Hz,Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,如果超过了16ms,我们则认为发生了卡顿。

而Android端显示原理用一句话描述就是:Android应用程序调用SurfaceFlinger服务把经过测量、布局和绘制后的Surface渲染到显示屏幕上。

一、优化方向

1、布局代码

在我们的实际开发中,布局代码很多时候是通过xml和一些自定义控件来实现的。

那么在我们写布局代码XML的时候,我们可以对一些方向进行优化

  • 避免过度绘制,减少布局层级
    • 有些时候,一个界面控件过多,如果只用线性布局和相对布局,会嵌套很多层级来实现绘制,这样绘制层级过高,可以用相对布局来约束各个控件的位置,减少层级。如果是简单的布局是否可以直接使用FrameLayout、LinearLayout来实现等等。
  • 去掉无用的背景,资源;
    • 过度绘制就是在绘制界面时,对同一个像素重复绘制了多次,但是用户能够看到的也只有最顶层绘制的内容,所以,是否可以去掉无用的背景,图片是否可以不要预设的图片资源,textview的文案是否可以先不设置。去除无用的父节点,子节点
  • 避免View重叠;

    • 使用 canvas.clipRect()方法实现只截取可见的部分,这样就能避免View重叠导致的过度绘制的问题 ,例如显示一把扑克牌的时候,因为只需要展示左边的数字就行了,所以使用此方法,不可见的部分就不会去绘制。

  • 使用合适的控件;
    • 在项目中,可以根据项目场景使用合适的控件,例如
      • ViewStub是一个可用于性能优化的控件,它是一个不可见的、零尺寸的View,可以在运行时进行延迟加载一个布局文件,从而提高显示速率。

        viewstub和include比较像,都是在一个布局文件中嵌入另外一个布局文件,然而viewstub可以延迟加载,它只会在手动指定加载的时候才会加载这个布局文件,而include则会立即加载。

        因此,不是必须显示的布局就可以使用ViewStub来代替,这样可以减少界面首次加载时资源消耗,提升最初加载速度。比如在无数据或者网络错误的时候,需要单独显示一个布局,那么这个布局就可以用ViewStub。、

      • 使用merge;通过merge可以消除视图层次结构的冗余;merge标签必须使用在根布局;tips:对merge标签设置的属性是无效的。

  • 使用一些异步加载框架;

避免View重叠实现代码;

// 老办法
protected void onDraw(Canvas canvas) {
	super.onDraw(canvas);
	if (mDroids.length > 0 && mDroids.size() == mDroids.length) {
		for (int i = 0; i < mDroidCards.size(); i++) {
			// 每一张卡牌平移的位置
			mCardLeft = i * mCardSpacing;
			// 绘制整张卡牌 
			drawDroidCard(canvas, mDroidCards.get(i), mCardLeft, 0);
		}
	}
}

// 优化后
protected void onDraw(Canvas canvas) {
	super.onDraw(canvas);
	if (mDroids.length > 0 && mDroids.size() == mDroids.length) {
		int i;
		for (int i = 0; i < mDroidCards.size() - 1; i++) {
			mCardLeft = i * mCardSpacing;

			canvas.save();
			// 只绘制截取卡牌可见部分
			canvas.clipRect(mCardLeft, 
							0, 
							mCardLeft + mCardSpacing, 	
							mDroidCards.get(i).getHeight());
			drawDroidCard(canvas, mDroidCards.get(i), mCardLeft, 0);
			canvas.restore();
		}
		// 绘制最后一张完全可见的卡牌
		drawDroidCard(canvas, mDroidCards.get(mDroidCards.size() - 1),
			mCardLeft + mCardSpacing, 0);
	}
}

如何查看是否过度绘制?

  • 通过数据线开发者模式,使用Android端的开发者设置-调试GPU过度绘制,选择显示过度绘制区域,然后就可以看到屏幕的绘制情况了,其中我们能够看到四种颜色,分别是:蓝色、绿色、淡红色和红色。
  • 使用LayoutInspector;LayoutInspector是AndroidStudio种的一个布局检查器,可以通过Tools > Layout Inspector找到,他可以检查应用中的某个界面的视图结构,但是无法查看非调式状态的应用。
  • 使用Android优化工具Systrace 这个使用复杂些,但是更全面具体,具体使用看链接文章 Android优化工具Systrace - 简书

 2、自定义View代码

        自定义View在开发中十分常见,当需要实现一个较为复杂的view时,就很容易漏掉很多细节。

常见问题:

  1. onDraw方法中过于耗时;
  2. onDraw方法中频繁的GC;例如我有次就很傻,会把paint和path对象的初始化放在onDraw中,这样是非常不合理的,每次绘制都会创建新的对象。可以直接定义在class类里,需要重置的时候reset一下就行了。
  3. 动画执行过多,选择合适的动画很重要,当一个动画过于复杂且花里胡哨的时候,叫UI做个lottie动画拿来用就好了。
  4. 是否在UI主线程执行了耗时操作。
  5. 有内存泄漏;如果代码里有静态变量,是不是该置空,动画在ondestroy调用时是不是该暂停销毁,资源是否关闭回收等等。

猜你喜欢

转载自blog.csdn.net/LoveFHM/article/details/135438515