LiveData是如何感知Room數(shù)據(jù)變化的

LiveData是如何感知Room數(shù)據(jù)變化的

LiveData是如何創(chuàng)建的

這里討論的LiveData的創(chuàng)建是特指Dao定義的方法的返回類型,而不是所有的LiveData。

PlaybackHistoryRecordDAO 舉例:

@Dao
interface PlaybackHistoryRecordDAO {
    ......
    @Query("SELECT * FROM history_record where sound_type = :soundType ORDER BY update_time DESC")
    fun getAllHistoryRecords(soundType: Int): LiveData<List<PlaybackHistoryRecord?>?>?
    ......
}

room會通過APT自動為PlaybackHistoryRecordDAO創(chuàng)建實(shí)體類PlaybackHistoryRecordDAO_Impl.java

public final class PlaybackHistoryRecordDAO_Impl implements PlaybackHistoryRecordDAO {
  private final RoomDatabase __db;
  ......
  @Override
  public LiveData<List<PlaybackHistoryRecord>> getAllHistoryRecords(final int soundType) {
    final String _sql = "SELECT * FROM history_record where sound_type = ? ORDER BY update_time DESC";
    final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
    int _argIndex = 1;
    _statement.bindLong(_argIndex, soundType);
    return __db.getInvalidationTracker().createLiveData(new String[]{"history_record"}, false, new Callable<List<PlaybackHistoryRecord>>() {
      @Override
      public List<PlaybackHistoryRecord> call() throws Exception {
        final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
        try {
          // 根據(jù)SQL語句向數(shù)據(jù)庫查詢數(shù)據(jù)
          final List<PlaybackHistoryRecord> _result = new ArrayList<PlaybackHistoryRecord>(_cursor.getCount());
          ......   
          return _result;
        } finally {
          _cursor.close();
        }
      }

      @Override
      protected void finalize() {
        _statement.release();
      }
    });
  }
  ......
}

通過InvalidationTracker#createLiveData方法創(chuàng)建需要返回的LiveData對象。

// 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);
}
// 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對象;
  • 維護(hù)一個裝載LiveData對象的set集合。

總結(jié):

  1. room會根據(jù)開發(fā)者定義的dataBae和各個dao類自動創(chuàng)建各自的對應(yīng)的實(shí)體類;
  2. DAO_Impl的實(shí)體方法會委托InvalidationTracker類創(chuàng)建需要返回的LiveData對象,并將數(shù)據(jù)庫操作方法以參數(shù)的形式向下傳遞。
  3. InvalidationTracker類委托InvalidationLiveDataContainer類創(chuàng)建RoomTrackingLiveData對象。自此LiveData對象創(chuàng)建成功。

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
        @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循環(huán)結(jié)束后,computed == false,mInvalid == false
                        while (mInvalid.compareAndSet(true, false)) {
                            computed = true;
                            try {
                                // 執(zhí)行數(shù)據(jù)庫操作方法,并返回結(jié)果
                                value = mComputeFunction.call();
                            } catch (Exception e) {
                                // 如果SQL語句執(zhí)行有誤,會非常粗暴的直接報(bào)錯,
                                // liveData不能將錯誤狀態(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 lock
                        mComputing.set(false);
                    }
                }
               
            } while (computed && mInvalid.get());
        }
    };

    @SuppressWarnings("WeakerAccess")
    final Runnable mInvalidationRunnable = new Runnable() {
        @MainThread
        @Override
        public 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) {
            @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();
        }
    }
}
  1. 當(dāng)開發(fā)者向RoomTrackingLiveData注冊了觀察者后(即調(diào)用了livedata.observe方法),會調(diào)用onActive方法,在子線程里執(zhí)行mRefreshRunnable任務(wù)。
  2. mRefreshRunnable在初次執(zhí)行時會向InvalidationTracker注冊一個觀察者。然后會根據(jù)SQL語句循環(huán)查詢數(shù)據(jù)庫,并向開發(fā)者返回查詢結(jié)果。
    a. SQL語句是通過開發(fā)者在創(chuàng)建DAO層方法的注解自動生成的,并以方法入?yún)⒌姆绞阶罱K傳遞給RoomTrackingLiveData對象。
    b. 這里的循環(huán)不是一直執(zhí)行的。在沒有外界干擾情況下(指循環(huán)條件的值在沒有被其他方法修改的情況),循環(huán)體只會執(zhí)行一次。
  3. 構(gòu)造函數(shù)里創(chuàng)建了mObserver 對象,當(dāng)mObserver被觸發(fā)時,會在主線程執(zhí)行mInvalidationRunnable任務(wù)。
  4. mInvalidationRunnable會在子線程里開啟mRefreshRunnable任務(wù),重新查詢數(shù)據(jù)庫,并返回?cái)?shù)據(jù)。

