LiveData源碼剖析以及Room對(duì)LiveData的支持源碼分析

LiveData是一個(gè)數(shù)據(jù)持有者,其本身實(shí)現(xiàn)了觀察者模式绽乔,支持?jǐn)?shù)據(jù)監(jiān)控(被觀察),并且可以感知組件的生命周期碳褒。
觀察者可以指定某一個(gè)LifeCycle(activity折砸,fragment)。并對(duì)數(shù)據(jù)進(jìn)行監(jiān)聽沙峻。
如果觀察者指定LifeCycle處于Started或者RESUMED狀態(tài)睦授,LiveData會(huì)將觀察者視為活動(dòng)狀態(tài),并通知其數(shù)據(jù)的變化专酗。

實(shí)戰(zhàn)

先來看一下簡單的使用睹逃,以下是一個(gè)Product列表。
我們首先來看一下數(shù)據(jù)的處理代碼:

public class ProductListViewModel extends AndroidViewModel {

    private final LiveData<List<ProductEntity>> mObservableProducts;

    public ProductListViewModel(Application application) {
        super(application);
        final ProductDataRepository repository = new ProductDataRepository();
        mObservableProducts = Transformations.switchMap(repository.isCreatedDatabase(), new Function<Boolean, LiveData<List<ProductEntity>>>() {
            @Override
            public LiveData<List<ProductEntity>> apply(Boolean input) {
                if(!Boolean.TRUE.equals(input)){
                    return ABSENT;
                }else{
                    return repository.getProducts();
                }
            }
        });
    }

    public LiveData<List<ProductEntity>> getProducts() {
        return mObservableProducts;
    }
}

代碼中將數(shù)據(jù)源定義成一個(gè)LiveData對(duì)象祷肯,LiveData中持有的是真正需要的數(shù)據(jù)List<ProductEntity>沉填。

接下來看看UI層是如何使用的,請(qǐng)看下面代碼:

public class ProductListFragment extends LifecycleFragment {

    private ProductListAdapter adapter;
    ...

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ProductListViewModel viewModel =
                ViewModelProviders.of(this).get(ProductListViewModel.class);
        subscribeUi(viewModel);
    }

    private void subscribeUi(ProductListViewModel viewModel){
        viewModel.getProducts().observe(this, new Observer<List<ProductEntity>>() {
            @Override
            public void onChanged(@Nullable List<ProductEntity> productEntities) {
                if(productEntities != null){
                    mBinding.setIsLoading(false);
                    adapter.setProducts(productEntities);
                }else{
                    mBinding.setIsLoading(true);
                }
            }
        });
    }
}

可以看到viewModel.getProducts().observe(...)就是訂閱數(shù)據(jù)佑笋。observe的第一個(gè)參數(shù)是LifeCycleOwner翼闹,即和生命周期綁定。
而ProductListFragment是繼承自LifecycleFragment蒋纬,LifecycleFragment就是一個(gè)LifecycleOwner,因此此處傳入this猎荠。第二個(gè)參數(shù)是一個(gè)觀察者坚弱,當(dāng)數(shù)據(jù)發(fā)生變化是會(huì)通過該觀察者來刷新。

下面通過分析Room對(duì)LiveData的支持來分析LiveData的工作原理

下面我們先看看從數(shù)據(jù)庫中獲取所有product的代碼:

1关摇、首先定義獲取數(shù)據(jù)的dao接口荒叶,返回類型為LiveData

@Dao
public interface ProductDao {
    @Query("select * from products")
    LiveData<List<ProductEntity>> queryLiveProducts();
}

2、編譯代碼输虱,會(huì)發(fā)現(xiàn)自動(dòng)生成ProductDao的實(shí)現(xiàn)類ProductDao_Impl.java:

