LiveData源码剖析以及Room对LiveData的支持源码分析

LiveData是一个数据持有者,其本身实现了观察者模式,支持数据监控(被观察),并且可以感知组件的生命周期。
观察者可以指定某一个LifeCycle(activity,fragment)。并对数据进行监听。
如果观察者指定LifeCycle处于Started或者RESUMED状态,LiveData会将观察者视为活动状态,并通知其数据的变化。

实战

先来看一下简单的使用,以下是一个Product列表。
我们首先来看一下数据的处理代码:

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;
    }
}

代码中将数据源定义成一个LiveData对象,LiveData中持有的是真正需要的数据List<ProductEntity>。

接下来看看UI层是如何使用的,请看下面代码:

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(...)就是订阅数据。observe的第一个参数是LifeCycleOwner,即和生命周期绑定。
而ProductListFragment是继承自LifecycleFragment,LifecycleFragment就是一个LifecycleOwner,因此此处传入this。第二个参数是一个观察者,当数据发生变化是会通过该观察者来刷新。

下面通过分析Room对LiveData的支持来分析LiveData的工作原理

下面我们先看看从数据库中获取所有product的代码:

1、首先定义获取数据的dao接口,返回类型为LiveData

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

2、编译代码,会发现自动生成ProductDao的实现类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();
  }
  
  ...... //省略一大波代码
}

这里我们只关心具体方法的实现,可以看到生成的代码中返回的是一个ComputableLiveData<List<ProductEntity>>对象,那么此对象是个啥玩意呢?让我们找到这个类看看:

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();
}

可以看出这个类其实就是对Live的一层包装,并且处理了线程切换相关的东西。
首先在初始化类时会初始化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);
            }
        };

并且如果当前组件处于active状态时会执行mRefreshRunnable。而这个runnable中的代码如下:

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

这段代码实现了两个功能。
1、获取需要的数据value = compute(),
2、刷新数据mLiveData.postValue(value)

从上面代码中我们看到这个compute是个抽象方法,那么他是在哪里实现的呢?让我们回到Dao的实现类里看看,具体实现代码如下:

 @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();
        }
      }

可以看到这里是从数据库中查询数据并返回一个List对象。至此回去数据的流程大致走了一遍,接下来就是如何订阅的事情了。

订阅数据

这里采用了MVVM模式,ViewModel将数据发送到对应的UI界面来更新UI,这里抛开MVVM框架只看LiveData是如何更新UI的。

要想更新UI首先需要订阅对应的数据,也就是liveData。所以我们需要在Fragment/Activity中来订阅数据,代码如下:

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()返回的是一个LiveData对象,通过调用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、可以发现订阅是首先判断当前的状态,如果是destroyed时直接返回。
2、将传入的observer放到一个Map中。
3、将当前的页面加入生命周期的观察者map中,让其可被观察。

这样当页面的生命周期状态发生变化时会通知到LiveData,LiveData再去遍历所以的observer复合条件的发送数据更新。代码如下:

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);   //这里的作用是通知对应的UI刷新数据
}

首先当生命周期状态发生改变时activeStateChanged会被调用,过滤掉非active的组件后调用dispatchingValue方法,在这个方法中遍历所以的观察者,发送数据来更新UI。

到此LiveData的整个流程就分析完了。

想要完整代码的请戳这里https://github.com/qiangzier/ORMSample

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容