總結(jié):

  1. RoomTrackingLiveData有三個比較重要的任務(wù):mRefreshRunnable、mInvalidationRunnablemObserver
  2. mRefreshRunnable主要負(fù)責(zé)向數(shù)據(jù)庫查詢數(shù)據(jù),并將結(jié)果返回給開發(fā)者注冊的觀察者。
  3. mObserver負(fù)責(zé)喚醒mInvalidationRunnable。
  4. mInvalidationRunnable任務(wù)分兩種情況:
    • 當(dāng)mRefreshRunnable還在運(yùn)行時,會要求mRefreshRunnable再執(zhí)行一次數(shù)據(jù)庫查詢?nèi)蝿?wù),并按要求將結(jié)果上報(bào)。(這個邏輯是在mRefreshRunnable里實(shí)現(xiàn)的。)
    • 當(dāng)mRefreshRunnable停止運(yùn)行時,會在子線程里重新開啟mRefreshRunnable任務(wù)。

由上可知,room配合livedata使用時,之所以livedata能夠自動感知數(shù)據(jù)庫數(shù)據(jù)變化,是由mObservermInvalidationRunnable、mRefreshRunnable三方共同配合的結(jié)果。

數(shù)據(jù)庫變化時,是如何通知RoomTrackingLiveData

由上文可以推斷出,當(dāng)數(shù)據(jù)庫發(fā)生變化時,是通過mObserver來啟動數(shù)據(jù)庫查詢?nèi)蝿?wù),并將結(jié)果通過
RoomTrackingLiveData#postValue方法傳遞給訂閱者。接下來就要研究一下mObserver的調(diào)用鏈。

   // RoomTrackingLiveData.java
    final Runnable mRefreshRunnable = new Runnable() {
        @WorkerThread
        @Override
        public void run() {
            // 1. 向InvalidationTracker注冊一個觀察者
            if (mRegisteredObserver.compareAndSet(false, true)) {
                mDatabase.getInvalidationTracker().addWeakObserver(mObserver);
            }
          ....
        }
    };
// 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();     
    }
}
  1. RoomTrackingLiveData創(chuàng)建mObserver對象,并一步步將mObserver進(jìn)行包裝,并存放在InvalidationTrackermObserverMap中。
  2. 接下來則需要調(diào)查源碼里在哪些情況下會遍歷mObserverMap,并去調(diào)用mObserverMapitem的方法。
// 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的調(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();
        }
        // 啟動 mRefreshRunnable 任務(wù)
        mDatabase.getQueryExecutor().execute(mRefreshRunnable);
    }
}
  1. 從方法說明上可以看出,當(dāng)RoomDatabase#endTransaction()被調(diào)用時,會啟動mRefreshRunnable任務(wù)。繼續(xù)跟蹤refreshVersionsAsync的調(diào)用鏈也能發(fā)現(xiàn)這點(diǎn)。
  2. 接下來讓我們回頭研究一下room框架自動為開發(fā)者定義的dao類自動生成的xxxDAO_Impl.java。仔細(xì)研究一下各個方法的實(shí)現(xiàn)會發(fā)現(xiàn),只要涉及到對數(shù)據(jù)庫進(jìn)行增、刪、改的操作,都會調(diào)用到__db.endTransaction()。這里的__db就是RoomDatabase的對象。例如:
// PlaybackHistoryRecordDAO_Impl.java

@Override
public int updateHistoryRecord(final int soundType, final String id, final long updateTime) {
  __db.assertNotSuspendingTransaction();
  final SupportSQLiteStatement _stmt = __preparedStmtOfUpdateHistoryRecord.acquire();
  ......
  try {
    final int _result = _stmt.executeUpdateDelete();
    __db.setTransactionSuccessful();
    return _result;
  } finally {
    // 調(diào)用了 RoomDatabase#endTransaction()
    __db.endTransaction();
    __preparedStmtOfUpdateHistoryRecord.release(_stmt);
  }
}