public class ProductDao_Impl implements ProductDao {
  ......//省略一大波代碼
  @Override
  public LiveData<List<ProductEntity>> queryLiveProducts() {
    final String _sql = "select * from products";
    final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
    return new ComputableLiveData<List<ProductEntity>>() {
      private Observer _observer;

      @Override
      protected List<ProductEntity> compute() {
        if (_observer == null) {
          _observer = new Observer("products") {
            @Override
            public void onInvalidated() {
              invalidate();
            }
          };
          __db.getInvalidationTracker().addWeakObserver(_observer);
        }
        final Cursor _cursor = __db.query(_statement);
        try {
          final int _cursorIndexOfId = _cursor.getColumnIndexOrThrow("id");
          final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
          final int _cursorIndexOfDescription = _cursor.getColumnIndexOrThrow("description");
          final int _cursorIndexOfPrice = _cursor.getColumnIndexOrThrow("price");
          final List<ProductEntity> _result = new ArrayList<ProductEntity>(_cursor.getCount());
          while(_cursor.moveToNext()) {
            final ProductEntity _item;
            _item = new ProductEntity();
            final Long _tmpId;
            if (_cursor.isNull(_cursorIndexOfId)) {
              _tmpId = null;
            } else {
              _tmpId = _cursor.getLong(_cursorIndexOfId);
            }
            _item.setId(_tmpId);
            final String _tmpName;
            _tmpName = _cursor.getString(_cursorIndexOfName);
            _item.setName(_tmpName);
            final String _tmpDescription;
            _tmpDescription = _cursor.getString(_cursorIndexOfDescription);
            _item.setDescription(_tmpDescription);
            final double _tmpPrice;
            _tmpPrice = _cursor.getDouble(_cursorIndexOfPrice);
            _item.setPrice(_tmpPrice);
            _result.add(_item);
          }
          return _result;
        } finally {
          _cursor.close();
        }
      }

      @Override
      protected void finalize() {
        _statement.release();
      }
    }.getLiveData();
  }
  
  ...... //省略一大波代碼
}

這里我們只關(guān)心具體方法的實(shí)現(xiàn)些楣,可以看到生成的代碼中返回的是一個(gè)ComputableLiveData<List<ProductEntity>>對(duì)象,那么此對(duì)象是個(gè)啥玩意呢宪睹?讓我們找到這個(gè)類看看:

public abstract class ComputableLiveData<T> {

    private final LiveData<T> mLiveData;
    ......

    /**
     * Creates a computable live data which is computed when there are active observers.
     * <p>
     * It can also be invalidated via {@link #invalidate()} which will result in a call to
     * {@link #compute()} if there are active observers (or when they start observing)
     */
    @SuppressWarnings("WeakerAccess")
    public ComputableLiveData() {
        mLiveData = new LiveData<T>() {
            @Override
            protected void onActive() {
                // TODO if we make this class public, we should accept an executor
                AppToolkitTaskExecutor.getInstance().executeOnDiskIO(mRefreshRunnable);
            }
        };
    }

    /**
     * Returns the LiveData managed by this class.
     *
     * @return A LiveData that is controlled by ComputableLiveData.
     */
    @SuppressWarnings("WeakerAccess")
    @NonNull
    public LiveData<T> getLiveData() {
        return mLiveData;
    }

    @VisibleForTesting
    final Runnable mRefreshRunnable = new Runnable() {
        @WorkerThread
        @Override
        public void run() {
            boolean computed;
            do {
                computed = false;
                // compute can happen only in 1 thread but no reason to lock others.
                if (mComputing.compareAndSet(false, true)) {
                    // as long as it is invalid, keep computing.
                    try {
                        T value = null;
                        while (mInvalid.compareAndSet(true, false)) {
                            computed = true;
                            value = compute();
                        }
                        if (computed) {
                            mLiveData.postValue(value);
                        }
                    } finally {
                        // release compute lock
                        mComputing.set(false);
                    }
                }
            } while (computed && mInvalid.get());
        }
    };

    // invalidation check always happens on the main thread
    @VisibleForTesting
    final Runnable mInvalidationRunnable = new Runnable() {
        @MainThread
        @Override
        public void run() {
            boolean isActive = mLiveData.hasActiveObservers();
            if (mInvalid.compareAndSet(false, true)) {
                if (isActive) {
                    // TODO if we make this class public, we should accept an executor.
                    AppToolkitTaskExecutor.getInstance().executeOnDiskIO(mRefreshRunnable);
                }
            }
        }
    };

    /**
     * Invalidates the LiveData.
     * <p>
     * When there are active observers, this will trigger a call to {@link #compute()}.
     */
    public void invalidate() {
        AppToolkitTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);
    }

    @SuppressWarnings("WeakerAccess")
    @WorkerThread
    protected abstract T compute();
}

可以看出這個(gè)類其實(shí)就是對(duì)Live的一層包裝愁茁,并且處理了線程切換相關(guān)的東西。
首先在初始化類時(shí)會(huì)初始化LiveData

mLiveData = new LiveData<T>() {
            @Override
            protected void onActive() {
                // TODO if we make this class public, we should accept an executor
                AppToolkitTaskExecutor.getInstance().executeOnDiskIO(mRefreshRunnable);
            }
        };

并且如果當(dāng)前組件處于active狀態(tài)時(shí)會(huì)執(zhí)行mRefreshRunnable亭病。而這個(gè)runnable中的代碼如下:

T value = null;
while (mInvalid.compareAndSet(true, false)) {
computed = true;
value = compute();
}
if (computed) {
    mLiveData.postValue(value);
}

這段代碼實(shí)現(xiàn)了兩個(gè)功能鹅很。
1、獲取需要的數(shù)據(jù)value = compute(),
2罪帖、刷新數(shù)據(jù)mLiveData.postValue(value)

從上面代碼中我們看到這個(gè)compute是個(gè)抽象方法促煮,那么他是在哪里實(shí)現(xiàn)的呢?讓我們回到Dao的實(shí)現(xiàn)類里看看胸蛛,具體實(shí)現(xiàn)代碼如下:

 @Override
      protected List<ProductEntity> compute() {
        if (_observer == null) {
          _observer = new Observer("products") {
            @Override
            public void onInvalidated() {
              invalidate();
            }
          };
          __db.getInvalidationTracker().addWeakObserver(_observer);
        }
        final Cursor _cursor = __db.query(_statement);
        try {
          final int _cursorIndexOfId = _cursor.getColumnIndexOrThrow("id");
          final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
          final int _cursorIndexOfDescription = _cursor.getColumnIndexOrThrow("description");
          final int _cursorIndexOfPrice = _cursor.getColumnIndexOrThrow("price");
          final List<ProductEntity> _result = new ArrayList<ProductEntity>(_cursor.getCount());
          while(_cursor.moveToNext()) {
            final ProductEntity _item;
            _item = new ProductEntity();
            final Long _tmpId;
            if (_cursor.isNull(_cursorIndexOfId)) {
              _tmpId = null;
            } else {
              _tmpId = _cursor.getLong(_cursorIndexOfId);
            }
            _item.setId(_tmpId);
            final String _tmpName;
            _tmpName = _cursor.getString(_cursorIndexOfName);
            _item.setName(_tmpName);
            final String _tmpDescription;
            _tmpDescription = _cursor.getString(_cursorIndexOfDescription);
            _item.setDescription(_tmpDescription);
            final double _tmpPrice;
            _tmpPrice = _cursor.getDouble(_cursorIndexOfPrice);
            _item.setPrice(_tmpPrice);
            _result.add(_item);
          }
          return _result;
        } finally {
          _cursor.close();
        }
      }

可以看到這里是從數(shù)據(jù)庫中查詢數(shù)據(jù)并返回一個(gè)List對(duì)象污茵。至此回去數(shù)據(jù)的流程大致走了一遍,接下來就是如何訂閱的事情了葬项。

訂閱數(shù)據(jù)

這里采用了MVVM模式泞当,ViewModel將數(shù)據(jù)發(fā)送到對(duì)應(yīng)的UI界面來更新UI,這里拋開MVVM框架只看LiveData是如何更新UI的民珍。

要想更新UI首先需要訂閱對(duì)應(yīng)的數(shù)據(jù)襟士,也就是liveData。所以我們需要在Fragment/Activity中來訂閱數(shù)據(jù)嚷量,代碼如下:

viewModel.getProducts().observe(this, new Observer<List<ProductEntity>>() {
            @Override
            public void onChanged(@Nullable List<ProductEntity> productEntities) {
                if(productEntities != null){
                    mBinding.setIsLoading(false);
                    adapter.setProducts(productEntities);
                }else{
                    mBinding.setIsLoading(true);
                }
            }
        });

這里getProducts()返回的是一個(gè)LiveData對(duì)象陋桂,通過調(diào)用observer方法將改Fragment的生命周期與LiveData綁定。下面我們看看Observer方法的代碼:

public void observe(LifecycleOwner owner, Observer<T> observer) {
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && existing.owner != wrapper.owner) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
        wrapper.activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
}

1蝶溶、可以發(fā)現(xiàn)訂閱是首先判斷當(dāng)前的狀態(tài)嗜历,如果是destroyed時(shí)直接返回。
2抖所、將傳入的observer放到一個(gè)Map中梨州。
3、將當(dāng)前的頁面加入生命周期的觀察者map中田轧,讓其可被觀察暴匠。

這樣當(dāng)頁面的生命周期狀態(tài)發(fā)生變化時(shí)會(huì)通知到LiveData,LiveData再去遍歷所以的observer復(fù)合條件的發(fā)送數(shù)據(jù)更新傻粘。代碼如下:

void activeStateChanged(boolean newActive) {
      if (newActive == active) {
               return;
      }
      active = newActive;
      if (active) {
            dispatchingValue(this);
      }
}
private void dispatchingValue(@Nullable LifecycleBoundObserver initiator) {
        ......
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
                considerNotify(initiator);
                initiator = null;
            } else {
                for (Iterator<Map.Entry<Observer<T>, LifecycleBoundObserver>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }
private void considerNotify(LifecycleBoundObserver observer) {
      if (!observer.active) {
            return;
      }
      
      if (!isActiveState(observer.owner.getLifecycle().getCurrentState())) {
            return;
      }
      if (observer.lastVersion >= mVersion) {
            return;
      }
      observer.lastVersion = mVersion;
      observer.observer.onChanged((T) mData);   //這里的作用是通知對(duì)應(yīng)的UI刷新數(shù)據(jù)
}

首先當(dāng)生命周期狀態(tài)發(fā)生改變時(shí)activeStateChanged會(huì)被調(diào)用每窖,過濾掉非active的組件后調(diào)用dispatchingValue方法帮掉,在這個(gè)方法中遍歷所以的觀察者,發(fā)送數(shù)據(jù)來更新UI窒典。

到此LiveData的整個(gè)流程就分析完了蟆炊。

想要完整代碼的請(qǐng)戳這里https://github.com/qiangzier/ORMSample

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瀑志,隨后出現(xiàn)的幾起案子盅称,更是在濱河造成了極大的恐慌,老刑警劉巖后室,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異混狠,居然都是意外死亡岸霹,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門将饺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贡避,“玉大人,你說我怎么就攤上這事予弧」伟桑” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵掖蛤,是天一觀的道長杀捻。 經(jīng)常有香客問我,道長蚓庭,這世上最難降的妖魔是什么致讥? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮器赞,結(jié)果婚禮上垢袱,老公的妹妹穿的比我還像新娘。我一直安慰自己港柜,他們只是感情好请契,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著夏醉,像睡著了一般爽锥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上授舟,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天救恨,我揣著相機(jī)與錄音,去河邊找鬼释树。 笑死肠槽,一個(gè)胖子當(dāng)著我的面吹牛擎淤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秸仙,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼嘴拢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了寂纪?” 一聲冷哼從身側(cè)響起席吴,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎捞蛋,沒想到半個(gè)月后孝冒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拟杉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年庄涡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搬设。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡穴店,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拿穴,到底是詐尸還是另有隱情泣洞,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布默色,位于F島的核電站球凰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏该窗。R本人自食惡果不足惜弟蚀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酗失。 院中可真熱鬧义钉,春花似錦、人聲如沸规肴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拖刃。三九已至删壮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間兑牡,已是汗流浹背央碟。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留均函,地道東北人亿虽。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓菱涤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親洛勉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子粘秆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容