布景
脱离事务场景讲技能计划都是耍流氓
最近接手了IM的事务,一上来就来了几个大需求,搞得有点手忙脚乱。在做需求的过程中发现,咱们的会话列表(RecyclerView)竟然每次更新都是notifyDataSetChanged(),因为IM的改写频率是十分高的
咱们能够想象一下微信会话列表,每来1条消息,就大局调用notifyDataSetChanged。
这儿瞎猜一下,或许由于历史原因,之前设计的同学也是不得已而为之。已然发现了这个问题,那么咱们如何来进行优化呢?
IM列表跟普通列表的差异
- 有序性:列表中的Item按时刻排序,或许其他规矩(置顶也是修正时刻完结)
- 仅有性:每个会话都是仅有的,不存在重复
- 单item更新频率高:能够参考微信的会话列表
DiffUtil
首要想到的是DiffUtil,它用来比较两个数据集,寻找出旧数据集->新数据集的最小改变量
完结思路:
- 获取原始会话数据,进行排序,去重操作
- 选用DiffUtil主动计算新老数据集差异,主动完结定向改写
这儿只摘取DiffUtil关键使用部分,至于高档用法和更高档的用法不再赘述
class DiffMsgCallBack: DiffUtil.Callback() {
private val oldData: MutableList<MsgItem> = mutableListOf()
private val newData: MutableList<MsgItem> = mutableListOf()
//老数据集size
override fun getOldListSize(): Int {
return oldData.size
}
//新数据集size
override fun getNewListSize(): Int {
return newData.size
}
/**
* 比较的是position,被DiffUtil调用,用来判断两个目标是否是相同的Item
* 例如,如果你的Item有仅有的id字段,这个办法就 判断id是否持平
*/
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldData[oldItemPosition].id == newData[newItemPosition].id
}
/**
* 用来查看 两个item是否含有相同的数据,当前item的内容是否发生了改变,这个办法仅仅在areItemsTheSame()返回true时,才调用
*/
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
if (oldData[oldItemPosition].id != newData[newItemPosition].id){
return false
}
if (oldData[oldItemPosition].content != newData[newItemPosition].content){
return false
}
if (oldData[oldItemPosition].time != newData[newItemPosition].time){
return false
}
return true
}
/**
* 高档用法:完结部分(partial)绑定的办法,需求配合onBindViewHolder的3个参数的办法
* 更高档用法:AsyncListDiffer+ListAdapter
*
*/
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
return super.getChangePayload(oldItemPosition, newItemPosition)
}
SortedList
当我认为DiffUtil
已经能够满意需求的时分,无意间又发现了一个SortedList
。
SortedList是一个有序列表(数据集)的完结,能够保持ItemData都是有序的,并(主动)告诉列表(RecyclerView)(数据集)中的更改。
调配RecyclerView使用,去重,有序,主动定向改写
这儿也只摘取关键使用部分,具体用法不再详解
class SortListCallBack(adapter: RecyclerView.Adapter<*>?) : SortedListAdapterCallback<MsgItem>(adapter) {
/**
* 排序条件,完结排序的逻辑
*/
override fun compare(o1: MsgItem?, o2: MsgItem?): Int {
o1 ?: return -1
o2 ?: return -1
return o1.time - o2.time
}
/**
* 和DiffUtil办法共同,用来判断 两个目标是否是相同的Item。
*/
override fun areItemsTheSame(item1: MsgItem?, item2: MsgItem?): Boolean {
return item1?.id == item2?.id
}
/**
* 和DiffUtil办法共同,返回false,代表Item内容改变。会回调mCallback.onChanged()办法;
* 相同:areContentsTheSame+areItemsTheSame
*/
override fun areContentsTheSame(oldItem: MsgItem?, newItem: MsgItem?): Boolean {
if (oldItem?.id != newItem?.id){
return false
}
if (oldItem?.content != newItem?.content){
return false
}
if (oldItem?.time != newItem?.time){
return false
}
return true
}
/**
* 高档用法:完结部分绑定的办法,需求配合onBindViewHolder的3个参数的办法
*/
override fun getChangePayload(item1: MsgItem?, item2: MsgItem?): Any? {
return super.getChangePayload(item1, item2)
}
}
对比
DiffUtil和SortedList是十分类似的,修正过数据后,内部持有的回调接口都是同一个:androidx.recyclerview.widget.ListUpdateCallback
/**
* An interface that can receive Update operations that are applied to a list.
* <p>
* This class can be used together with DiffUtil to detect changes between two lists.
*/
public interface ListUpdateCallback {
void onInserted(int position, int count);
void onRemoved(int position, int count);
void onMoved(int fromPosition, int toPosition);
void onChanged(int position, int count, @Nullable Object payload);
DiffUtil计算出Diff或许SortedList察觉出数据集有改变后,会回调ListUpdateCallback接口的这四个办法,DiffUtil和SortedList供给的默认Callback完结中,都会告诉Adapter完结定向改写。
这便是主动定向改写的原理
总结
- DiffUtil比较两个数据源(一般是List)的差异(Diff),Callback中比对时传递的参数是 position
- SortedList能完结数据集的排序和去重,Callback中比对时,传递的参数是ItemData
- 都能完结主动定向改写 + 部分绑定,一种主动定向改写的手法
- DiffUtil: 检测不出重复的,会被认为是新增的
- DiffUtil高档用法支撑子线程中处理数据,而SortList不支撑
抱负与现实
2种计划都有了,是不是能够进行IM会话列表的优化了呢,答案是不能
- 事务需求迭代,牵一发而动全身
- 祖传代码,无人敢动,更甭说优化了
有时分咱们写代码会想着后面再优化一下,但是许多时分都不会给你优化的机会,除非严重需求变动,所以一开始设计结构的时分就要结合事务场景尽量设计的愈加合理
参考文章:blog.csdn.net/zxt0601/art…