Android Listadapter Submitlist Caveats

January 31, 2020
submitList and currentList concurrency issues

When using [ListAdapter], ListAdapter.submitList actually launch a background process to perform comparison before updating the actual ListAdapter.currentList. If you rely on currentList to perform update (e.g. check if an item exist or not before perform actual action), you need to make sure you get the latest currentList.

Option 1: commitCallback

adapter.submitList(items) {
    // this is the callback after the background task is completed
    // currentList should be latest
}

The commit callback can be used to know when the List is committed, but note that it may not be executed. If List B is submitted immediately after List A, and is committed directly, the callback associated with List A will not be run. - Source

As you can see, if you call submitList immediately after submitList, the callback might not be called.

Option 2: Mutex

I use Kotlin coroutines Mutex to make sure submitList should only be called after the prior update is committed.

In the following example, I have a fruit ListAdapter. Oranges will always list/appear before Apples (so I check currentList, prepend if orange, append if apple)

class FruitAdapter : ListAdapter<Fruit, LocalAdapter.ViewHolder>(DiffCallback()) {
    private val mutex = Mutex()


    suspend fun submitOranges(items: List<Fruit>) {
        mutex.lock()

        // clone list
        val newItems = currentList.toMutableList()
        // prepend to make sure oranges always appear first
        newItems.addAll(0, items)


        submitList(newItems) {
            mutex.unlock()
        }
    }

    suspend fun submitApples(items: List<Fruit>) {
        mutex.lock()

        // clone list
        val newItems = currentList.toMutableList()
        // append to make sure apples always appear last
        newItems.addAll(items)

        submitList(newItems) {
            mutex.unlock()
        }
    }
}

With the mutex lock

  • submitOranges and submitApples cannot be override each other’s execution, thus commitCallback shall always be called.
  • submitOranges and submitApples shall always have access to the latest currentList

NOTE: If for some unepexted circumstances commitCallback is not called, then both methods shall be permanently locked.

With this implementaion, you should not call submitList directly, else it shall make the mutex implementation useless.

Use Mutex instead of ReentrantLock, as Mutex perform lock even when executed on the same thread.

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.