@Override
public int deleteAllHistoryRecord(final int soundType) {
  __db.assertNotSuspendingTransaction();
  final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteAllHistoryRecord.acquire();
  ......
  try {
    final int _result = _stmt.executeUpdateDelete();
    __db.setTransactionSuccessful();
    return _result;
  } finally {
    // 調(diào)用了 RoomDatabase#endTransaction()
    __db.endTransaction();
    __preparedStmtOfDeleteAllHistoryRecord.release(_stmt);
  }
}

總結(jié):

  1. 數(shù)據(jù)庫的增、刪、改操作會調(diào)用RoomDatabase#endTransaction();
  2. RoomDatabase#endTransaction()會調(diào)用InvalidationTracker#refreshVersionsAsync();
  3. refreshVersionsAsync()會開啟mRefreshRunnable任務(wù)。
  4. mRefreshRunnable里會遍歷mObserverMap,并挨個調(diào)用其item的指定方法。
  5. RoomTrackingLiveData在構(gòu)造函數(shù)里創(chuàng)建了mObserver對象,并將此對象放置于InvalidationTrackermObserverMap中。且此對象的方法就是用來喚醒RoomTrackingLiveDatamRefreshRunnable任務(wù)。還記得這個任務(wù)是干嘛的嗎?這個任務(wù)就是根據(jù)RoomTrackingLiveData持有的數(shù)據(jù)庫查詢語句向數(shù)據(jù)庫查詢數(shù)據(jù),并將查詢結(jié)果上報(bào)給開發(fā)者指定的Observer。

至此,RoomTrackingLiveData完美實(shí)現(xiàn)了數(shù)據(jù)庫發(fā)生變化時,會主動將新的數(shù)據(jù)上報(bào)給開發(fā)者的功能。
[圖片上傳失敗...(image-e3e348-1678850688570)]

Room是如何通知其他進(jìn)程的訂閱者

如果有兩個進(jìn)程同時關(guān)聯(lián)了同一個數(shù)據(jù)庫,如果一個進(jìn)程對此數(shù)據(jù)庫的數(shù)據(jù)進(jìn)行改變,那么另一個進(jìn)程的RoomTrackingLiveData依舊能感知到數(shù)據(jù)變化,這是怎么做到的呢?
還記得上面在調(diào)查InvalidationTrackermObserverMap時,發(fā)現(xiàn)有兩個方法遍歷了這個map嗎。其中mRefreshRunnable已經(jīng)分析過了,接下來分析另一個方法notifyObserversByTableNames。

// 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);
            }
        }
    }
}
// MultiInstanceInvalidationClient.java

final IMultiInstanceInvalidationCallback mCallback =
        new IMultiInstanceInvalidationCallback.Stub() {
            @Override
            public void onInvalidation(final String[] tables) {
                mExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        //1.調(diào)用了 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. 執(zhí)行 mSetUpRunnable 任務(wù) 
        mExecutor.execute(mSetUpRunnable);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mExecutor.execute(mRemoveObserverRunnable);
        mService = null;
    }

};        
  1. 由上可見,在MultiInstanceInvalidationClient類里綁定了一個service,并向service注冊mCallback。這個mCallback會通過InvalidationTracker#notifyObserversByTableNames()通知RoomTrackingLiveData該干活了(查詢和上報(bào)數(shù)據(jù)庫新值)。

看到IMultiInstanceInvalidationService.Stub可以大膽猜測這里涉及到了跨進(jìn)程通信。
接下來研究MultiInstanceInvalidationService

// 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循環(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
    @Override
    public IBinder onBind(@NonNull Intent intent) {
        return mBinder;
    }
}
  1. 由以上源碼可以推斷出這個service主要做了兩件事:
    • 在內(nèi)存中維護(hù)一個集合,這個集合裝載的是所有client注冊的callBack;
    • 在合適的時機(jī)調(diào)用所有client注冊的callBack。這個合適的時機(jī),就是調(diào)用broadcastInvalidation()的時候。

回到MultiInstanceInvalidationClient,回想一下這個clientservice注冊了個什么玩意。

// 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()

// 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. 調(diào)用了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個步驟是不是似曾相識?還記得RoomTrackingLiveDatamObserver對象嗎?和這里的套路是一模一樣。接下來很明顯,InvalidationTracker里面會有一個map來裝載這個mObserver。然后會有兩個方法去遍歷這個map。其中一個Runnable方法會在調(diào)用數(shù)據(jù)庫的增刪改方法時觸發(fā),另一個方法notifyObserversByTableNames會在...會在...???
我不是在研究notifyObserversByTableNames的調(diào)用鏈嗎?怎么又繞回來了?

