一 Room数据变化LiveData如何收到onChanged回调的?
1.1 LiveData是如何创建的
这里讨论的LiveData的创建是特指Dao定义的方法的返回类型,而不是所有的LiveData。
以**NoteDao
** 举例:
java
@Dao
public interface NoteDao {
@Query("select * from note")
LiveData<List<EntityNote>> getAll();
@Update
int update(EntityNote note);
@Delete
int delete(EntityNote note);
@Insert
void insert(EntityNote note);
}
Room会通过APT自动为**NoteDao
** 创建实体类NoteDao_Impl.java
java
package com.example.sourcecode.jetpack.dao;
import android.database.Cursor;
import androidx.lifecycle.LiveData;
import androidx.room.EntityDeletionOrUpdateAdapter;
import androidx.room.EntityInsertionAdapter;
import androidx.room.RoomDatabase;
import androidx.room.RoomSQLiteQuery;
import androidx.room.util.CursorUtil;
import androidx.room.util.DBUtil;
import androidx.sqlite.db.SupportSQLiteStatement;
import com.example.sourcecode.jetpack.entity.EntityNote;
import java.lang.Class;
import java.lang.Exception;
import java.lang.Override;
import java.lang.String;
import java.lang.SuppressWarnings;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
@SuppressWarnings({"unchecked", "deprecation"})
public final class NoteDao_Impl implements NoteDao {
private final RoomDatabase __db;
private final EntityInsertionAdapter<EntityNote> __insertionAdapterOfEntityNote;
private final EntityDeletionOrUpdateAdapter<EntityNote> __deletionAdapterOfEntityNote;
private final EntityDeletionOrUpdateAdapter<EntityNote> __updateAdapterOfEntityNote;
public NoteDao_Impl(RoomDatabase __db) {
this.__db = __db;
this.__insertionAdapterOfEntityNote = new EntityInsertionAdapter<EntityNote>(__db) {
@Override
public String createQuery() {
return "INSERT OR ABORT INTO `note` (`id`,`uuid`,`title`,`content`,`searchContent`) VALUES (nullif(?, 0),?,?,?,?)";
}
@Override
public void bind(SupportSQLiteStatement stmt, EntityNote value) {
stmt.bindLong(1, value.id);
if (value.uuid == null) {
stmt.bindNull(2);
} else {
stmt.bindString(2, value.uuid);
}
if (value.title == null) {
stmt.bindNull(3);
} else {
stmt.bindString(3, value.title);
}
if (value.content == null) {
stmt.bindNull(4);
} else {
stmt.bindString(4, value.content);
}
if (value.searchContent == null) {
stmt.bindNull(5);
} else {
stmt.bindString(5, value.searchContent);
}
}
};
this.__deletionAdapterOfEntityNote = new EntityDeletionOrUpdateAdapter<EntityNote>(__db) {
@Override
public String createQuery() {
return "DELETE FROM `note` WHERE `id` = ?";
}
@Override
public void bind(SupportSQLiteStatement stmt, EntityNote value) {
stmt.bindLong(1, value.id);
}
};
this.__updateAdapterOfEntityNote = new EntityDeletionOrUpdateAdapter<EntityNote>(__db) {
@Override
public String createQuery() {
return "UPDATE OR ABORT `note` SET `id` = ?,`uuid` = ?,`title` = ?,`content` = ?,`searchContent` = ? WHERE `id` = ?";
}
@Override
public void bind(SupportSQLiteStatement stmt, EntityNote value) {
stmt.bindLong(1, value.id);
if (value.uuid == null) {
stmt.bindNull(2);
} else {
stmt.bindString(2, value.uuid);
}
if (value.title == null) {
stmt.bindNull(3);
} else {
stmt.bindString(3, value.title);
}
if (value.content == null) {
stmt.bindNull(4);
} else {
stmt.bindString(4, value.content);
}
if (value.searchContent == null) {
stmt.bindNull(5);
} else {
stmt.bindString(5, value.searchContent);
}
stmt.bindLong(6, value.id);
}
};
}
@Override
public void insert(final EntityNote note) {
__db.assertNotSuspendingTransaction();
__db.beginTransaction();
try {
__insertionAdapterOfEntityNote.insert(note);
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
}
}
@Override
public int delete(final EntityNote note) {
__db.assertNotSuspendingTransaction();
int _total = 0;
__db.beginTransaction();
try {
_total +=__deletionAdapterOfEntityNote.handle(note);
__db.setTransactionSuccessful();
return _total;
} finally {
__db.endTransaction();
}
}
@Override
public int update(final EntityNote note) {
__db.assertNotSuspendingTransaction();
int _total = 0;
__db.beginTransaction();
try {
_total +=__updateAdapterOfEntityNote.handle(note);
__db.setTransactionSuccessful();
return _total;
} finally {
__db.endTransaction();
}
}
@Override
public LiveData<List<EntityNote>> getAll() {
final String _sql = "select * from note";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
return __db.getInvalidationTracker().createLiveData(new String[]{"note"}, false, new Callable<List<EntityNote>>() {
@Override
public List<EntityNote> call() throws Exception {
final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
final int _cursorIndexOfUuid = CursorUtil.getColumnIndexOrThrow(_cursor, "uuid");
final int _cursorIndexOfTitle = CursorUtil.getColumnIndexOrThrow(_cursor, "title");
final int _cursorIndexOfContent = CursorUtil.getColumnIndexOrThrow(_cursor, "content");
final int _cursorIndexOfSearchContent = CursorUtil.getColumnIndexOrThrow(_cursor, "searchContent");
final List<EntityNote> _result = new ArrayList<EntityNote>(_cursor.getCount());
while(_cursor.moveToNext()) {
final EntityNote _item;
_item = new EntityNote();
_item.id = _cursor.getInt(_cursorIndexOfId);
if (_cursor.isNull(_cursorIndexOfUuid)) {
_item.uuid = null;
} else {
_item.uuid = _cursor.getString(_cursorIndexOfUuid);
}
if (_cursor.isNull(_cursorIndexOfTitle)) {
_item.title = null;
} else {
_item.title = _cursor.getString(_cursorIndexOfTitle);
}
if (_cursor.isNull(_cursorIndexOfContent)) {
_item.content = null;
} else {
_item.content = _cursor.getString(_cursorIndexOfContent);
}
if (_cursor.isNull(_cursorIndexOfSearchContent)) {
_item.searchContent = null;
} else {
_item.searchContent = _cursor.getString(_cursorIndexOfSearchContent);
}
_result.add(_item);
}
return _result;
} finally {
_cursor.close();
}
}
@Override
protected void finalize() {
_statement.release();
}
});
}
public static List<Class<?>> getRequiredConverters() {
return Collections.emptyList();
}
}
通过InvalidationTracker#createLiveData
方法创建需要返回的LiveData对象。
java
// InvalidationTracker.java
public <T> LiveData<T> createLiveData(String[] tableNames, Callable<T> computeFunction) {
return createLiveData(tableNames, false, computeFunction);
}
public <T> LiveData<T> createLiveData(String[] tableNames, boolean inTransaction,
Callable<T> computeFunction) {
return mInvalidationLiveDataContainer.create(
validateAndResolveTableNames(tableNames), inTransaction, computeFunction);
}
java
// InvalidationLiveDataContainer.java
<T> LiveData<T> create(String[] tableNames, boolean inTransaction,
Callable<T> computeFunction) {
return new RoomTrackingLiveData<>(mDatabase, this, inTransaction, computeFunction,
tableNames);
}
InvalidationLiveDataContainer
的功能比较简单:
- 创建
RoomTrackingLiveData
对象; - 维护一个装载
LiveData
对象的set集合。
总结:
- room会根据开发者定义的dataBae和各个dao类自动创建各自的对应的实体类;
- DAO_Impl的实体方法会委托InvalidationTracker类创建需要返回的LiveData对象,并将数据库操作方法以参数的形式向下传递。
- InvalidationTracker类委托InvalidationLiveDataContainer类创建RoomTrackingLiveData对象。自此LiveData对象创建成功。
1.2 RoomTrackingLiveData有何作用
java
class RoomTrackingLiveData<T> extends LiveData<T> {
final RoomDatabase mDatabase;
final boolean mInTransaction;
final Callable<T> mComputeFunction;
private final InvalidationLiveDataContainer mContainer;
final InvalidationTracker.Observer mObserver;
final AtomicBoolean mInvalid = new AtomicBoolean(true);
final AtomicBoolean mComputing = new AtomicBoolean(false);
final AtomicBoolean mRegisteredObserver = new AtomicBoolean(false);
final Runnable mRefreshRunnable = new Runnable() {
@WorkerThread
@Override
public void run() {
// 向InvalidationTracker注册一个观察者
if (mRegisteredObserver.compareAndSet(false, true)) {
mDatabase.getInvalidationTracker().addWeakObserver(mObserver);
}
boolean computed;
do {
computed = false;
// mComputing 初始值为 false
if (mComputing.compareAndSet(false, true)) {
// as long as it is invalid, keep computing.
try {
T value = null;
// mInvalid初始值为 true
// 此while循环结束后,computed == false,mInvalid == false
while (mInvalid.compareAndSet(true, false)) {
computed = true;
try {
// 执行数据库操作方法,并返回结果
value = mComputeFunction.call();
} catch (Exception e) {
// 如果SQL语句执行有误,会非常粗暴的直接报错,
// liveData不能将错误状态上报给开发者。
throw new RuntimeException("Exception while computing database"
+ " live data.", e);
}
}
if (computed) {
// 向当前livedata的观察者们发送数据库查询结果
postValue(value);
}
} finally {
// release compute lock
mComputing.set(false);
}
}
} while (computed && mInvalid.get());
}
};
@SuppressWarnings("WeakerAccess")
final Runnable mInvalidationRunnable = new Runnable() {
@MainThread
@Override
public void run() {
// 当前livedata是否有存活的观察者
boolean isActive = hasActiveObservers();
// 如果 mRefreshRunnable正在运行 mInvalid == true,条件不成立。
// 如果 mRefreshRunnable运行结束 mInvalid == false,条件成立,重新开启任务。
if (mInvalid.compareAndSet(false, true)) {
if (isActive) {
getQueryExecutor().execute(mRefreshRunnable);
}
}
}
};
@SuppressLint("RestrictedApi")
RoomTrackingLiveData(
RoomDatabase database,
InvalidationLiveDataContainer container,
boolean inTransaction,
Callable<T> computeFunction,
String[] tableNames) {
mDatabase = database;
mInTransaction = inTransaction;
mComputeFunction = computeFunction;
mContainer = container;
mObserver = new InvalidationTracker.Observer(tableNames) {
@Override
public void onInvalidated(@NonNull Set<String> tables) {
ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);
}
};
}
@Override
protected void onActive() {
super.onActive();
mContainer.onActive(this);
getQueryExecutor().execute(mRefreshRunnable);
}
@Override
protected void onInactive() {
super.onInactive();
mContainer.onInactive(this);
}
Executor getQueryExecutor() {
if (mInTransaction) {
return mDatabase.getTransactionExecutor();
} else {
return mDatabase.getQueryExecutor();
}
}
}
- 当开发者向
RoomTrackingLiveData
注册了观察者后(即调用了livedata.observe
方法),会调用onActive方
法,在子线程里执行mRefreshRunnable
任务。 mRefreshRunnable
在初次执行时会向InvalidationTracker
注册一个观察者。然后会根据SQL语句循环查询数据库,并向开发者返回查询结果。
a. SQL语句是通过开发者在创建DAO层方法的注解自动生成的,并以方法入参的方式最终传递给RoomTrackingLiveData
对象。
b. 这里的循环不是一直执行的。在没有外界干扰情况下(指循环条件的值在没有被其他方法修改的情况),循环体只会执行一次。- 构造函数里创建了
mObserver
对象,当mObserver
被触发时,会在主线程执行mInvalidationRunnable
任务。 mInvalidationRunnable
会在子线程里开启mRefreshRunnable
任务,重新查询数据库,并返回数据。
总结:
RoomTrackingLiveData
有三个比较重要的任务:mRefreshRunnable
、mInvalidationRunnable
和mObserver
。mRefreshRunnable
主要负责向数据库查询数据,并将结果返回给开发者注册的观察者。mObserver
负责唤醒mInvalidationRunnable
。mInvalidationRunnable
任务分两种情况:- 当
mRefreshRunnable
还在运行时,会要求mRefreshRunnable
再执行一次数据库查询任务,并按要求将结果上报。(这个逻辑是在mRefreshRunnable
里实现的。) - 当
mRefreshRunnable
停止运行时,会在子线程里重新开启mRefreshRunnable
任务。
- 当
由上可知,room配合livedata使用时,之所以livedata能够自动感知数据库数据变化,是由
mObserver
、mInvalidationRunnable
、mRefreshRunnable
三方共同配合的结果。
1.3 数据库变化时,是如何通知RoomTrackingLiveData
由上文可以推断出,当数据库发生变化时,是通过mObserver
来启动数据库查询任务,并将结果通过
RoomTrackingLiveData#postValue
方法传递给订阅者。接下来就要研究一下mObserver
的调用链。
java
// RoomTrackingLiveData.java
final Runnable mRefreshRunnable = new Runnable() {
@WorkerThread
@Override
public void run() {
// 1. 向InvalidationTracker注册一个观察者
if (mRegisteredObserver.compareAndSet(false, true)) {
mDatabase.getInvalidationTracker().addWeakObserver(mObserver);
}
....
}
};
java
// InvalidationTracker.java
public void addWeakObserver(Observer observer) {
// 2
addObserver(new WeakObserver(this, observer));
}
public void addObserver(@NonNull Observer observer) {
final String[] tableNames = resolveViews(observer.mTables);
int[] tableIds = new int[tableNames.length];
final int size = tableNames.length;
for (int i = 0; i < size; i++) {
Integer tableId = mTableIdLookup.get(tableNames[i].toLowerCase(Locale.US));
if (tableId == null) {
throw new IllegalArgumentException("There is no table with name " + tableNames[i]);
}
tableIds[i] = tableId;
}
ObserverWrapper wrapper = new ObserverWrapper(observer, tableIds, tableNames);
ObserverWrapper currentObserver;
synchronized (mObserverMap) {
// 3
currentObserver = mObserverMap.putIfAbsent(observer, wrapper);
}
if (currentObserver == null && mObservedTableTracker.onAdded(tableIds)) {
syncTriggers();
}
}
RoomTrackingLiveData
创建mObserver
对象,并一步步将mObserver
进行包装,并存放在InvalidationTracker
的mObserverMap
中。- 接下来则需要调查源码里在哪些情况下会遍历
mObserverMap
,并去调用mObserverMap
里item
的方法。
java
// InvalidationTracker.java
Runnable mRefreshRunnable = new Runnable() {
@Override
public void run() {
......
if (invalidatedTableIds != null && !invalidatedTableIds.isEmpty()) {
synchronized (mObserverMap) {
// 1. 遍历了 mObserverMap
for (Map.Entry<Observer, ObserverWrapper> entry : mObserverMap) {
entry.getValue().notifyByTableInvalidStatus(invalidatedTableIds);
}
}
}
}
......
};
public void notifyObserversByTableNames(String... tables) {
synchronized (mObserverMap) {
// 2. 遍历了 mObserverMap
for (Map.Entry<Observer, ObserverWrapper> entry : mObserverMap) {
if (!entry.getKey().isRemote()) {
entry.getValue().notifyByTableNames(tables);
}
}
}
}
由源码可知,共有两处遍历了mObserverMap
,我们先研究一下mRefreshRunnable
的调用链。
java
/**
* Enqueues a task to refresh the list of updated tables.
* <p>
* This method is automatically called when {@link RoomDatabase#endTransaction()} is called but
* if you have another connection to the database or directly use {@link
* SupportSQLiteDatabase}, you may need to call this manually.
*/
public void refreshVersionsAsync() {
// TODO we should consider doing this sync instead of async.
if (mPendingRefresh.compareAndSet(false, true)) {
if (mAutoCloser != null) {
mAutoCloser.incrementCountAndEnsureDbIsOpen();
}
// 启动 mRefreshRunnable 任务
mDatabase.getQueryExecutor().execute(mRefreshRunnable);
}
}
- 从方法说明上可以看出,当
RoomDatabase#endTransaction()
被调用时,会启动mRefreshRunnable
任务。继续跟踪refreshVersionsAsync
的调用链也能发现这点。 - 接下来让我们回头研究一下room框架自动为开发者定义的dao类自动生成的
xxxDAO_Impl.java
。仔细研究一下各个方法的实现会发现,只要涉及到对数据库进行增、删、改的操作,都会调用到__db.endTransaction()
。这里的__db
就是RoomDatabase
的对象。例如:
java
@Override
public void insert(final EntityNote note) {
__db.assertNotSuspendingTransaction();
__db.beginTransaction();
try {
__insertionAdapterOfEntityNote.insert(note);
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
}
}
@Override
public int delete(final EntityNote note) {
__db.assertNotSuspendingTransaction();
int _total = 0;
__db.beginTransaction();
try {
_total +=__deletionAdapterOfEntityNote.handle(note);
__db.setTransactionSuccessful();
return _total;
} finally {
__db.endTransaction();
}
}
@Override
public int update(final EntityNote note) {
__db.assertNotSuspendingTransaction();
int _total = 0;
__db.beginTransaction();
try {
_total +=__updateAdapterOfEntityNote.handle(note);
__db.setTransactionSuccessful();
return _total;
} finally {
__db.endTransaction();
}
}
1.3.1 __db.endTransaction()中调用internalEndTransaction()
java
public void endTransaction() {
if (mAutoCloser == null) {
internalEndTransaction();
} else {
mAutoCloser.executeRefCountingFunction(db -> {
internalEndTransaction();
return null;
});
}
}
1.3.2 mInvalidationTracker.refreshVersionsAsync()
java
private void internalEndTransaction() {
mOpenHelper.getWritableDatabase().endTransaction();
if (!inTransaction()) {
// enqueue refresh only if we are NOT in a transaction. Otherwise, wait for the last
// endTransaction call to do it.
mInvalidationTracker.refreshVersionsAsync();
}
}
1.3.3 mDatabase.getQueryExecutor().execute(mRefreshRunnable)
java
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);
}
}
java
Runnable mRefreshRunnable = new Runnable() {
@Override
public void run() {
final Lock closeLock = mDatabase.getCloseLock();
Set<Integer> invalidatedTableIds = null;
closeLock.lock();
try {
if (!ensureInitialization()) {
return;
}
if (!mPendingRefresh.compareAndSet(true, false)) {
// no pending refresh
return;
}
if (mDatabase.inTransaction()) {
// current thread is in a transaction. when it ends, it will invoke
// refreshRunnable again. mPendingRefresh is left as false on purpose
// so that the last transaction can flip it on again.
return;
}
// 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.beginTransactionNonExclusive();
try {
invalidatedTableIds = checkUpdatedTable();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
} catch (IllegalStateException | SQLiteException exception) {
// may happen if db is closed. just log.
Log.e(Room.LOG_TAG, "Cannot run invalidation tracker. Is the db closed?",
exception);
} finally {
closeLock.unlock();
if (mAutoCloser != null) {
mAutoCloser.decrementCountAndScheduleClose();
}
}
if (invalidatedTableIds != null && !invalidatedTableIds.isEmpty()) {
synchronized (mObserverMap) {
for (Map.Entry<Observer, ObserverWrapper> entry : mObserverMap) {
entry.getValue().notifyByTableInvalidStatus(invalidatedTableIds);
}
}
}
}
private Set<Integer> checkUpdatedTable() {
HashSet<Integer> invalidatedTableIds = new HashSet<>();
Cursor cursor = mDatabase.query(new SimpleSQLiteQuery(SELECT_UPDATED_TABLES_SQL));
//noinspection TryFinallyCanBeTryWithResources
try {
while (cursor.moveToNext()) {
final int tableId = cursor.getInt(0);
invalidatedTableIds.add(tableId);
}
} finally {
cursor.close();
}
if (!invalidatedTableIds.isEmpty()) {
mCleanupStatement.executeUpdateDelete();
}
return invalidatedTableIds;
}
};
1.3.4 entry.getValue().notifyByTableInvalidStatus(invalidatedTableIds)
java
void notifyByTableInvalidStatus(Set<Integer> invalidatedTablesIds) {
Set<String> invalidatedTables = null;
final int size = mTableIds.length;
for (int index = 0; index < size; index++) {
final int tableId = mTableIds[index];
if (invalidatedTablesIds.contains(tableId)) {
if (size == 1) {
// Optimization for a single-table observer
invalidatedTables = mSingleTableSet;
} else {
if (invalidatedTables == null) {
invalidatedTables = new HashSet<>(size);
}
invalidatedTables.add(mTableNames[index]);
}
}
}
if (invalidatedTables != null) {
mObserver.onInvalidated(invalidatedTables);
}
}
1.3.5 mObserver.onInvalidated(invalidatedTables)
java
RoomTrackingLiveData(
RoomDatabase database,
InvalidationLiveDataContainer container,
boolean inTransaction,
Callable<T> computeFunction,
String[] tableNames) {
mDatabase = database;
mInTransaction = inTransaction;
mComputeFunction = computeFunction;
mContainer = container;
mObserver = new InvalidationTracker.Observer(tableNames) {
@Override
public void onInvalidated(@NonNull Set<String> tables) {
ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);
}
};
}
1.3.6 ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable)
java
final Runnable mInvalidationRunnable = new Runnable() {
@MainThread
@Override
public void run() {
boolean isActive = hasActiveObservers();
if (mInvalid.compareAndSet(false, true)) {
if (isActive) {
getQueryExecutor().execute(mRefreshRunnable);
}
}
}
}
1.3.7 mRefreshRunnable.run postValue(value)
java
final Runnable mRefreshRunnable = new Runnable() {
@WorkerThread
@Override
public void run() {
// 向InvalidationTracker注册一个观察者
if (mRegisteredObserver.compareAndSet(false, true)) {
mDatabase.getInvalidationTracker().addWeakObserver(mObserver);
}
boolean computed;
do {
computed = false;
// mComputing 初始值为 false
if (mComputing.compareAndSet(false, true)) {
// as long as it is invalid, keep computing.
try {
T value = null;
// mInvalid初始值为 true
// 此while循环结束后,computed == false,mInvalid == false
while (mInvalid.compareAndSet(true, false)) {
computed = true;
try {
// 执行数据库操作方法,并返回结果
value = mComputeFunction.call();
} catch (Exception e) {
// 如果SQL语句执行有误,会非常粗暴的直接报错,
// liveData不能将错误状态上报给开发者。
throw new RuntimeException("Exception while computing database"
+ " live data.", e);
}
}
if (computed) {
// 向当前livedata的观察者们发送数据库查询结果
postValue(value);
}
} finally {
// release compute lock
mComputing.set(false);
}
}
} while (computed && mInvalid.get());
}
};
1.3.8 observer收到onChanged回调
LiveData<List<EntityNote>> EntityNoteLiveData = AppDatabase.getInstance().noteDao().getAll()注册的RoomLiveData的observer会回调onChanged
java
// 继承AndroidViewModel,带有Application环境
public class NoteViewModel extends AndroidViewModel {
private MediatorLiveData<List<EntityNote >> mMediatorLiveData;
public NoteViewModel(@NonNull Application application) {
super(application);
mMediatorLiveData = new MediatorLiveData<>();
LiveData<List<EntityNote>> EntityNoteLiveData = AppDatabase.getInstance().noteDao().getAll();
mMediatorLiveData.addSource(EntityNoteLiveData, new Observer<List<EntityNote>>() {
private List<EntityNote> mLastEntityNoteList;
@Override
public void onChanged(List<EntityNote> entityNotes) {
if (mLastEntityNoteList == null) {
mLastEntityNoteList = entityNotes;
return;
}
if (entityNotes == null) {
setValue(new ArrayList<>());
return;
}
int lastSize = mLastEntityNoteList.size();
int size = entityNotes.size();
if (lastSize != size) {
setValue(entityNotes);
return;
}
for (int i = 0; i < size; i++) {
EntityNote lastNote = mLastEntityNoteList.get(i);
EntityNote note = entityNotes.get(i);
if (!isSameNote(lastNote, note)) {
setValue(entityNotes);
break;
}
}
// 没有变化不setValue不触发onChanged
mLastEntityNoteList = entityNotes;
}
private void setValue(List<EntityNote> entityNotes) {
mMediatorLiveData.setValue(entityNotes);
mLastEntityNoteList = entityNotes;
}
private boolean isSameNote(EntityNote first, EntityNote second) {
if (first == null || second == null) {
return false;
}
return first.uuid.equals(second.uuid) && first.title.equals(second.title)
&& first.id == second.id && first.content.equals(second.content);
}
});
}
//查(所有)
public MediatorLiveData<List<EntityNote>> getNoteListLiveData() {
return mMediatorLiveData;
}
}
1.4 总结:
- 数据库的增、删、改操作会调用
RoomDatabase#endTransaction()
; RoomDatabase#endTransaction()
会调用InvalidationTracker#refreshVersionsAsync()
;refreshVersionsAsync()
会开启mRefreshRunnable
任务。mRefreshRunnable
里会遍历mObserverMap
,并挨个调用其item
的指定方法。RoomTrackingLiveData
在构造函数里创建了mObserver
对象,并将此对象放置于InvalidationTracker
的mObserverMap
中。且此对象的方法就是用来唤醒RoomTrackingLiveData
的mRefreshRunnable
任务。还记得这个任务是干嘛的吗?这个任务就是根据RoomTrackingLiveData
持有的数据库查询语句向数据库查询数据,并将查询结果上报给开发者指定的Observer
。
至此,RoomTrackingLiveData完美实现了数据库发生变化时,会主动将新的数据上报给开发者的功能。
二 Room是如何通知其他进程的订阅者
如果有两个进程同时关联了同一个数据库,如果一个进程对此数据库的数据进行改变,那么另一个进程的RoomTrackingLiveData
依旧能感知到数据变化,这是怎么做到的呢?
还记得上面在调查InvalidationTracker
的mObserverMap
时,发现有两个方法遍历了这个map
吗。其中mRefreshRunnable
已经分析过了,接下来分析另一个方法notifyObserversByTableNames
。
java
// InvalidationTracker.java
public void notifyObserversByTableNames(String... tables) {
synchronized (mObserverMap) {
// 遍历了 mObserverMap
for (Map.Entry<Observer, ObserverWrapper> entry : mObserverMap) {
if (!entry.getKey().isRemote()) {
entry.getValue().notifyByTableNames(tables);
}
}
}
}
java
// MultiInstanceInvalidationClient.java
final IMultiInstanceInvalidationCallback mCallback =
new IMultiInstanceInvalidationCallback.Stub() {
@Override
public void onInvalidation(final String[] tables) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
//1.调用了 nvalidationTracker#notifyObserversByTableNames()
mInvalidationTracker.notifyObserversByTableNames(tables);
}
});
}
};
final Runnable mSetUpRunnable = new Runnable() {
@Override
public void run() {
try {
final IMultiInstanceInvalidationService service = mService;
if (service != null) {
//2. 向 service 注册 mCallback
mClientId = service.registerCallback(mCallback, mName);
mInvalidationTracker.addObserver(mObserver);
}
} catch (RemoteException e) {
Log.w(Room.LOG_TAG, "Cannot register multi-instance invalidation callback", e);
}
}
};
final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IMultiInstanceInvalidationService.Stub.asInterface(service);
// 3. 执行 mSetUpRunnable 任务
mExecutor.execute(mSetUpRunnable);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mExecutor.execute(mRemoveObserverRunnable);
mService = null;
}
};
- 由上可见,在
MultiInstanceInvalidationClient
类里绑定了一个service
,并向service
注册mCallback
。这个mCallback
会通过InvalidationTracker#notifyObserversByTableNames()
通知RoomTrackingLiveData
该干活了(查询和上报数据库新值)。
看到
IMultiInstanceInvalidationService.Stub
可以大胆猜测这里涉及到了跨进程通信。接下来研究
MultiInstanceInvalidationService
java
// MultiInstanceInvalidationService.java
public class MultiInstanceInvalidationService extends Service {
int mMaxClientId = 0;
final HashMap<Integer, String> mClientNames = new HashMap<>();
// 1. 可以理解成这是一个装载 callBack的集合
final RemoteCallbackList<IMultiInstanceInvalidationCallback> mCallbackList =
new RemoteCallbackList<IMultiInstanceInvalidationCallback>() {
@Override
public void onCallbackDied(IMultiInstanceInvalidationCallback callback,
Object cookie) {
mClientNames.remove((int) cookie);
}
};
private final IMultiInstanceInvalidationService.Stub mBinder =
new IMultiInstanceInvalidationService.Stub() {
@Override
public int registerCallback(IMultiInstanceInvalidationCallback callback,
String name) {
if (name == null) {
return 0;
}
synchronized (mCallbackList) {
int clientId = ++mMaxClientId;
// 2. 将 callback 放入 mCallbackList 集合中
if (mCallbackList.register(callback, clientId)) {
mClientNames.put(clientId, name);
return clientId;
} else {
--mMaxClientId;
return 0;
}
}
}
@Override
public void unregisterCallback(IMultiInstanceInvalidationCallback callback,
int clientId) {
synchronized (mCallbackList) {
mCallbackList.unregister(callback);
mClientNames.remove(clientId);
}
}
@Override
public void broadcastInvalidation(int clientId, String[] tables) {
synchronized (mCallbackList) {
String name = mClientNames.get(clientId);
if (name == null) {
Log.w(Room.LOG_TAG, "Remote invalidation client ID not registered");
return;
}
int count = mCallbackList.beginBroadcast();
try {
// 这个for循环,可以理解成取出mCallbackList集合中的所有callBack,
// 并调用各自的 onInvalidation方法。
for (int i = 0; i < count; i++) {
int targetClientId = (int) mCallbackList.getBroadcastCookie(i);
String targetName = mClientNames.get(targetClientId);
if (clientId == targetClientId // This is the caller itself.
|| !name.equals(targetName)) { // Not the same file.
continue;
}
try {
IMultiInstanceInvalidationCallback callback =
mCallbackList.getBroadcastItem(i);
callback.onInvalidation(tables);
} catch (RemoteException e) {
Log.w(Room.LOG_TAG, "Error invoking a remote callback", e);
}
}
} finally {
mCallbackList.finishBroadcast();
}
}
}
};
@Nullable
@Override
public IBinder onBind(@NonNull Intent intent) {
return mBinder;
}
}
- 由以上源码可以推断出这个
service
主要做了两件事:- 在内存中维护一个集合,这个集合装载的是所有
client
注册的callBack
; - 在合适的时机调用所有
client
注册的callBack
。这个合适的时机,就是调用broadcastInvalidation()
的时候。
- 在内存中维护一个集合,这个集合装载的是所有
回到MultiInstanceInvalidationClient
,回想一下这个client
向service
注册了个什么玩意。
java
// MultiInstanceInvalidationClient.java
final Runnable mSetUpRunnable = new Runnable() {
@Override
public void run() {
try {
final IMultiInstanceInvalidationService service = mService;
if (service != null) {
// 1. 向service注册mCallback
mClientId = service.registerCallback(mCallback, mName);
mInvalidationTracker.addObserver(mObserver);
}
} catch (RemoteException e) {
Log.w(Room.LOG_TAG, "Cannot register multi-instance invalidation callback", e);
}
}
};
final IMultiInstanceInvalidationCallback mCallback =
new IMultiInstanceInvalidationCallback.Stub() {
@Override
public void onInvalidation(final String[] tables) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
// 2. 这个方法是干什么的来着?
// 是拜托InvalidationTracker通知RoomTrackingLiveData该干活了。
// 上文有介绍
mInvalidationTracker.notifyObserversByTableNames(tables);
}
});
}
};
接下来追踪一下MultiInstanceInvalidationService#broadcastInvalidation()
java
// MultiInstanceInvalidationClient.java
MultiInstanceInvalidationClient(Context context, String name, Intent serviceIntent,
InvalidationTracker invalidationTracker, Executor executor) {
......
mObserver = new InvalidationTracker.Observer(tableNames.toArray(new String[0])) {
@Override
public void onInvalidated(@NonNull Set<String> tables) {
if (mStopped.get()) {
return;
}
try {
final IMultiInstanceInvalidationService service = mService;
if (service != null) {
// 1. 调用了MultiInstanceInvalidationService#broadcastInvalidation()
service.broadcastInvalidation(mClientId, tables.toArray(new String[0]));
}
} catch (RemoteException e) {
Log.w(Room.LOG_TAG, "Cannot broadcast invalidation", e);
}
}
@Override
boolean isRemote() {
return true;
}
};
mAppContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
final Runnable mSetUpRunnable = new Runnable() {
@Override
public void run() {
try {
final IMultiInstanceInvalidationService service = mService;
if (service != null) {
mClientId = service.registerCallback(mCallback, mName);
// 2. 将mObserver传递给InvalidationTracker
mInvalidationTracker.addObserver(mObserver);
}
} catch (RemoteException e) {
Log.w(Room.LOG_TAG, "Cannot register multi-instance invalidation callback", e);
}
}
};
看了以上2个步骤是不是似曾相识?还记得RoomTrackingLiveData
的mObserver
对象吗?和这里的套路是一模一样。接下来很明显,InvalidationTracker
里面会有一个map
来装载这个mObserver
。然后会有两个方法去遍历这个map
。其中一个Runnable
方法会在调用数据库的增删改方法时触发,另一个方法notifyObserversByTableNames
会在...会在...???
我不是在研究notifyObserversByTableNames的调用链吗?怎么又绕回来了?
这里理解起来有点绕,先明确一下前提:
- 针对不同的进程操作同一个数据库的场景,其实每一个进程都会拥有自己独立的
RoomDatabase
实例。相应的MultiInstanceInvalidationClient
、InvalidationTracker
、RoomTrackingLiveData
都是相互独立的。- 只有
MultiInstanceInvalidationService
是共同的实例。而这个共同的实例,是保证不同进程能相互感知到数据库操作的关键。InvalidationTracker
的mRefreshRunnable
是在单进程中调用的。InvalidationTracker
的notifyObserversByTableNames
是用于跨进程调用的。
下面重新捋一下思路。首先假设现在有两个进程会操作同一个数据库。那么这两个进程都会各自拥有一套自己的独立对象。即都会做一下事情:
- 创建
RoomTrackingLiveData
对象,并将mObserver
委托给InvalidationTracker
管理。 RoomTrackingLiveData
里的mRefreshRunnable
会在被唤醒时重新查询数据库,并上报结果。- 创建
MultiInstanceInvalidationClient
对象,并与唯一的MultiInstanceInvalidationService
进行绑定,并将callBack
委托给service
管理。 callBack
里会调用InvalidationTracker#notifyObserversByTableNames()
。MultiInstanceInvalidationClient
对象将mObserver
委托给InvalidationTracker
管理。MultiInstanceInvalidationClient
的mObserver
会通知所有与MultiInstanceInvalidationService
进行绑定的MultiInstanceInvalidationClient
,告知它们数据库有变化。
针对进程1,我们重点关注3、4、5、6。针对进程2,我们重点关注1、2。现在开始发车:
- 当前用户在进程1操作了数据库的修改操作,那么就会触发进程1的
RoomDatabase#endTransaction()
,
进而触发了InvalidationTracker#mRefreshRunnable
任务,遍历InvalidationTracker#mObserverMap
(在上一节有相关介绍)。此mObserverMap
里存在一个MultiInstanceInvalidationClient
添加进来的mObserver
(上面第5点有提到)。 - 进程1的
MultiInstanceInvalidationClient
的mObserver
会调用MultiInstanceInvalidationService#broadcastInvalidation()
。 MultiInstanceInvalidationService
会遍历和执行所有MultiInstanceInvalidationClient
注册的callback
。这其中的一个callback
就是进程2的MultiInstanceInvalidationClient
注册的(上面第5点有提到)。- 进程2的
callback
会调用进程2的InvalidationTracker#notifyObserversByTableNames()
。再回忆一下这个notifyObserversByTableNames()
是干嘛的?没错,就是我们研究的第二个遍历InvalidationTracker
的mObserverMap
的方法。 - 既然进程2已经遍历了
mObserverMap
,那么势必会让进程2的RoomTrackingLiveData
干活(查询数据库,上报新数据)。
至此,room框架完成了一次完美的跨进程通讯。
要想当前的RoomDataBase
具有跨进程通讯的能力,需要在构建databaseBuilder
的时候调用enableMultiInstanceInvalidation()
。例如:
java
@Database(entities = {EntityNote.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
private static final String DB_NAME = "note.db";
private static volatile AppDatabase instance;//创建单例
public static synchronized AppDatabase getInstance() {
if (instance == null) {
instance = create();
}
return instance;
}
/**
* 创建数据库
*/
private static AppDatabase create() {
return Room.databaseBuilder(MyApplication.getInstance(), AppDatabase.class, DB_NAME)
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.enableMultiInstanceInvalidation() // 跨进程通讯的能力
.build();
}
public abstract NoteDao noteDao();
}
从源码来看,RoomDataBase
正是通过此方法来间接创建MultiInstanceInvalidationClient
对象,并与MultiInstanceInvalidationService
建立绑定关系。