我们是采用paging+room的方式实现聊天对话界面的,有一种消息类型是互动动画,类似微信的掷骰子,骰子动画只执行一遍。遇到一个问题是,动画执行完后,滑动其它消息再滑回来,骰子动画还执行一遍。可是,明明执行完动画后,已经在表示骰子消息的数据结构中标示动画已经执行过了,理论上,再滑回来,骰子类型用的还是内存中的这个数据结构。
首先,得确认的一件事是,再滑回来,骰子消息类型用的数据结构和之前是不是一个数据结构,虽然,从认知上应该是一个才对,还是得实际确认一下。所以,我就是打印了这个数据结构对象。
这个数据结构ConversationModel对象的“编号”是e6881edf,动画执行完后,调用了updateAnimationState方法将这个对象标示为动画执行过了。再滑回来后看编号还是这个对象啊,怎么标示是没有执行过动画呢?
大晚上的改bug,都没时间陪家人和孩子,搞的我很烦躁。
内心挣扎中,总有个疑问,这个编号是怎么来的?能表示就是一个对象吗?
查资料知道,直接打印对象,其实就是打印了对象.toString
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
也就是说“编号”其实就是hashCode转成16进制的字符串。那么我们看看ConversationModel的hashCode是啥
@Override
public int hashCode() {
return localId.hashCode();
}
hashCode进行了重写,那么会不会是hashCode一样,但其实是两个不同对象呢?
为了验证这个想法,先把hasdCode重写注释掉,再运行看看,结果确如所料,再滑回来变成了不同的对象。
那么现在的问题是?怎么会变成不同的对象呢?
仔细分析发现,是因为数据加载了两遍,room数据发生变化的时候,数据就会重新加载。在paging中,为了提高消息,paging机制中使用了diff机制,通过diff机制发现,第二次的数据加载其实就是在0位置插入了一条数据,看上面的log,其它都没有变化,UI也没必要刷新。
注意,重点来了,UI没必要刷新,持有了ConversationModel对象是之前的对象,但是,第二次加载的数据已经替换掉了之前的数据列表。虽然,动画执行完我们给ConversationModel对象设置了动画执行完的标示,但是这个是老对象,再滑回来后,重新从数据列表中取对象,是新对象,是没有设置过动画执行完标示的,所以动画还得再执行。最终破案了
个人感觉这个diff机制是有问题,虽然能够通过比对找到不同减少UI的刷新,但是UI持有的数据和数据列表中的数据会有不一致的问题。如果不注意就会导致很奇怪的问题。