Status Update
Comments
yb...@google.com <yb...@google.com> #2
ti...@gmail.com <ti...@gmail.com> #3
yb...@google.com <yb...@google.com> #4
private void refreshAllLiveData() {
AppDataBase YOUR_DATABASE_WHICH_YOU_BUILD = .....
SupportSQLiteDatabase writableDatabase = YOUR_DATABASE_WHICH_YOU_BUILD.getOpenHelper().getWritableDatabase();
//get the database count;
Cursor cursor = writableDatabase.query("SELECT count(*) FROM sqlite_master WHERE type = 'table' AND name != 'android_metadata' AND name != 'room_master_table';");
int tableCount = 0;
while(cursor.moveToNext()) {
tableCount = cursor.getInt(0);
}
for (int i = 0; i < tableCount; i++) {
//update version table with the incremented count because room modification log stores tables with ids instead of names
writableDatabase.execSQL("INSERT OR REPLACE INTO room_table_modification_log VALUES(null, "+(i)+")");
}
YOUR_DATABASE_WHICH_YOU_BUILD.getInvalidationTracker().refreshVersionsAsync();
}
-----
This is a workaroud for refreshing all live datas, I still prefer to use a proper API you implemented.
Thanx
ti...@gmail.com <ti...@gmail.com> #5
yb...@google.com <yb...@google.com> #6
In fact, the current implementation (without my fixes) doesn't honor the rule that says that on multiple submittions only the last one should be committed.
Is this only when called from different thread or even when called from the main thread? I might be missing something but I'm not sure how that will happen unless this method is called from multiple threads (which is not supported).
ti...@gmail.com <ti...@gmail.com> #7
Even when called from the main thread. Let me put a simple scenario so you can better understand what's going on" 1 - I call submitList with a list containint 5 elements. (Computing will start, and the result will be queued to be committed or not on the main thread in the future) 2 - I call submitList with an empty list. This will be committed ASAP (wrong) 3 - Right away I submitList with a list containint 10 elements. (Computing will start, and the result will be queued to be committed or not on the main thread in the future)
Listo on step number 2 should or should have not been committed, due to list 3 kicking in. But due to the unconditional nature of the call in the block that handles newList being null, it'll be committed no matter what.
Calling submitList from a thread other than the main thread only makes this worse... Also, submitList does operate with collections (outside of the diff computing)... Forcing this on the main thread is bad. Submittions should be thread safe
Another thing to add is that you don't really need complex locking mechanisms to synchronize the commit operation. If you just make sure any and all commit operations are queued on the main thread, and you make sure you check for the mMaxScheduledGeneration counter before executing, things will happen the right way. The fact that there's only one Main thread per application means there's no parallelism, so commits will happen in the order they were queued. The mMaxScheduledGeneration check allows to not execute old (not needed operations). This is a good example of how limited parallelism can sometimes help control concurrent access to resources.
yb...@google.com <yb...@google.com> #8
So to better understand:
You list is at ListVersion0 (ListV0)
You call
submitList(listV1)// 5 items) - async computing
submitList(listV2)// 0 items) - immediate
submitList(listV3)// 10 items) - async computing
Not sure why you think listV2
shouldn't be committed immediately.
submitList do not guarantee that all intermediate lists are applied, it will commit V2 immediately and cancel V1 (when it arrives, it will realize there was another call after).
And when listV3's computation comes back, it will override listV2
.
So your visible list goes from V0 -> V2 -> V3
. That is working as intended.
As for "we could simply hop onto the main thread" argument, it is not very straightforward.
If you call submitList from ThreadA and ThreadB, even if your code to call submitList
from ThreadA was executed before the call from ThreadB, the code running inside has no guarantees to be executed first A then B (since they are in different threads). In theory, you could call ThreadA first but the main thread object could be enqueued after ThreadB's submitList call. The only want to ensure ordering there is to add a sync barrier.
Furthermore, even when you are making these calls from a background thread, you will have to ensure you are calling them in the right order, hence you'll need some locking anyways, which we get from forcing on the main thread in the first place.
e.g. you still needed to make sure you never call submitList
before the previous call to submitList
returned, otherwise, there will be no guarantees on which one executes first.
Does that makes sense?
Description
Component used: ListAdapter, along with a RecyclerView Version used: 1.2.1 Devices/Android versions reproduced on: All of them.-
Recently got the following crash due to submitting a list on a ListAdapter from a background thread:
Seems like the problem is on the AsyncListDiffer class, in the submitList(@Nullable final List<T> newList, @Nullable final Runnable commitCallback) method. This method does a good job at computing list changes in a background thread, and then committing the result in the main thread. But if the newList argument is null, or the mList attribute on the class is null, no diff computing happens (as expected), and the result is commiting on the thread of the caller (not expected). The result should be committed on the main thread for both of this cases, so the method can be considered thread safe.
Current implementation of the method is:
The following changes would come to solve the issue:
As you can see, the important bit of the change is that when no diff computation is done the
mUpdateCallback.onRemoved(0, countRemoved);
andmUpdateCallback.onInserted(0, newList.size());
calls are being done on the main thread on the changes I shared on this ticket. I would gladly submit the changes myself, but sadly I don't have the possibility of cloning the repo and doing it. Due to it I'm raising this ticket, with a detailed explanation of what's going on, and how to solve the problem. Hope you find it useful.