Issue#84 Ensure BackgroundThread work is synced for DiffUtil
This commit is contained in:
parent
b8a044ac3d
commit
27f1276cc1
6 changed files with 111 additions and 1 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package org.fnives.test.showcase.ui.home
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.AsyncDifferConfig
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import org.fnives.test.showcase.R
|
||||
|
|
@ -8,13 +9,16 @@ import org.fnives.test.showcase.databinding.ItemFavouriteContentBinding
|
|||
import org.fnives.test.showcase.model.content.ContentId
|
||||
import org.fnives.test.showcase.model.content.FavouriteContent
|
||||
import org.fnives.test.showcase.ui.shared.ViewBindingAdapter
|
||||
import org.fnives.test.showcase.ui.shared.executor.AsyncTaskExecutor
|
||||
import org.fnives.test.showcase.ui.shared.layoutInflater
|
||||
import org.fnives.test.showcase.ui.shared.loadRoundedImage
|
||||
|
||||
class FavouriteContentAdapter(
|
||||
private val listener: OnFavouriteItemClicked,
|
||||
) : ListAdapter<FavouriteContent, ViewBindingAdapter<ItemFavouriteContentBinding>>(
|
||||
DiffUtilItemCallback()
|
||||
AsyncDifferConfig.Builder(DiffUtilItemCallback())
|
||||
.setBackgroundThreadExecutor(AsyncTaskExecutor.iOThreadExecutor)
|
||||
.build()
|
||||
) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewBindingAdapter<ItemFavouriteContentBinding> =
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package org.fnives.test.showcase.ui.shared.executor
|
||||
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
/**
|
||||
* Basic copy of [ArchTaskExecutor][androidx.arch.core.executor.ArchTaskExecutor], needed because that is restricted to Library.
|
||||
*
|
||||
* Intended to be used for [AsyncDifferConfig][androidx.recyclerview.widget.AsyncDifferConfig] so it can be synchronized with Espresso.
|
||||
*
|
||||
* Workaround until https://github.com/android/android-test/issues/382 is fixed finally.
|
||||
*/
|
||||
object AsyncTaskExecutor : TaskExecutor {
|
||||
|
||||
val mainThreadExecutor = Executor { command -> postToMainThread(command) }
|
||||
val iOThreadExecutor = Executor { command -> executeOnDiskIO(command) }
|
||||
|
||||
var delegate: TaskExecutor? = null
|
||||
private val defaultExecutor by lazy { DefaultTaskExecutor() }
|
||||
private val executor get() = delegate ?: defaultExecutor
|
||||
|
||||
override fun executeOnDiskIO(runnable: Runnable) {
|
||||
executor.executeOnDiskIO(runnable)
|
||||
}
|
||||
|
||||
override fun postToMainThread(runnable: Runnable) {
|
||||
executor.postToMainThread(runnable)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package org.fnives.test.showcase.ui.shared.executor
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
/**
|
||||
* Basic copy of [androidx.arch.core.executor.DefaultTaskExecutor], needed because that is restricted to Library.
|
||||
* With a Flavour of [androidx.recyclerview.widget.AsyncDifferConfig].
|
||||
* Used within [AsyncTaskExecutor].
|
||||
*
|
||||
* Intended to be used for AsyncDiffUtil so it can be synchronized with Espresso.
|
||||
*/
|
||||
class DefaultTaskExecutor : TaskExecutor {
|
||||
|
||||
private val diskIO = Executors.newFixedThreadPool(2)
|
||||
private val mMainHandler: Handler by lazy { createAsync(Looper.getMainLooper()) }
|
||||
|
||||
override fun executeOnDiskIO(runnable: Runnable) {
|
||||
diskIO.execute(runnable)
|
||||
}
|
||||
|
||||
override fun postToMainThread(runnable: Runnable) {
|
||||
mMainHandler.post(runnable)
|
||||
}
|
||||
|
||||
private fun createAsync(looper: Looper): Handler =
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
Handler.createAsync(looper)
|
||||
} else {
|
||||
Handler(looper)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package org.fnives.test.showcase.ui.shared.executor
|
||||
|
||||
/**
|
||||
* Define TaskExecutor intended for [AsyncDifferConfig][androidx.recyclerview.widget.AsyncDifferConfig]
|
||||
*/
|
||||
interface TaskExecutor {
|
||||
fun executeOnDiskIO(runnable: Runnable)
|
||||
|
||||
fun postToMainThread(runnable: Runnable)
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package org.fnives.test.showcase.testutils.idling
|
||||
|
||||
import org.fnives.test.showcase.ui.shared.executor.AsyncTaskExecutor
|
||||
import org.fnives.test.showcase.ui.shared.executor.TaskExecutor
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runners.model.Statement
|
||||
|
||||
/**
|
||||
* Similar Test Rule to InstantTaskExecutorRule just for the [AsyncTaskExecutor] to make AsyncDiffUtil synchronized.
|
||||
*/
|
||||
class AsyncDiffUtilInstantTestRule : TestRule {
|
||||
override fun apply(base: Statement, description: Description): Statement =
|
||||
object : Statement() {
|
||||
@Throws(Throwable::class)
|
||||
override fun evaluate() {
|
||||
AsyncTaskExecutor.delegate = object : TaskExecutor {
|
||||
override fun executeOnDiskIO(runnable: Runnable) {
|
||||
runnable.run()
|
||||
}
|
||||
|
||||
override fun postToMainThread(runnable: Runnable) {
|
||||
runnable.run()
|
||||
}
|
||||
}
|
||||
|
||||
base.evaluate()
|
||||
|
||||
AsyncTaskExecutor.delegate = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import org.fnives.test.showcase.network.mockserver.ContentData
|
|||
import org.fnives.test.showcase.network.mockserver.scenario.content.ContentScenario
|
||||
import org.fnives.test.showcase.network.mockserver.scenario.refresh.RefreshTokenScenario
|
||||
import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule
|
||||
import org.fnives.test.showcase.testutils.idling.AsyncDiffUtilInstantTestRule
|
||||
import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule
|
||||
import org.fnives.test.showcase.testutils.idling.loopMainThreadFor
|
||||
import org.fnives.test.showcase.testutils.idling.loopMainThreadUntilIdleWithIdlingResources
|
||||
|
|
@ -37,6 +38,7 @@ class MainActivityInstrumentedTest : KoinTest {
|
|||
@JvmField
|
||||
val ruleOrder: RuleChain = RuleChain.outerRule(mockServerScenarioSetupTestRule)
|
||||
.around(mainDispatcherTestRule)
|
||||
.around(AsyncDiffUtilInstantTestRule())
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue