外贸公司应该怎样做外贸网站,市场营销方案,学科网站建设标准,西乡县门户网站一 Room数据变化LiveData如何收到onChanged回调的#xff1f;
1.1 LiveData是如何创建的 这里讨论的LiveData的创建是特指Dao定义的方法的返回类型#xff0c;而不是所有的LiveData。 以NoteDao 举例#xff1a;
Dao
public interface NoteDao {Query(select * fr…一 Room数据变化LiveData如何收到onChanged回调的
1.1 LiveData是如何创建的 这里讨论的LiveData的创建是特指Dao定义的方法的返回类型而不是所有的LiveData。 以NoteDao 举例
Dao
public interface NoteDao {Query(select * from note)LiveDataListEntityNote getAll();Updateint update(EntityNote note);Deleteint delete(EntityNote note);Insertvoid insert(EntityNote note);
}Room会通过APT自动为NoteDao 创建实体类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 EntityInsertionAdapterEntityNote __insertionAdapterOfEntityNote;private final EntityDeletionOrUpdateAdapterEntityNote __deletionAdapterOfEntityNote;private final EntityDeletionOrUpdateAdapterEntityNote __updateAdapterOfEntityNote;public NoteDao_Impl(RoomDatabase __db) {this.__db __db;this.__insertionAdapterOfEntityNote new EntityInsertionAdapterEntityNote(__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 EntityDeletionOrUpdateAdapterEntityNote(__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 EntityDeletionOrUpdateAdapterEntityNote(__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 LiveDataListEntityNote getAll() {final String _sql select * from note;final RoomSQLiteQuery _statement RoomSQLiteQuery.acquire(_sql, 0);return __db.getInvalidationTracker().createLiveData(new String[]{note}, false, new CallableListEntityNote() {Overridepublic ListEntityNote 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 ListEntityNote _result new ArrayListEntityNote(_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 ListClass? getRequiredConverters() {return Collections.emptyList();}
}通过InvalidationTracker#createLiveData方法创建需要返回的LiveData对象。
// InvalidationTracker.javapublic T LiveDataT createLiveData(String[] tableNames, CallableT computeFunction) {return createLiveData(tableNames, false, computeFunction);
}public T LiveDataT createLiveData(String[] tableNames, boolean inTransaction,CallableT computeFunction) {return mInvalidationLiveDataContainer.create(validateAndResolveTableNames(tableNames), inTransaction, computeFunction);
}
// InvalidationLiveDataContainer.javaT LiveDataT create(String[] tableNames, boolean inTransaction,CallableT 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有何作用
class RoomTrackingLiveDataT extends LiveDataT {final RoomDatabase mDatabase;final boolean mInTransaction;final CallableT 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() {WorkerThreadOverridepublic void run() {// 向InvalidationTracker注册一个观察者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循环结束后computed falsemInvalid falsewhile (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 lockmComputing.set(false);}}} while (computed mInvalid.get());}};SuppressWarnings(WeakerAccess)final Runnable mInvalidationRunnable new Runnable() {MainThreadOverridepublic 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,CallableT computeFunction,String[] tableNames) {mDatabase database;mInTransaction inTransaction;mComputeFunction computeFunction;mContainer container;mObserver new InvalidationTracker.Observer(tableNames) {Overridepublic void onInvalidated(NonNull SetString 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();}}
}
当开发者向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的调用链。 // RoomTrackingLiveData.javafinal Runnable mRefreshRunnable new Runnable() {WorkerThreadOverridepublic void run() {// 1. 向InvalidationTracker注册一个观察者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创建mObserver对象并一步步将mObserver进行包装并存放在InvalidationTracker的mObserverMap中。接下来则需要调查源码里在哪些情况下会遍历mObserverMap并去调用mObserverMap里item的方法。
// InvalidationTracker.javaRunnable mRefreshRunnable new Runnable() {Overridepublic void run() {......if (invalidatedTableIds ! null !invalidatedTableIds.isEmpty()) {synchronized (mObserverMap) {// 1. 遍历了 mObserverMapfor (Map.EntryObserver, ObserverWrapper entry : mObserverMap) {entry.getValue().notifyByTableInvalidStatus(invalidatedTableIds);}}}}......
};public void notifyObserversByTableNames(String... tables) {synchronized (mObserverMap) {// 2. 遍历了 mObserverMapfor (Map.EntryObserver, ObserverWrapper entry : mObserverMap) {if (!entry.getKey().isRemote()) {entry.getValue().notifyByTableNames(tables);}}}
}
由源码可知共有两处遍历了mObserverMap我们先研究一下mRefreshRunnable的调用链。
/*** 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的对象。例如 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()中调用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 cant be closed here, but we need to be sure that our// db isnt 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();SetInteger 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.EntryObserver, ObserverWrapper entry : mObserverMap) {entry.getValue().notifyByTableInvalidStatus(invalidatedTableIds);}}}}private SetInteger checkUpdatedTable() {HashSetInteger 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(SetInteger invalidatedTablesIds) {SetString 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,CallableT computeFunction,String[] tableNames) {mDatabase database;mInTransaction inTransaction;mComputeFunction computeFunction;mContainer container;mObserver new InvalidationTracker.Observer(tableNames) {Overridepublic void onInvalidated(NonNull SetString tables) {ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);}};}
1.3.6 ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable) final Runnable mInvalidationRunnable new Runnable() {MainThreadOverridepublic 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() {WorkerThreadOverridepublic void run() {// 向InvalidationTracker注册一个观察者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循环结束后computed falsemInvalid falsewhile (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 lockmComputing.set(false);}}} while (computed mInvalid.get());}};1.3.8 observer收到onChanged回调
LiveDataListEntityNote EntityNoteLiveData AppDatabase.getInstance().noteDao().getAll()注册的RoomLiveData的observer会回调onChanged
// 继承AndroidViewModel,带有Application环境
public class NoteViewModel extends AndroidViewModel {private MediatorLiveDataListEntityNote mMediatorLiveData;public NoteViewModel(NonNull Application application) {super(application);mMediatorLiveData new MediatorLiveData();LiveDataListEntityNote EntityNoteLiveData AppDatabase.getInstance().noteDao().getAll();mMediatorLiveData.addSource(EntityNoteLiveData, new ObserverListEntityNote() {private ListEntityNote mLastEntityNoteList;Overridepublic void onChanged(ListEntityNote 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不触发onChangedmLastEntityNoteList entityNotes;}private void setValue(ListEntityNote 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 MediatorLiveDataListEntityNote 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。
// InvalidationTracker.javapublic void notifyObserversByTableNames(String... tables) {synchronized (mObserverMap) {// 遍历了 mObserverMapfor (Map.EntryObserver, 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.调用了 nvalidationTracker#notifyObserversByTableNames()mInvalidationTracker.notifyObserversByTableNames(tables);}});}};final Runnable mSetUpRunnable new Runnable() {Overridepublic void run() {try {final IMultiInstanceInvalidationService service mService;if (service ! null) {//2. 向 service 注册 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. 执行 mSetUpRunnable 任务 mExecutor.execute(mSetUpRunnable);}Overridepublic void onServiceDisconnected(ComponentName name) {mExecutor.execute(mRemoveObserverRunnable);mService null;}};
由上可见在MultiInstanceInvalidationClient类里绑定了一个service并向service注册mCallback。这个mCallback会通过InvalidationTracker#notifyObserversByTableNames()通知RoomTrackingLiveData该干活了查询和上报数据库新值。 看到IMultiInstanceInvalidationService.Stub可以大胆猜测这里涉及到了跨进程通信。 接下来研究MultiInstanceInvalidationService // MultiInstanceInvalidationService.javapublic class MultiInstanceInvalidationService extends Service {int mMaxClientId 0;final HashMapInteger, String mClientNames new HashMap();// 1. 可以理解成这是一个装载 callBack的集合final RemoteCallbackListIMultiInstanceInvalidationCallback mCallbackList new RemoteCallbackListIMultiInstanceInvalidationCallback() {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 {// 这个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();}}}};NullableOverridepublic IBinder onBind(NonNull Intent intent) {return mBinder;}
}
由以上源码可以推断出这个service主要做了两件事 在内存中维护一个集合这个集合装载的是所有client注册的callBack在合适的时机调用所有client注册的callBack。这个合适的时机就是调用broadcastInvalidation的时候。
回到MultiInstanceInvalidationClient回想一下这个client向service注册了个什么玩意。 // MultiInstanceInvalidationClient.javafinal Runnable mSetUpRunnable new Runnable() {Overridepublic void run() {try {final IMultiInstanceInvalidationService service mService;if (service ! null) {// 1. 向service注册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. 这个方法是干什么的来着// 是拜托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 SetString 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);}}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个步骤是不是似曾相识还记得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()。例如
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建立绑定关系。
文章转载自: http://www.morning.ysbhj.cn.gov.cn.ysbhj.cn http://www.morning.kyflr.cn.gov.cn.kyflr.cn http://www.morning.rflcy.cn.gov.cn.rflcy.cn http://www.morning.jpmcb.cn.gov.cn.jpmcb.cn http://www.morning.cgdyx.cn.gov.cn.cgdyx.cn http://www.morning.yrdkl.cn.gov.cn.yrdkl.cn http://www.morning.sqfrg.cn.gov.cn.sqfrg.cn http://www.morning.kxltf.cn.gov.cn.kxltf.cn http://www.morning.rsszk.cn.gov.cn.rsszk.cn http://www.morning.rcwbc.cn.gov.cn.rcwbc.cn http://www.morning.yjmns.cn.gov.cn.yjmns.cn http://www.morning.lnsnyc.com.gov.cn.lnsnyc.com http://www.morning.pjwfs.cn.gov.cn.pjwfs.cn http://www.morning.qyqmj.cn.gov.cn.qyqmj.cn http://www.morning.tzrmp.cn.gov.cn.tzrmp.cn http://www.morning.xrtsx.cn.gov.cn.xrtsx.cn http://www.morning.zqcdl.cn.gov.cn.zqcdl.cn http://www.morning.mgtrc.cn.gov.cn.mgtrc.cn http://www.morning.hfrbt.cn.gov.cn.hfrbt.cn http://www.morning.dytqf.cn.gov.cn.dytqf.cn http://www.morning.syfty.cn.gov.cn.syfty.cn http://www.morning.xdqrz.cn.gov.cn.xdqrz.cn http://www.morning.nshhf.cn.gov.cn.nshhf.cn http://www.morning.nfcxq.cn.gov.cn.nfcxq.cn http://www.morning.lstmq.cn.gov.cn.lstmq.cn http://www.morning.tlpsd.cn.gov.cn.tlpsd.cn http://www.morning.rjmb.cn.gov.cn.rjmb.cn http://www.morning.rhkmn.cn.gov.cn.rhkmn.cn http://www.morning.jrkzk.cn.gov.cn.jrkzk.cn http://www.morning.yybcx.cn.gov.cn.yybcx.cn http://www.morning.sjwiki.com.gov.cn.sjwiki.com http://www.morning.hcqpc.cn.gov.cn.hcqpc.cn http://www.morning.pjxlg.cn.gov.cn.pjxlg.cn http://www.morning.kkrnm.cn.gov.cn.kkrnm.cn http://www.morning.thlzt.cn.gov.cn.thlzt.cn http://www.morning.spfh.cn.gov.cn.spfh.cn http://www.morning.fesiy.com.gov.cn.fesiy.com http://www.morning.wnnfh.cn.gov.cn.wnnfh.cn http://www.morning.rglzy.cn.gov.cn.rglzy.cn http://www.morning.skwwj.cn.gov.cn.skwwj.cn http://www.morning.tqpnf.cn.gov.cn.tqpnf.cn http://www.morning.prqdr.cn.gov.cn.prqdr.cn http://www.morning.clpdm.cn.gov.cn.clpdm.cn http://www.morning.rdxp.cn.gov.cn.rdxp.cn http://www.morning.jcxyq.cn.gov.cn.jcxyq.cn http://www.morning.dwrbn.cn.gov.cn.dwrbn.cn http://www.morning.pmrlt.cn.gov.cn.pmrlt.cn http://www.morning.sggzr.cn.gov.cn.sggzr.cn http://www.morning.mhnxs.cn.gov.cn.mhnxs.cn http://www.morning.dxgt.cn.gov.cn.dxgt.cn http://www.morning.bhwll.cn.gov.cn.bhwll.cn http://www.morning.rxdsq.cn.gov.cn.rxdsq.cn http://www.morning.cfqyx.cn.gov.cn.cfqyx.cn http://www.morning.bgdk.cn.gov.cn.bgdk.cn http://www.morning.kxqfz.cn.gov.cn.kxqfz.cn http://www.morning.fmrrr.cn.gov.cn.fmrrr.cn http://www.morning.qmbpy.cn.gov.cn.qmbpy.cn http://www.morning.yrkdq.cn.gov.cn.yrkdq.cn http://www.morning.skrh.cn.gov.cn.skrh.cn http://www.morning.kjnfs.cn.gov.cn.kjnfs.cn http://www.morning.nyqzz.cn.gov.cn.nyqzz.cn http://www.morning.sbczr.cn.gov.cn.sbczr.cn http://www.morning.27asw.cn.gov.cn.27asw.cn http://www.morning.cjqcx.cn.gov.cn.cjqcx.cn http://www.morning.gbyng.cn.gov.cn.gbyng.cn http://www.morning.lfcnj.cn.gov.cn.lfcnj.cn http://www.morning.rxrw.cn.gov.cn.rxrw.cn http://www.morning.kkgbs.cn.gov.cn.kkgbs.cn http://www.morning.lwnwl.cn.gov.cn.lwnwl.cn http://www.morning.xhsxj.cn.gov.cn.xhsxj.cn http://www.morning.ybgpk.cn.gov.cn.ybgpk.cn http://www.morning.zsyrk.cn.gov.cn.zsyrk.cn http://www.morning.gwtgt.cn.gov.cn.gwtgt.cn http://www.morning.bsrcr.cn.gov.cn.bsrcr.cn http://www.morning.hwcln.cn.gov.cn.hwcln.cn http://www.morning.qwdlj.cn.gov.cn.qwdlj.cn http://www.morning.zplzj.cn.gov.cn.zplzj.cn http://www.morning.tpkxs.cn.gov.cn.tpkxs.cn http://www.morning.srbl.cn.gov.cn.srbl.cn http://www.morning.roymf.cn.gov.cn.roymf.cn