Updated ROOM

This commit is contained in:
M66B
2021-01-31 18:26:44 +01:00
parent 1a359431c6
commit 70a2af170f
25 changed files with 2850 additions and 76 deletions

View File

@@ -20,6 +20,7 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -89,6 +90,9 @@ public class InvalidationTracker {
@NonNull
private Map<String, Set<String>> mViewTables;
@Nullable
AutoCloser mAutoCloser = null;
@SuppressWarnings("WeakerAccess") /* synthetic access */
final RoomDatabase mDatabase;
@@ -159,6 +163,23 @@ public class InvalidationTracker {
}
}
/**
* Sets the auto closer for this invalidation tracker so that the invalidation tracker can
* ensure that the database is not closed if there are pending invalidations that haven't yet
* been flushed.
*
* This also adds a callback to the autocloser to ensure that the InvalidationTracker is in
* an ok state once the table is invalidated.
*
* This must be called before the database is used.
*
* @param autoCloser the autocloser associated with the db
*/
void setAutoCloser(AutoCloser autoCloser) {
this.mAutoCloser = autoCloser;
mAutoCloser.setAutoCloseCallback(this::onAutoCloseCallback);
}
/**
* Internal method to initialize table tracking.
* <p>
@@ -182,6 +203,13 @@ public class InvalidationTracker {
}
}
void onAutoCloseCallback() {
synchronized (this) {
mInitialized = false;
mObservedTableTracker.resetTriggerState();
}
}
void startMultiInstanceInvalidation(Context context, String name) {
mMultiInstanceInvalidationClient = new MultiInstanceInvalidationClient(context, name, this,
mDatabase.getQueryExecutor());
@@ -249,6 +277,9 @@ public class InvalidationTracker {
* <p>
* If one of the tables in the Observer does not exist in the database, this method throws an
* {@link IllegalArgumentException}.
* <p>
* This method should be called on a background/worker thread as it performs database
* operations.
*
* @param observer The observer which listens the database for changes.
*/
@@ -305,6 +336,15 @@ public class InvalidationTracker {
return tables.toArray(new String[tables.size()]);
}
private static void beginTransactionInternal(SupportSQLiteDatabase database) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
&& database.isWriteAheadLoggingEnabled()) {
database.beginTransactionNonExclusive();
} else {
database.beginTransaction();
}
}
/**
* Adds an observer but keeps a weak reference back to it.
* <p>
@@ -322,6 +362,9 @@ public class InvalidationTracker {
/**
* Removes the observer from the observers list.
* <p>
* This method should be called on a background/worker thread as it performs database
* operations.
*
* @param observer The observer to remove.
*/
@@ -360,8 +403,8 @@ public class InvalidationTracker {
public void run() {
final Lock closeLock = mDatabase.getCloseLock();
Set<Integer> invalidatedTableIds = null;
closeLock.lock();
try {
closeLock.lock();
if (!ensureInitialization()) {
return;
@@ -383,7 +426,7 @@ public class InvalidationTracker {
// This transaction has to be on the underlying DB rather than the RoomDatabase
// in order to avoid a recursive loop after endTransaction.
SupportSQLiteDatabase db = mDatabase.getOpenHelper().getWritableDatabase();
db.beginTransaction();
db.beginTransactionNonExclusive();
try {
invalidatedTableIds = checkUpdatedTable();
db.setTransactionSuccessful();
@@ -399,6 +442,10 @@ public class InvalidationTracker {
exception);
} finally {
closeLock.unlock();
if (mAutoCloser != null) {
mAutoCloser.decrementCountAndScheduleClose();
}
}
if (invalidatedTableIds != null && !invalidatedTableIds.isEmpty()) {
synchronized (mObserverMap) {
@@ -439,6 +486,13 @@ public class InvalidationTracker {
public void refreshVersionsAsync() {
// TODO we should consider doing this sync instead of async.
if (mPendingRefresh.compareAndSet(false, true)) {
if (mAutoCloser != null) {
// refreshVersionsAsync is called with the ref count incremented from
// RoomDatabase, so the db can't be closed here, but we need to be sure that our
// db isn't closed until refresh is completed. This increment call must be
// matched with a corresponding call in mRefreshRunnable.
mAutoCloser.incrementCountAndEnsureDbIsOpen();
}
mDatabase.getQueryExecutor().execute(mRefreshRunnable);
}
}
@@ -451,6 +505,10 @@ public class InvalidationTracker {
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
@WorkerThread
public void refreshVersionsSync() {
if (mAutoCloser != null) {
// This increment call must be matched with a corresponding call in mRefreshRunnable.
mAutoCloser.incrementCountAndEnsureDbIsOpen();
}
syncTriggers();
mRefreshRunnable.run();
}
@@ -495,7 +553,7 @@ public class InvalidationTracker {
return;
}
final int limit = tablesToSync.length;
database.beginTransaction();
beginTransactionInternal(database);
try {
for (int tableId = 0; tableId < limit; tableId++) {
switch (tablesToSync[tableId]) {
@@ -785,6 +843,17 @@ public class InvalidationTracker {
return needTriggerSync;
}
/**
* If we are re-opening the db we'll need to add all the triggers that we need so change
* the current state to false for all.
*/
void resetTriggerState() {
synchronized (this) {
Arrays.fill(mTriggerStates, false);
mNeedsSync = true;
}
}
/**
* If this returns non-null, you must call onSyncCompleted.
*