最近,我在我的应用程序中发现了奇怪的崩溃。我发现它们是由下面的ListAdapter
-> DiffUtil
引起的。合同规定,仅当areContentsTheSame
为相应的项目返回true时,才会调用areItemsTheSame
回调。问题是对于从未调用过areContentsTheSame
的项目调用了areItemsTheSame
。
我正在String
个项目上对其进行测试,因此它不应与自己的回收器实施相关。如果是我的错(现在几乎没有逻辑)或DiffUtil
工具中的错误,我真的很困惑
我已经创建了一个简单的仪器测试,该测试在上述情况下失败了-更有经验的人可以看看吗:
package com.example.diffutilbug import android.util.Log import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import junit.framework.Assert.assertTrue import kotlinx.coroutines.* import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.BlockJUnit4ClassRunner @RunWith(BlockJUnit4ClassRunner::class) internal class ExampleUnitTest { @Test fun testDiffUtil4() { val handler = CoroutineExceptionHandler { _, exception -> throw exception } // adapter compare items : // areItemsTheSame -> compare length of String // areContentsTheSame -> compare content with == val adapter = StringAdapterJunit(handler) runBlocking { adapter.submitList( mutableListOf<String>( "1",//1, "22",//2, "333",//3, "4444",//4, "55555",//5, "666666",//6, "7777777",//7, "88888888",//8, "999999999",//9, "55555",//5, "1010101010",//10, "1010109999",//10, "55555",//5, "1313131313",//10, "1414141414",//10, "55555",//5, "1313131313",//10, "1414141414",//10, "55555"//5 ) ) delay(40) adapter.submitList( mutableListOf<String>( "55555",//5, "1010101010",//10, "1010109999",//10, "55555",//11, "1313131313",//10, "1414141414",//10, "11111111111"//11 ) ) delay(500) } } } // Stub Adapter for Strings that uses DiffUtil underneath. // logs all callbacks to logcat class StringAdapterJunit(val handler: CoroutineExceptionHandler) : ListAdapter<String, RecyclerView.ViewHolder>(object : DiffUtil.ItemCallback<String>() { override fun areItemsTheSame(oldItem: String, newItem: String): Boolean { Log.e("DiffUtilTest", "areItemsTheSame comparing $oldItem with $newItem = ${oldItem.length == newItem.length}") return oldItem.length == newItem.length } override fun areContentsTheSame(oldItem: String, newItem: String): Boolean { //should be called only if areContentsTheSame == true Log.e( "DiffUtilTest", "areContentsTheSame error = ${oldItem.length != newItem.length} comparing $oldItem with $newItem" ) runBlocking { GlobalScope.launch(handler + Dispatchers.Main) { assertTrue("areContentsTheSame can be called only if areItemsTheSame return true" , areItemsTheSame(oldItem, newItem)) }.join() } return oldItem == newItem } override fun getChangePayload(oldItem: String, newItem: String): Any? { //should be called only if areItemsTheSame = true and areContentsTheSame = false Log.e( "DiffUtilTest", "getChangePayload error = ${oldItem.length == newItem.length && oldItem == newItem} $oldItem with $newItem" ) return null } }) { // stub implementation on adapter - never used override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = object : RecyclerView.ViewHolder(View(null)) {} override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {} override fun getItemViewType(position: Int): Int = getItem(position).length }
和需要的gradle依赖项:
dependencies { implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' androidTestImplementation 'androidx.test.ext:junit:1.1.0' //coroutines implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.1' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1' implementation 'androidx.recyclerview:recyclerview:1.0.0' }
请注意,您需要添加
android.useAndroidX=true android.enableJetifier=true
在您的
gradle.properties
中
为异常添加协程和处理程序,因为DiffUtil
在后台线程上计算diff,JUnit
仅在主线程上处理断言
最近,我在我的应用程序中发现了奇怪的崩溃。我发现它们是由下面的ListAdapter-> DiffUtil引起的。合同规定只有在...
我收到Google的回复,当列表包含重复项(空值,相同对象等)时,他们确认DiffUtil
中存在错误]