LiveData
LiveData 的概念比较简单,就是使你的数据 能够 具备生命周期的特性 举个例子:我们去本地数据库查询一个 ‘列表’,是不是先开一个异步线程,查询到 ‘列表’ 后在通过 Handler 从 异步线程中把 ‘列表’ push 到 UI 线程, 使用 LiveData 后:
- ROOM 框架 支持返回
LiveData<T>格式的数据, - 我们只需要在界面 使用 LiveData.observer.onChanged() 就好了
- 当我们列表有新增数据 还会自动 出发 observer.onChanged() 这样的的福利还不安利吗?
- Retrofit 同样也支持了 LiveData
- 配合 kotlin 协程使用效果更佳。
- 当然你最好结合 ViewModel+DataBinding 来实现你的懒人(MVVM)架构
具体例子
以 Google BaseSimple 为例,我迁移了一份到 gitee https://gitee.com/pengqinping/architecture-components-samples/tree/master/BasicSample 建议 在 AS 打开项目查看
- 在
ProductDao编写查询 并定义searchAllProducts - 在
DataRepository中调用searchAllProducts(架构分层 不贴代码) - 在
ProductListViewModel中调用searchProducts(架构分层 不贴代码) - 在
ProductListFragment中监听 LiveData
1.ROOM 返回 livedata
// ROOM 通过注解 在编译的时候会生成实现类 ProductDao_Impl
@Query("SELECT products.* FROM products JOIN productsFts ON (products.id = productsFts.rowid) "
+ "WHERE productsFts MATCH :query")
LiveData<List<ProductEntity>> searchAllProducts(String query);ProductDao_Impl 目录结构, 感兴趣可以去看看具体实现, 这个文件是通过 annotationProcessor room.compiler 依赖在编译的时候通过注解形成。
2. ProductListFragment.java livedata 监听
// 只要数据变化 mbinding 会自动去刷新UI 不用我们在 setView setText 等待一些列操作
private void subscribeUi(LiveData<List<ProductEntity>> liveData) {
// Update the list when the data changes
liveData.observe(getViewLifecycleOwner(), myProducts -> {
// 更新UI操作
if (myProducts != null) {
mBinding.setIsLoading(false);
mProductAdapter.setProductList(myProducts);
} else {
mBinding.setIsLoading(true);
}
// espresso does not know how to wait for data binding's loop so we execute changes
// sync.
mBinding.executePendingBindings();
});
}LiveData 源码浅析
我们从下面几个操作上来看 LiveData 的实现
- 我们创建普通的
LiveData<T>一般是直接通过构造器来 创建对象 - 更新数据
livedata.setValue(T)liveData.postValue(T) - observer LiveData 的变化
1. LiveData<T>()
public LiveData(T value) {
mData = value;
mVersion = START_VERSION + 1;
}2. setValue(T)
// 必须在主线程调用
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}3. postValue(T)
// 可以在子线程调用,这里有切换线程的操作
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
// 切换线程 最终是 通过 DefaultTaskExecutor.postToMainThread() 这里面会创建一个 MainHandler 最终还是通过 handler 去实现的,
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
// 其实就做了一个同步锁,最终还是通过 setValue来更新,所以基本所有的 通知更新 都在 dispatchingValue() 里面
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
setValue((T) newValue);
}
};4. dispatchingValue(@Nullable ObserverWrapper initiator)
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
// 遍历 所有的 mObserver 去进行通知
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
// 发布通知
observer.mObserver.onChanged((T) mData);
}mObservers 是在 去监听 LiveData 的时候传入的 liveData.observe(getViewLifecycleOwner(),Observer{})
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
// lifecycle相关的逻辑也只在这里实现的
// owner 一般是 Activity 和 Fragment (最好是 androidx包下的)
//
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
owner.getLifecycle().addObserver(wrapper);
}源码分析到此结束,感兴趣可自行查看更多细节
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
// 当 Activity 或者 fragment destory 后 移除出掉 observer 所以不会出现 destory 后 liveData 在收到消息的情况
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}Transformations.map() 和 Transformations.switchMap()
- 为什么需要
Trasnformations? - 如果你的界面需要显示一个
String接口给你了一个List<String>,你可能会想,我把List<String>传过去自己 截取拼接。这样虽然也可以,但是,你凭借出来的数据 不是LiveData类型的。没法Observers. 这个时候你就需要Trasnformations
Document: 中文版的我硬是没有看懂 You may want to make changes to the value stored in a LiveData object before dispatching it to the observers, or you may need to return a different LiveData instance based on the value of another one. The Lifecycle package provides the Transformations class which includes helper methods that support these scenarios.
1. Transformations.map()
- 文档说的第一种情况就是 我想要展示 UserName, 实例中的都是 LiveData 不可变,也就是初始化后不能改变它的值
val userLiveData: LiveData<User> = UserLiveData()
val userName: LiveData<String> = Transformations.map(userLiveData) {
user -> "${user.name} ${user.lastName}"
}2. Trasnformations.switchMap()
比如根据一个 Id 去查询一个列表,如果直接使用函数,并不能体现出 LiveData 的特性,这个时候想直接把查询结果作为 LiveData 的时候我们就需要 LiveData 了
private fun getUser(id: String): LiveData<User> {
...
// 这里的请求可以是 retrofit 也可以是 ROOM 目前这两个都支持了直接返回LiveData
}
val userId: LiveData<String> = ...
val user = Transformations.switchMap(userId) { id -> getUser(id) }下面的例子中 我改变 liveGroupId 数据,links 会自动的去更新
// ViewModel
private val liveGroupId = MutableLiveData<Long>()
val links = Transformations.switchMap(liveGroupId) {
linkRepository.findLinksWithGroupId(it)
}
// Fragment
linkViewModel.links.observe(this, Observer {
if (it.isNullOrEmpty()) {
enableDeleteMenu()
state_view.showEmptyView()
} else {
state_view.showContent()
}
// 为空也要去清理下 Adapter 否则还有数据
linkAdapter.setNewData(it)
})