ListView卡顿的原因以及优化策略
这道题想考察什么?
- 是否了解ListView卡顿的原因以及优化策略与真实场景使用,是否熟悉ListView卡顿的原因以及优化策略在工作中的表现是什么?
考察的知识点
- ListView卡顿的原因以及优化策略的概念在项目中使用与基本知识
考生应该如何回答
导致ListView卡顿的原因有很多,主要包括:Item没有复用、层级过深、数据绑定逻辑过多、滑动时不必要的图片刷新以及频繁的notifyDataSetChanged。
Item没有复用
ListView的Item没有复用是导致卡顿的常见原因。在滑动的过程中,有些Item离开屏幕,有些Item需要进入屏幕。离开屏幕的Item一般会加入到缓存容器中,而不是让item直接被GC的回收。如果有缓存,那么滑动进入屏幕的Item会优先从缓存容器中读取。读取到的缓存会通过给convertView赋值来更新UI。如果没有课复用的item,那么之后每次都会重新创建这些Item,也就是通过LayoutInflater进行Item的创建,LayoutInflater创建Item是采用反射去解析xml因此是比较耗费时间的,这就会带来性能损耗。
布局的层级过深
布局的层级过深是非常容易引起卡顿,原因是ViewGroup会对子View进行多次测量。假设有一个这样的场景:父布局的布局属性是wrap_content、子布局是match_parent,此时的布局过程是:
- 父布局先以0为强制宽度测量子View、然后继续测量剩下的其他子View
- 再用其他子View里最宽的宽度,二次测量这个match_parent的子 View,最终得出它的尺寸,并把这个宽度作为自己最终的宽度。
即 这个过程就对单个子View进行了二次测量。
「而布局嵌套对性能影响则是指数形式的」,即:父布局会对每个子view做两次测量,子view也会对下面的子view进行两次测量,即相当于是 O(2ⁿ)测量。
总的来说,层级过深会导致多次测量,不必要的测量造成了多余的性能消耗,最终有可能会引起页面的卡顿。
数据绑定逻辑过多
数据绑定逻辑过多也容易导致卡顿。在适配器中,我们通过onCreateViewHolder创建Item,创建之后的Item要通过onBindViewHolder进行数据绑定。过程大致如下:先通过position找到数据源中对应的数据,然后再将数据设置到控件的属性中。简单的设置以及一些简单的计算是不会消耗很多的时间的,如果是包含大量的控件属性设置、数据遍历、转化的话时间就会成倍的增长,最终可能就会导致ListView的卡顿。
滑动时不必要的图片刷新
图片的刷新也有可能导致卡顿。如果Item中包含图片并且我们以非常快的速度滑动列表,那么代表着有Item快速离开屏幕,也有Item快速的进入屏幕。快速进入屏幕的Item必须以极短的时间完成数据绑定和图片加载刷新,但图片的加载是需要耗费较大的性能和时间的,频繁的图片加载会延缓Item的数据绑定过程,容易造成卡顿。
频繁的notifyDataSetChanged
频繁的notifyDataSetChanged也是导致ListView卡顿的一大原因。notifyDataSetChanged是刷新ListView的所有Item的界面上展示的数据,所有的Item都刷新数据肯定是十分消耗性能的,如果是频繁的notifyDataSetChanged,那带来的性能消耗肯定更加严重,最终非常有可能导致到卡顿。
解决办法
Item没有复用,这个问题的解决就是加上复用代码,让item的界面和展示的数据可以得以复用,减少之后创建新的item和绑定数据的次数。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
ListViewItem itemData = items.get(position);
if(convertView == null){
convertView = View.inflate(context, R.layout.list_item_layout, null);
holder = new ViewHolder();
holder.userImg = (ImageView) convertView.findViewById(R.id.user_header_img);
holder.userName = (TextView) convertView.findViewById(R.id.user_name);
holder.userComment = (TextView) convertView.findViewById(R.id.user_coomment);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.userImg.setImageResource(itemData.getUserImg());
holder.userName.setText(itemData.getUserName());
holder.userComment.setText(itemData.getUserComment());
return convertView;
}
static class ViewHolder{
ImageView userImg;
TextView userName;
TextView userComment;
}
如上面代码所示,可以先对convertView进行判断,如果为空再通过LayoutInflater创建新的Item,如果不为空则可以复用。然后通过ViewHolder进一步优化,不用ViewHolder的情况下每次设置Item的值都会需要通过convertView.findViewById寻找相关的控件,不方便且耗时,如果在创建Item时将各类控件引用设置到ViewHolder中,以后就可以直接从ViewHolder找控件,是十分节约性能的。
布局的层级过深的解决方式是减少层级,尽量使用约束布局ConstraintLayout。约束布局ConstraintLayout是Google 参考了ios的约束布局创建的,他的功能相当于 RelativeLayout + LinearLayout,而性能相对于他们提升了40%左右,所以通过使用ConstraintLayout可以达到优化性能的目的。
数据绑定逻辑过多的话,首先是区分出与界面有关的逻辑计算和与界面无关的逻辑计算,与界面有关的逻辑计算只能通过优化算法逻辑、数据结构处理,与界面无关的逻辑计算不影响界面的显示可以开线程用于计算。
滑动时不必要的图片刷新这个解决办法就十分明确,如果快速滑动带来了很多不必要的刷新,那么可以在快速滑动的时候设置为不刷新图片,这样可以减少非常多不必要的性能消耗。
频繁的notifyDataSetChanged是容易导致卡顿的,这种情况可以优化代码逻辑,减少频繁的notifyDataSetChanged。
详细关注公众号:Android老皮
还能解锁 《Android十大板块文档》 ,让学习更贴近未来实战。已形成PDF版
内容如下:
1.Android车载应用开发系统学习指南(附项目实战)
2.Android Framework学习指南,助力成为系统级开发高手
3.2023最新Android中高级面试题汇总+解析,告别零offer
4.企业级Android音视频开发学习路线+项目实战(附源码)
5.Android Jetpack从入门到精通,构建高质量UI界面
6.Flutter技术解析与实战,跨平台首要之选
7.Kotlin从入门到实战,全方面提升架构基础
8.高级Android插件化与组件化(含实战教程和源码)
9.Android 性能优化实战+360°全方面性能调优
10.Android零基础入门到精通,高手进阶之路