房地產(chǎn)怎么做網(wǎng)站推廣子域名網(wǎng)址查詢
一? Room數(shù)據(jù)變化LiveData如何收到onChanged回調(diào)的?
1.1? LiveData是如何創(chuàng)建的
這里討論的LiveData的創(chuàng)建是特指Dao定義的方法的返回類型,而不是所有的LiveData。
以NoteDao
?舉例:
@Dao
public interface NoteDao {@Query("select * from note")LiveData<List<EntityNote>> getAll();@Updateint update(EntityNote note);@Deleteint delete(EntityNote note);@Insertvoid insert(EntityNote note);
}
?Room會(huì)通過APT自動(dòng)為NoteDao
?創(chuàng)建實(shí)體類NoteDao_Impl.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) {@Overridepublic String createQuery() {return "INSERT OR ABORT INTO `note` (`id`,`uuid`,`title`,`content`,`searchContent`) VALUES (nullif(?, 0),?,?,?,?)";}@Overridepublic 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) {@Overridepublic String createQuery() {return "DELETE FROM `note` WHERE `id` = ?";}@Overridepublic void bind(SupportSQLiteStatement stmt, EntityNote value) {stmt.bindLong(1, value.id);}};this.__updateAdapterOfEntityNote = new EntityDeletionOrUpdateAdapter<EntityNote>(__db) {@Overridepublic String createQuery() {return "UPDATE OR ABORT `note` SET `id` = ?,`uuid` = ?,`title` = ?,`content` = ?,`searchContent` = ? WHERE `id` = ?";}@Overridepublic 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);}};}@Overridepublic void insert(final EntityNote note) {__db.assertNotSuspendingTransaction();__db.beginTransaction();try {__insertionAdapterOfEntityNote.insert(note);__db.setTransactionSuccessful();} finally {__db.endTransaction();}}@Overridepublic int delete(final EntityNote note) {__db.assertNotSuspendingTransaction();int _total = 0;__db.beginTransaction();try {_total +=__deletionAdapterOfEntityNote.handle(note);__db.setTransactionSuccessful();return _total;} finally {__db.endTransaction();}}@Overridepublic int update(final EntityNote note) {__db.assertNotSuspendingTransaction();int _total = 0;__db.beginTransaction();try {_total +=__updateAdapterOfEntityNote.handle(note);__db.setTransactionSuccessful();return _total;} finally {__db.endTransaction();}}@Overridepublic 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>>() {@Overridepublic 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();}}@Overrideprotected void finalize() {_statement.release();}});}public static List<Class<?>> getRequiredConverters() {return Collections.emptyList();}
}
通過InvalidationTracker#createLiveData
方法創(chuàng)建需要返回的LiveData對(duì)象。
// InvalidationTracker.javapublic <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);
}
// InvalidationLiveDataContainer.java<T> LiveData<T> create(String[] tableNames, boolean inTransaction,Callable<T> computeFunction) {return new RoomTrackingLiveData<>(mDatabase, this, inTransaction, computeFunction,tableNames);
}
InvalidationLiveDataContainer
的功能比較簡單:
- 創(chuàng)建
RoomTrackingLiveData
對(duì)象; - 維護(hù)一個(gè)裝載
LiveData
對(duì)象的set集合。
總結(jié):
- room會(huì)根據(jù)開發(fā)者定義的dataBae和各個(gè)dao類自動(dòng)創(chuàng)建各自的對(duì)應(yīng)的實(shí)體類;
- DAO_Impl的實(shí)體方法會(huì)委托InvalidationTracker類創(chuàng)建需要返回的LiveData對(duì)象,并將數(shù)據(jù)庫操作方法以參數(shù)的形式向下傳遞。
- InvalidationTracker類委托InvalidationLiveDataContainer類創(chuàng)建RoomTrackingLiveData對(duì)象。自此LiveData對(duì)象創(chuàng)建成功。
1.2 RoomTrackingLiveData有何作用
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@Overridepublic void run() {// 向InvalidationTracker注冊(cè)一個(gè)觀察者if (mRegisteredObserver.compareAndSet(false, true)) {mDatabase.getInvalidationTracker().addWeakObserver(mObserver);}boolean computed;do {computed = false;// mComputing 初始值為 falseif (mComputing.compareAndSet(false, true)) {// as long as it is invalid, keep computing.try {T value = null;// mInvalid初始值為 true// 此while循環(huán)結(jié)束后,computed == false,mInvalid == falsewhile (mInvalid.compareAndSet(true, false)) {computed = true;try {// 執(zhí)行數(shù)據(jù)庫操作方法,并返回結(jié)果value = mComputeFunction.call();} catch (Exception e) {// 如果SQL語句執(zhí)行有誤,會(huì)非常粗暴的直接報(bào)錯(cuò),// liveData不能將錯(cuò)誤狀態(tài)上報(bào)給開發(fā)者。throw new RuntimeException("Exception while computing database"+ " live data.", e);}}if (computed) {// 向當(dāng)前l(fā)ivedata的觀察者們發(fā)送數(shù)據(jù)庫查詢結(jié)果postValue(value);}} finally {// release compute lockmComputing.set(false);}}} while (computed && mInvalid.get());}};@SuppressWarnings("WeakerAccess")final Runnable mInvalidationRunnable = new Runnable() {@MainThread@Overridepublic void run() {// 當(dāng)前l(fā)ivedata是否有存活的觀察者boolean isActive = hasActiveObservers();// 如果 mRefreshRunnable正在運(yùn)行 mInvalid == true,條件不成立。// 如果 mRefreshRunnable運(yùn)行結(jié)束 mInvalid == false,條件成立,重新開啟任務(wù)。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) {@Overridepublic void onInvalidated(@NonNull Set<String> tables) {ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);}};}@Overrideprotected void onActive() {super.onActive();mContainer.onActive(this);getQueryExecutor().execute(mRefreshRunnable);}@Overrideprotected void onInactive() {super.onInactive();mContainer.onInactive(this);}Executor getQueryExecutor() {if (mInTransaction) {return mDatabase.getTransactionExecutor();} else {return mDatabase.getQueryExecutor();}}
}
- 當(dāng)開發(fā)者向
RoomTrackingLiveData
注冊(cè)了觀察者后(即調(diào)用了livedata.observe
方法),會(huì)調(diào)用onActive方
法,在子線程里執(zhí)行mRefreshRunnable
任務(wù)。 mRefreshRunnable
在初次執(zhí)行時(shí)會(huì)向InvalidationTracker
注冊(cè)一個(gè)觀察者。然后會(huì)根據(jù)SQL語句循環(huán)查詢數(shù)據(jù)庫,并向開發(fā)者返回查詢結(jié)果。
a. SQL語句是通過開發(fā)者在創(chuàng)建DAO層方法的注解自動(dòng)生成的,并以方法入?yún)⒌姆绞阶罱K傳遞給RoomTrackingLiveData
對(duì)象。
b. 這里的循環(huán)不是一直執(zhí)行的。在沒有外界干擾情況下(指循環(huán)條件的值在沒有被其他方法修改的情況),循環(huán)體只會(huì)執(zhí)行一次。- 構(gòu)造函數(shù)里創(chuàng)建了
mObserver
對(duì)象,當(dāng)mObserver
被觸發(fā)時(shí),會(huì)在主線程執(zhí)行mInvalidationRunnable
任務(wù)。 mInvalidationRunnable
會(huì)在子線程里開啟mRefreshRunnable
任務(wù),重新查詢數(shù)據(jù)庫,并返回?cái)?shù)據(jù)。
總結(jié):
RoomTrackingLiveData
有三個(gè)比較重要的任務(wù):mRefreshRunnable
、mInvalidationRunnable
和mObserver
。mRefreshRunnable
主要負(fù)責(zé)向數(shù)據(jù)庫查詢數(shù)據(jù),并將結(jié)果返回給開發(fā)者注冊(cè)的觀察者。mObserver
負(fù)責(zé)喚醒mInvalidationRunnable
。mInvalidationRunnable
任務(wù)分兩種情況:- 當(dāng)
mRefreshRunnable
還在運(yùn)行時(shí),會(huì)要求mRefreshRunnable
再執(zhí)行一次數(shù)據(jù)庫查詢?nèi)蝿?wù),并按要求將結(jié)果上報(bào)。(這個(gè)邏輯是在mRefreshRunnable
里實(shí)現(xiàn)的。) - 當(dāng)
mRefreshRunnable
停止運(yùn)行時(shí),會(huì)在子線程里重新開啟mRefreshRunnable
任務(wù)。
- 當(dāng)
由上可知,room配合livedata使用時(shí),之所以livedata能夠自動(dòng)感知數(shù)據(jù)庫數(shù)據(jù)變化,是由
mObserver
、mInvalidationRunnable
、mRefreshRunnable
三方共同配合的結(jié)果。
1.3 數(shù)據(jù)庫變化時(shí),是如何通知RoomTrackingLiveData
由上文可以推斷出,當(dāng)數(shù)據(jù)庫發(fā)生變化時(shí),是通過mObserver
來啟動(dòng)數(shù)據(jù)庫查詢?nèi)蝿?wù),并將結(jié)果通過RoomTrackingLiveData#postValue
方法傳遞給訂閱者。接下來就要研究一下mObserver
的調(diào)用鏈。
// RoomTrackingLiveData.javafinal Runnable mRefreshRunnable = new Runnable() {@WorkerThread@Overridepublic void run() {// 1. 向InvalidationTracker注冊(cè)一個(gè)觀察者if (mRegisteredObserver.compareAndSet(false, true)) {mDatabase.getInvalidationTracker().addWeakObserver(mObserver);}....}};
// InvalidationTracker.javapublic void addWeakObserver(Observer observer) {// 2addObserver(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
創(chuàng)建mObserver
對(duì)象,并一步步將mObserver
進(jìn)行包裝,并存放在InvalidationTracker
的mObserverMap
中。- 接下來則需要調(diào)查源碼里在哪些情況下會(huì)遍歷
mObserverMap
,并去調(diào)用mObserverMap
里item
的方法。
// InvalidationTracker.javaRunnable mRefreshRunnable = new Runnable() {@Overridepublic void run() {......if (invalidatedTableIds != null && !invalidatedTableIds.isEmpty()) {synchronized (mObserverMap) {// 1. 遍歷了 mObserverMapfor (Map.Entry<Observer, ObserverWrapper> entry : mObserverMap) {entry.getValue().notifyByTableInvalidStatus(invalidatedTableIds);}}}}......
};public void notifyObserversByTableNames(String... tables) {synchronized (mObserverMap) {// 2. 遍歷了 mObserverMapfor (Map.Entry<Observer, ObserverWrapper> entry : mObserverMap) {if (!entry.getKey().isRemote()) {entry.getValue().notifyByTableNames(tables);}}}
}
由源碼可知,共有兩處遍歷了mObserverMap
,我們先研究一下mRefreshRunnable
的調(diào)用鏈。
/*** 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();}// 啟動(dòng) mRefreshRunnable 任務(wù)mDatabase.getQueryExecutor().execute(mRefreshRunnable);}
}
- 從方法說明上可以看出,當(dāng)
RoomDatabase#endTransaction()
被調(diào)用時(shí),會(huì)啟動(dòng)mRefreshRunnable
任務(wù)。繼續(xù)跟蹤refreshVersionsAsync
的調(diào)用鏈也能發(fā)現(xiàn)這點(diǎn)。 - 接下來讓我們回頭研究一下room框架自動(dòng)為開發(fā)者定義的dao類自動(dòng)生成的
xxxDAO_Impl.java
。仔細(xì)研究一下各個(gè)方法的實(shí)現(xiàn)會(huì)發(fā)現(xiàn),只要涉及到對(duì)數(shù)據(jù)庫進(jìn)行增、刪、改的操作,都會(huì)調(diào)用到__db.endTransaction()
。這里的__db
就是RoomDatabase
的對(duì)象。例如:
@Overridepublic void insert(final EntityNote note) {__db.assertNotSuspendingTransaction();__db.beginTransaction();try {__insertionAdapterOfEntityNote.insert(note);__db.setTransactionSuccessful();} finally {__db.endTransaction();}}@Overridepublic int delete(final EntityNote note) {__db.assertNotSuspendingTransaction();int _total = 0;__db.beginTransaction();try {_total +=__deletionAdapterOfEntityNote.handle(note);__db.setTransactionSuccessful();return _total;} finally {__db.endTransaction();}}@Overridepublic 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()中調(diào)用internalEndTransaction()
public void endTransaction() {if (mAutoCloser == null) {internalEndTransaction();} else {mAutoCloser.executeRefCountingFunction(db -> {internalEndTransaction();return null;});}}
1.3.2 mInvalidationTracker.refreshVersionsAsync()
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)
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);}}
Runnable mRefreshRunnable = new Runnable() {@Overridepublic void run() {final Lock closeLock = mDatabase.getCloseLock();Set<Integer> invalidatedTableIds = null;closeLock.lock();try {if (!ensureInitialization()) {return;}if (!mPendingRefresh.compareAndSet(true, false)) {// no pending refreshreturn;}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 TryFinallyCanBeTryWithResourcestry {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)
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 observerinvalidatedTables = 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)
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) {@Overridepublic void onInvalidated(@NonNull Set<String> tables) {ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);}};}
1.3.6 ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable)
final Runnable mInvalidationRunnable = new Runnable() {@MainThread@Overridepublic void run() {boolean isActive = hasActiveObservers();if (mInvalid.compareAndSet(false, true)) {if (isActive) {getQueryExecutor().execute(mRefreshRunnable);}}}}
?1.3.7 mRefreshRunnable.run ?postValue(value)
final Runnable mRefreshRunnable = new Runnable() {@WorkerThread@Overridepublic void run() {// 向InvalidationTracker注冊(cè)一個(gè)觀察者if (mRegisteredObserver.compareAndSet(false, true)) {mDatabase.getInvalidationTracker().addWeakObserver(mObserver);}boolean computed;do {computed = false;// mComputing 初始值為 falseif (mComputing.compareAndSet(false, true)) {// as long as it is invalid, keep computing.try {T value = null;// mInvalid初始值為 true// 此while循環(huán)結(jié)束后,computed == false,mInvalid == falsewhile (mInvalid.compareAndSet(true, false)) {computed = true;try {// 執(zhí)行數(shù)據(jù)庫操作方法,并返回結(jié)果value = mComputeFunction.call();} catch (Exception e) {// 如果SQL語句執(zhí)行有誤,會(huì)非常粗暴的直接報(bào)錯(cuò),// liveData不能將錯(cuò)誤狀態(tài)上報(bào)給開發(fā)者。throw new RuntimeException("Exception while computing database"+ " live data.", e);}}if (computed) {// 向當(dāng)前l(fā)ivedata的觀察者們發(fā)送數(shù)據(jù)庫查詢結(jié)果postValue(value);}} finally {// release compute lockmComputing.set(false);}}} while (computed && mInvalid.get());}};
1.3.8 observer收到onChanged回調(diào)? ? ?? ?
LiveData<List<EntityNote>> EntityNoteLiveData = AppDatabase.getInstance().noteDao().getAll()注冊(cè)的RoomLiveData的observer會(huì)回調(diào)onChanged
// 繼承AndroidViewModel,帶有Application環(huán)境
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;@Overridepublic 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不觸發(fā)onChangedmLastEntityNoteList = 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 總結(jié):
- 數(shù)據(jù)庫的增、刪、改操作會(huì)調(diào)用
RoomDatabase#endTransaction()
; RoomDatabase#endTransaction()
會(huì)調(diào)用InvalidationTracker#refreshVersionsAsync()
;refreshVersionsAsync()
會(huì)開啟mRefreshRunnable
任務(wù)。mRefreshRunnable
里會(huì)遍歷mObserverMap
,并挨個(gè)調(diào)用其item
的指定方法。RoomTrackingLiveData
在構(gòu)造函數(shù)里創(chuàng)建了mObserver
對(duì)象,并將此對(duì)象放置于InvalidationTracker
的mObserverMap
中。且此對(duì)象的方法就是用來喚醒RoomTrackingLiveData
的mRefreshRunnable
任務(wù)。還記得這個(gè)任務(wù)是干嘛的嗎?這個(gè)任務(wù)就是根據(jù)RoomTrackingLiveData
持有的數(shù)據(jù)庫查詢語句向數(shù)據(jù)庫查詢數(shù)據(jù),并將查詢結(jié)果上報(bào)給開發(fā)者指定的Observer
。
至此,RoomTrackingLiveData完美實(shí)現(xiàn)了數(shù)據(jù)庫發(fā)生變化時(shí),會(huì)主動(dòng)將新的數(shù)據(jù)上報(bào)給開發(fā)者的功能。
二? Room是如何通知其他進(jìn)程的訂閱者
如果有兩個(gè)進(jìn)程同時(shí)關(guān)聯(lián)了同一個(gè)數(shù)據(jù)庫,如果一個(gè)進(jìn)程對(duì)此數(shù)據(jù)庫的數(shù)據(jù)進(jìn)行改變,那么另一個(gè)進(jìn)程的RoomTrackingLiveData
依舊能感知到數(shù)據(jù)變化,這是怎么做到的呢?
還記得上面在調(diào)查InvalidationTracker
的mObserverMap
時(shí),發(fā)現(xiàn)有兩個(gè)方法遍歷了這個(gè)map
嗎。其中mRefreshRunnable
已經(jīng)分析過了,接下來分析另一個(gè)方法notifyObserversByTableNames
。
// InvalidationTracker.javapublic void notifyObserversByTableNames(String... tables) {synchronized (mObserverMap) {// 遍歷了 mObserverMapfor (Map.Entry<Observer, ObserverWrapper> entry : mObserverMap) {if (!entry.getKey().isRemote()) {entry.getValue().notifyByTableNames(tables);}}}
}
// MultiInstanceInvalidationClient.javafinal IMultiInstanceInvalidationCallback mCallback =new IMultiInstanceInvalidationCallback.Stub() {@Overridepublic void onInvalidation(final String[] tables) {mExecutor.execute(new Runnable() {@Overridepublic void run() {//1.調(diào)用了 nvalidationTracker#notifyObserversByTableNames()mInvalidationTracker.notifyObserversByTableNames(tables);}});}};final Runnable mSetUpRunnable = new Runnable() {@Overridepublic void run() {try {final IMultiInstanceInvalidationService service = mService;if (service != null) {//2. 向 service 注冊(cè) mCallbackmClientId = 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() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mService = IMultiInstanceInvalidationService.Stub.asInterface(service);// 3. 執(zhí)行 mSetUpRunnable 任務(wù) mExecutor.execute(mSetUpRunnable);}@Overridepublic void onServiceDisconnected(ComponentName name) {mExecutor.execute(mRemoveObserverRunnable);mService = null;}};
- 由上可見,在
MultiInstanceInvalidationClient
類里綁定了一個(gè)service
,并向service
注冊(cè)mCallback
。這個(gè)mCallback
會(huì)通過InvalidationTracker#notifyObserversByTableNames()
通知RoomTrackingLiveData
該干活了(查詢和上報(bào)數(shù)據(jù)庫新值)。
看到
IMultiInstanceInvalidationService.Stub
可以大膽猜測這里涉及到了跨進(jìn)程通信。
接下來研究MultiInstanceInvalidationService
// MultiInstanceInvalidationService.javapublic class MultiInstanceInvalidationService extends Service {int mMaxClientId = 0;final HashMap<Integer, String> mClientNames = new HashMap<>();// 1. 可以理解成這是一個(gè)裝載 callBack的集合final RemoteCallbackList<IMultiInstanceInvalidationCallback> mCallbackList =new RemoteCallbackList<IMultiInstanceInvalidationCallback>() {@Overridepublic void onCallbackDied(IMultiInstanceInvalidationCallback callback,Object cookie) {mClientNames.remove((int) cookie);}};private final IMultiInstanceInvalidationService.Stub mBinder =new IMultiInstanceInvalidationService.Stub() {@Overridepublic 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;}}}@Overridepublic void unregisterCallback(IMultiInstanceInvalidationCallback callback,int clientId) {synchronized (mCallbackList) {mCallbackList.unregister(callback);mClientNames.remove(clientId);}}@Overridepublic 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 {// 這個(gè)for循環(huán),可以理解成取出mCallbackList集合中的所有callBack,// 并調(diào)用各自的 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@Overridepublic IBinder onBind(@NonNull Intent intent) {return mBinder;}
}
- 由以上源碼可以推斷出這個(gè)
service
主要做了兩件事:- 在內(nèi)存中維護(hù)一個(gè)集合,這個(gè)集合裝載的是所有
client
注冊(cè)的callBack
; - 在合適的時(shí)機(jī)調(diào)用所有
client
注冊(cè)的callBack
。這個(gè)合適的時(shí)機(jī),就是調(diào)用broadcastInvalidation()
的時(shí)候。
- 在內(nèi)存中維護(hù)一個(gè)集合,這個(gè)集合裝載的是所有
回到MultiInstanceInvalidationClient
,回想一下這個(gè)client
向service
注冊(cè)了個(gè)什么玩意。
// MultiInstanceInvalidationClient.javafinal Runnable mSetUpRunnable = new Runnable() {@Overridepublic void run() {try {final IMultiInstanceInvalidationService service = mService;if (service != null) {// 1. 向service注冊(cè)mCallbackmClientId = 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() {@Overridepublic void onInvalidation(final String[] tables) {mExecutor.execute(new Runnable() {@Overridepublic void run() {// 2. 這個(gè)方法是干什么的來著?// 是拜托InvalidationTracker通知RoomTrackingLiveData該干活了。// 上文有介紹mInvalidationTracker.notifyObserversByTableNames(tables);}});}};
接下來追蹤一下MultiInstanceInvalidationService#broadcastInvalidation()
// MultiInstanceInvalidationClient.javaMultiInstanceInvalidationClient(Context context, String name, Intent serviceIntent,InvalidationTracker invalidationTracker, Executor executor) {......mObserver = new InvalidationTracker.Observer(tableNames.toArray(new String[0])) {@Overridepublic void onInvalidated(@NonNull Set<String> tables) {if (mStopped.get()) {return;}try {final IMultiInstanceInvalidationService service = mService;if (service != null) {// 1. 調(diào)用了MultiInstanceInvalidationService#broadcastInvalidation()service.broadcastInvalidation(mClientId, tables.toArray(new String[0]));}} catch (RemoteException e) {Log.w(Room.LOG_TAG, "Cannot broadcast invalidation", e);}}@Overrideboolean isRemote() {return true;}};mAppContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
}final Runnable mSetUpRunnable = new Runnable() {@Overridepublic void run() {try {final IMultiInstanceInvalidationService service = mService;if (service != null) {mClientId = service.registerCallback(mCallback, mName);// 2. 將mObserver傳遞給InvalidationTrackermInvalidationTracker.addObserver(mObserver);}} catch (RemoteException e) {Log.w(Room.LOG_TAG, "Cannot register multi-instance invalidation callback", e);}}
};
看了以上2個(gè)步驟是不是似曾相識(shí)?還記得RoomTrackingLiveData
的mObserver
對(duì)象嗎?和這里的套路是一模一樣。接下來很明顯,InvalidationTracker
里面會(huì)有一個(gè)map
來裝載這個(gè)mObserver
。然后會(huì)有兩個(gè)方法去遍歷這個(gè)map
。其中一個(gè)Runnable
方法會(huì)在調(diào)用數(shù)據(jù)庫的增刪改方法時(shí)觸發(fā),另一個(gè)方法notifyObserversByTableNames
會(huì)在...會(huì)在...???
我不是在研究notifyObserversByTableNames的調(diào)用鏈嗎?怎么又繞回來了?
這里理解起來有點(diǎn)繞,先明確一下前提:
- 針對(duì)不同的進(jìn)程操作同一個(gè)數(shù)據(jù)庫的場景,其實(shí)每一個(gè)進(jìn)程都會(huì)擁有自己獨(dú)立的
RoomDatabase
實(shí)例。相應(yīng)的MultiInstanceInvalidationClient
、InvalidationTracker
、RoomTrackingLiveData
都是相互獨(dú)立的。- 只有
MultiInstanceInvalidationService
是共同的實(shí)例。而這個(gè)共同的實(shí)例,是保證不同進(jìn)程能相互感知到數(shù)據(jù)庫操作的關(guān)鍵。InvalidationTracker
的mRefreshRunnable
是在單進(jìn)程中調(diào)用的。InvalidationTracker
的notifyObserversByTableNames
是用于跨進(jìn)程調(diào)用的。
下面重新捋一下思路。首先假設(shè)現(xiàn)在有兩個(gè)進(jìn)程會(huì)操作同一個(gè)數(shù)據(jù)庫。那么這兩個(gè)進(jìn)程都會(huì)各自擁有一套自己的獨(dú)立對(duì)象。即都會(huì)做一下事情:
- 創(chuàng)建
RoomTrackingLiveData
對(duì)象,并將mObserver
委托給InvalidationTracker
管理。 RoomTrackingLiveData
里的mRefreshRunnable
會(huì)在被喚醒時(shí)重新查詢數(shù)據(jù)庫,并上報(bào)結(jié)果。- 創(chuàng)建
MultiInstanceInvalidationClient
對(duì)象,并與唯一的MultiInstanceInvalidationService
進(jìn)行綁定,并將callBack
委托給service
管理。 callBack
里會(huì)調(diào)用InvalidationTracker#notifyObserversByTableNames()
。MultiInstanceInvalidationClient
對(duì)象將mObserver
委托給InvalidationTracker
管理。MultiInstanceInvalidationClient
的mObserver
會(huì)通知所有與MultiInstanceInvalidationService
進(jìn)行綁定的MultiInstanceInvalidationClient
,告知它們數(shù)據(jù)庫有變化。
針對(duì)進(jìn)程1,我們重點(diǎn)關(guān)注3、4、5、6。針對(duì)進(jìn)程2,我們重點(diǎn)關(guān)注1、2。現(xiàn)在開始發(fā)車:
- 當(dāng)前用戶在進(jìn)程1操作了數(shù)據(jù)庫的修改操作,那么就會(huì)觸發(fā)進(jìn)程1的
RoomDatabase#endTransaction()
,
進(jìn)而觸發(fā)了InvalidationTracker#mRefreshRunnable
任務(wù),遍歷InvalidationTracker#mObserverMap
(在上一節(jié)有相關(guān)介紹)。此mObserverMap
里存在一個(gè)MultiInstanceInvalidationClient
添加進(jìn)來的mObserver
(上面第5點(diǎn)有提到)。 - 進(jìn)程1的
MultiInstanceInvalidationClient
的mObserver
會(huì)調(diào)用MultiInstanceInvalidationService#broadcastInvalidation()
。 MultiInstanceInvalidationService
會(huì)遍歷和執(zhí)行所有MultiInstanceInvalidationClient
注冊(cè)的callback
。這其中的一個(gè)callback
就是進(jìn)程2的MultiInstanceInvalidationClient
注冊(cè)的(上面第5點(diǎn)有提到)。- 進(jìn)程2的
callback
會(huì)調(diào)用進(jìn)程2的InvalidationTracker#notifyObserversByTableNames()
。再回憶一下這個(gè)notifyObserversByTableNames()
是干嘛的?沒錯(cuò),就是我們研究的第二個(gè)遍歷InvalidationTracker
的mObserverMap
的方法。 - 既然進(jìn)程2已經(jīng)遍歷了
mObserverMap
,那么勢必會(huì)讓進(jìn)程2的RoomTrackingLiveData
干活(查詢數(shù)據(jù)庫,上報(bào)新數(shù)據(jù))。
至此,room框架完成了一次完美的跨進(jìn)程通訊。
要想當(dāng)前的RoomDataBase
具有跨進(jìn)程通訊的能力,需要在構(gòu)建databaseBuilder
的時(shí)候調(diào)用enableMultiInstanceInvalidation()
。例如:
@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;//創(chuàng)建單例public static synchronized AppDatabase getInstance() {if (instance == null) {instance = create();}return instance;}/*** 創(chuàng)建數(shù)據(jù)庫*/private static AppDatabase create() {return Room.databaseBuilder(MyApplication.getInstance(), AppDatabase.class, DB_NAME).allowMainThreadQueries().fallbackToDestructiveMigration().enableMultiInstanceInvalidation() // 跨進(jìn)程通訊的能力.build();}public abstract NoteDao noteDao();
}
從源碼來看,RoomDataBase
正是通過此方法來間接創(chuàng)建MultiInstanceInvalidationClient
對(duì)象,并與MultiInstanceInvalidationService
建立綁定關(guān)系。