這里理解起來有點(diǎn)繞,先明確一下前提:

  1. 針對不同的進(jìn)程操作同一個數(shù)據(jù)庫的場景,其實(shí)每一個進(jìn)程都會擁有自己獨(dú)立的RoomDatabase實(shí)例。相應(yīng)的MultiInstanceInvalidationClientInvalidationTracker、RoomTrackingLiveData都是相互獨(dú)立的。
  2. 只有MultiInstanceInvalidationService是共同的實(shí)例。而這個共同的實(shí)例,是保證不同進(jìn)程能相互感知到數(shù)據(jù)庫操作的關(guān)鍵。
  3. InvalidationTrackermRefreshRunnable是在單進(jìn)程中調(diào)用的。
  4. InvalidationTrackernotifyObserversByTableNames是用于跨進(jìn)程調(diào)用的。

下面重新捋一下思路。首先假設(shè)現(xiàn)在有兩個進(jìn)程會操作同一個數(shù)據(jù)庫。那么這兩個進(jìn)程都會各自擁有一套自己的獨(dú)立對象。即都會做一下事情:

  1. 創(chuàng)建RoomTrackingLiveData對象,并將mObserver委托給InvalidationTracker管理。
  2. RoomTrackingLiveData里的mRefreshRunnable會在被喚醒時重新查詢數(shù)據(jù)庫,并上報(bào)結(jié)果。
  3. 創(chuàng)建MultiInstanceInvalidationClient對象,并與唯一的MultiInstanceInvalidationService進(jìn)行綁定,并將callBack委托給service管理。
  4. callBack里會調(diào)用InvalidationTracker#notifyObserversByTableNames()。
  5. MultiInstanceInvalidationClient對象將mObserver委托給InvalidationTracker管理。
  6. MultiInstanceInvalidationClientmObserver會通知所有與MultiInstanceInvalidationService進(jìn)行綁定的MultiInstanceInvalidationClient,告知它們數(shù)據(jù)庫有變化。

針對進(jìn)程1,我們重點(diǎn)關(guān)注3、4、5、6。針對進(jìn)程2,我們重點(diǎn)關(guān)注1、2?,F(xiàn)在開始發(fā)車:

  1. 當(dāng)前用戶在進(jìn)程1操作了數(shù)據(jù)庫的修改操作,那么就會觸發(fā)進(jìn)程1的RoomDatabase#endTransaction(),
    進(jìn)而觸發(fā)了InvalidationTracker#mRefreshRunnable 任務(wù),遍歷InvalidationTracker#mObserverMap(在上一節(jié)有相關(guān)介紹)。此mObserverMap里存在一個MultiInstanceInvalidationClient添加進(jìn)來的mObserver(上面第5點(diǎn)有提到)。
  2. 進(jìn)程1的MultiInstanceInvalidationClientmObserver會調(diào)用MultiInstanceInvalidationService#broadcastInvalidation()
  3. MultiInstanceInvalidationService會遍歷和執(zhí)行所有MultiInstanceInvalidationClient注冊的callback。這其中的一個callback就是進(jìn)程2的MultiInstanceInvalidationClient注冊的(上面第5點(diǎn)有提到)。
  4. 進(jìn)程2的callback會調(diào)用進(jìn)程2的InvalidationTracker#notifyObserversByTableNames()。再回憶一下這個notifyObserversByTableNames()是干嘛的?沒錯,就是我們研究的第二個遍歷InvalidationTrackermObserverMap的方法。
  5. 既然進(jìn)程2已經(jīng)遍歷了mObserverMap,那么勢必會讓進(jìn)程2的RoomTrackingLiveData干活(查詢數(shù)據(jù)庫,上報(bào)新數(shù)據(jù))。

至此,room框架完成了一次完美的跨進(jìn)程通訊。
[圖片上傳失敗...(image-8769a-1678850688570)]

要想當(dāng)前的RoomDataBase具有跨進(jìn)程通訊的能力,需要在構(gòu)建databaseBuilder的時候調(diào)用enableMultiInstanceInvalidation()。例如:

Room.databaseBuilder(
   GlobalContext.getGlobalApplication(),
  AppDatabase::class.java,
 DB_NAME
)
   .enableMultiInstanceInvalidation()
 .build()

從源碼來看,RoomDataBase正是通過此方法來間接創(chuàng)建MultiInstanceInvalidationClient對象,并與MultiInstanceInvalidationService建立綁定關(guān)系。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容