recyclerview优化:DiffUtil使用过程中遇到的问题总结-kotlin中==未比较equals的问题分析

https://blog.csdn.net/fitaotao/article/details/84314043

使用DiffUtil:DiffUtil是Android Support Library中的一个工具类,可以帮助计算新旧数据集的差异并高效更新RecyclerView的数据。通过使用DiffUtil,可以避免不必要的数据刷新和界面重绘,提高列表更新的效率。

fun <T> RecyclerView.Adapter<*>.autoNotify(old: List<T>, new: List<T>, compare: (T, T) -> Boolean) {
    val diff = DiffUtil.calculateDiff(object : DiffUtil.Callback() {

        override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return compare(old[oldItemPosition], new[newItemPosition])
        }

        override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
            return old[oldItemPosition] == new[newItemPosition]
        }

        override fun getOldListSize() = old.size

        override fun getNewListSize() = new.size
    })

    diff.dispatchUpdatesTo(this)
}
    fun setData(list: List<MessageInfo>) {
        autoNotify(messageList, list) { old, new ->
            old.id == new.id
        }
        messageList.clear()
        messageList.addAll(list)
//        notifyItemRangeChanged(0, messageList.size)
    }

在使用的过程中发现areContentsTheSame 中的old[oldItemPosition] == new[newItemPosition]这个比较,当其中某个字段发生变化后,比较结果仍为true。期望肯定是字段如发生变化,则需要返回false,更新这个Item。

分析出现上述问题的原因:
问题1:项目代码中,在adapter使用的list和外部list是同一个数据源,这就导致在比较之前,old和new的值就已经相同了。
问题2:使用==比较。在Kotlin中,这种比较方式默认调用的是equals方法进行比较,理想状态是对每个字段进行逐一比较,但是,实际情况并非如此,具体造成这个问题的原因在下文分解。

分析问题:
问题1:
这个问题其实按照上面的描述,其实就是自己在跟自己比较,所以恒为true。所以在adapter中,给adapter中的List进行赋值的时候,要注意使用不同的对象。利用bean的clone,给list进行一次重新赋值。
代码如下:

public static class MessageInfo implements Serializable , Cloneable{
	      @Override
        public MessageInfo clone() {
            MessageInfo o = null;
            try{
                o = (MessageInfo)super.clone();
            }catch(CloneNotSupportedException e){
                e.printStackTrace();
            }
            return o;
        }
}

问题2:
这个问题就比较奇怪了,根据对kotlin的理解,==比较就是调用equals进行比较,并且写过测试代码,当字段变化时,两个对象进行比较返回的是false。

    data class Personal(val id:String, val name:String)
    @Test
    fun testEquals(){
        val personal1 = Personal("1", "dy")
        val personal2 = Personal("1", "dy")
        println("$personal1 , $personal2")
        val personal3 = Personal("1", "dy1")
        val personal4 = personal2

        assertEquals(true,  personal1 == personal2 )
        assertEquals(false,  personal1 === personal2 )
        assertEquals(false,  personal1 === personal3 )
        assertEquals(false,  personal1 == personal3 )
        assertEquals(true,  personal4 == personal1 )
        assertEquals(false,  personal4 === personal1)

    }

上述用例中,针对 kotlin 中的== === 进行了测试比较

其实这个问题的原因是项目中,虽然在调用处使用的是kotlin,但是bean文件是用Java创建的,这其实就涉及到一个java和kotlin混合使用的问题。那么问题究竟是什么呢?
我发现项目中的bean文件是java文件,而我的测试用例中,使用的是kotlin的data。
进一步验证问题,查看测试用例编译后生成的java代码:

public final class Personal {
   @NotNull
   private final String id;
   @NotNull
   private final String name;

   @NotNull
   public final String getId() {
      return this.id;
   }

   @NotNull
   public final String getName() {
      return this.name;
   }

   public Personal(@NotNull String id, @NotNull String name) {
      Intrinsics.checkNotNullParameter(id, "id");
      Intrinsics.checkNotNullParameter(name, "name");
      super();
      this.id = id;
      this.name = name;
   }

   @NotNull
   public final String component1() {
      return this.id;
   }

   @NotNull
   public final String component2() {
      return this.name;
   }

   @NotNull
   public final Personal copy(@NotNull String id, @NotNull String name) {
      Intrinsics.checkNotNullParameter(id, "id");
      Intrinsics.checkNotNullParameter(name, "name");
      return new Personal(id, name);
   }

   // $FF: synthetic method
   public static Personal copy$default(Personal var0, String var1, String var2, int var3, Object var4) {
      if ((var3 & 1) != 0) {
         var1 = var0.id;
      }

      if ((var3 & 2) != 0) {
         var2 = var0.name;
      }

      return var0.copy(var1, var2);
   }

   @NotNull
   public String toString() {
      return "Personal(id=" + this.id + ", name=" + this.name + ")";
   }

   public int hashCode() {
      String var10000 = this.id;
      int var1 = (var10000 != null ? var10000.hashCode() : 0) * 31;
      String var10001 = this.name;
      return var1 + (var10001 != null ? var10001.hashCode() : 0);
   }

   public boolean equals(@Nullable Object var1) {
      if (this != var1) {
         if (var1 instanceof Personal) {
            Personal var2 = (Personal)var1;
            if (Intrinsics.areEqual(this.id, var2.id) && Intrinsics.areEqual(this.name, var2.name)) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }
   }
}

通过上述代码发现了核心问题,在转为java代码的时候,data会自动生成equals比较方法。而我项目中自定义的java bean没有实现equals方法,这就导致在判断调用的时候,调用的是any默认的equals方法,仅会对引用进行比较,而不会对字段进行逐一的比较。。所以解决问的方案也一目了然,自己重写一下equals方法。另外要注意需要同时重写hashcode方法。

在Java中,当我们重写 equals() 方法时,通常也需要重写 hashCode() 方法。这是因为Java的 Object 类规定,如果两个对象相等(即 equals() 方法返回 true ),那么它们的 hashCode() 方法必须返回相同的值。如果你只重写了 equals() 方法而没有重写 hashCode() 方法,那么可能会违反这个规定,导致哈希表等数据结构的行为异常。

简单省事的解决方法就是直接用kotlin的bean, 并注意使用data class

滑动冲突:
http://wed.xjx100.cn/news/231642.html
https://blog.csdn.net/xiaokangss/article/details/128002513

混淆:
http://www.taodudu.cc/news/show-1258028.html?action=onClick
https://www.ngui.cc/el/1833760.html?action=onClick

你可能感兴趣的:(kotlin,开发语言,android)