继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

ViewPager卡顿优化实战

米脂
关注TA
已关注
手记 492
粉丝 88
获赞 591

应用UI卡顿常见原因主要在以下几个方面:

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

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

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

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

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

  6. 内存频繁触发GC过多(同一帧中频繁创建内存),导致暂时阻塞渲染操作;

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

发现和定位问题,及解决方案

打开GPU绘制,手指在卡片上来回滑来滑去,通过观察高线的位置判断卡顿的时机,我们发现滑动停止后再一次滑动时出现高峰,如图,按照经验ViewPager的卡顿问题在于滑动事件的回调,重点排查onPageScrolledonPageSelectedAdapterinstantiateItem方法。

5bacf01400016f6410801920.jpg

为了更精确的定位耗时方法,可以使用TraceView。打开DDMS,进入下面界面

点击红点位置开始录制,滑动手机屏幕,一段时间后再点击红点停止录制。分析下面的图

可以看到耗时最多的两个方法,RoundAngleImageView的draw方法和CardAdapter的instantiateItem方法。我们通过打log也应证了TraceView的判断,ViewPager滑动过程中RoundAngleImageView会不断绘制,在滑动停顿后又开始滑动得时候出现耗时峰值。优化的方案就是用RoundedImageView替换掉自定义的RoundAngleImageView,RoundedImageView使用BitmapShader方式优于RoundAngleImageView使用的PorterDuffXfermode圆角方案。特别在不断绘制的情况下,效果较明显。

接下来分析CardAdapter的instantiateItem方法,同样先看log,定位更细的方法

通过日志看出val view = LayoutInflater.from(container.context).inflate(R.layout.``item_lesson_card``, container, false) as LessonCardViewinitLessonCardView(position, view, cardList)两个方法。

inflate方法可做的优化就是减少View层级,把最外层的RelativeLayout替换成LinearLayout。另外一个优化就是把 initLessonCardView方法加入延时,对于当前显示的item延时为0,不可见的item延时时间与当前View的位置步距依次延长。通过分步延时避免UI线程一直被占用。

下面来跟踪initLessonCardView方法

通过日志发现耗时最大的在于View的构造函数。

val exerciseItemView: ExerciseItemView = ExerciseItemView(context)

尝试把View的创建方法改成inflate方式,事实证明然并卵。

val exerciseItemView: ExerciseItemView = View.inflate(context, R.layout.view_exercise_item, null) as ExerciseItemView

优化后的图

左边是优化后的图,右边是某竞品的图,比较下很明显看出渲染的差异来

5bacf01b0001f61e10801920.jpg
5bacf01c0001818010801920.jpg

总结

优化到此为止,总结一下View的优化套路。首先发现问题,通过GPU柱状图判断卡顿程度。然后通过TraceView定位卡顿的方法,打log方式找到更具体的耗时细节,然后逐个优化。

本次ViewPager的优化包括:

  • RoundedImageView控件的选择,选择更高效的圆角方案

  • 优化view的层次结构,尽量减少层级,同样层级情况使用LinearLayout优于RelativeLayout

  • adapter instantiateItem分布延迟渲染

Tips

ViewPager里包含ListView时,layout_height属性为wrap_content会导致adapter getView不断被调用,建议改成match_content

<ListView 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" />

原文链接:http://www.apkbus.com/blog-705730-62433.html

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP

相关阅读

UI卡顿解决