感知生命周期的数据 -- LiveData

零. 前言

上篇文章《万物基于Lifecycle》 介绍了整个Lifecycle体系的基石,今天这篇文章咱们来看看Jetpack给我们带来的活着的数据——LiveData

大纲

  • LiveData 是什么?
  • 为什么要用LiveData?
  • How to use LiveData?
  • LiveData的生命感知能力从何而来,是如何与Lifecycle结合的?

一. LiveData 是什么?

LiveData 简单来说,就是普通数据对象的一个包装类,这个包装类中帮助你主动管理了数据的版本号,基于观察者模式,让普通的数据对象能够感知所属宿主(Activity、Fragment)的生命周期。这种感知能力就能够保证只有宿主活跃(Resumed、Started)时,数据的观察者才能受到数据变化的消息。

上面这短短的一段话,却有着重大的意义,举一个case:

有一个页面需要加载一个列表,我们需要在后台线程去服务器请求对应的数据,请求数据成功并经过解析后post消息通知UI,UI再渲染请求来的数据。等等!假若此时的网络很慢,而刚好用户此时按home键退出了应用,那么在此时UI是不应该被渲染的,而是应该等用户重新进入应用时才开始渲染。

从下面的演示代码就诠释了上面的说所的case

class MainActivity extends AppcompactActivity{
    public void onCreate(Bundle bundle){
  
     Handler handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
             //无论页面可见不可见,都会去执行页面刷新,IO。更有甚者弹出对话框
        }
      };
     //1.无论当前页面是否可见,这条消息都会被分发。----消耗资源
     //2.无论当前宿主是否还存活,这条消息都会被分发。---内存泄漏
    handler.sendMessage(msg)

    
    liveData.observer(this,new Observer<User>){
         void onChanged(User user){
           
         }
     }
    //1.减少资源占用---          页面不可见时不会派发消息
    //2.确保页面始终保持最新状态---页面可见时,会立刻派发最新的一条消息给所有观察者--保证页面最新状态
    //3.不再需要手动处理生命周期---避免NPE
    //4.可以打造一款不用反注册,不会内存泄漏的消息总线---取代eventbus
    liveData.postValue(data);
  }
}

有人说,我可以在处理消息时,根据当前页面时是否可见来具体处理对应逻辑。是的,没错,确实可以这样,就像下面这样。

Handler handler = new Handler(){
    @Override
    public void handleMessage(@NonNull Message msg) {
         if (isActivityValid()) {
         	// updateUI...
         } else {
         	// dosomething...
         }
    }
  };

我再拿上面的例子说一下这种问题:

  1. 需要自行判断宿主活跃状态,防止生命周期越界。
  2. 如果Activity不可见,此时不更新UI,那么就需要复写onResume方法去更新UI,手工管理生命周期,增加了代码的复杂性。

二. 为什么要使用LiveData?

上面的例子已经很好地诠释了LiveData的强大,当然不仅限于此,它的优势如下:

  1. 确保界面符合数据状态LiveData 遵循观察者模式。当生命周期状态发生变化时,LiveData 会通知 Observer 对象。观察者可以在onChanged事件时更新界面,而不是在每次数据发生更改时立即更新界面。
  2. 不会发生内存泄漏观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。
  3. 不会因 Activity 停止而导致崩溃如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。
  4. 不再需要手动处理生命周期界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。
  5. 数据始终保持最新状态如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。
  6. 适当的配置更改如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。
  7. 共享资源可以使用单一实例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData 对象
  8. 支持黏性事件的分发
    即先发送一条数据,后注册一个观察者,默认是能够收到之前发送的那条数据的

三. How to use LiveData ?

step1: 添加依赖:

step2: 在ViewModel中创建 LiveData 实例 (ViewModel组件会在下期讲到,将LiveData存储至viewModel中,是为了符合MVVM架构思想,V层仅负责展示,而VM层负责数据逻辑)

public class ViewModelTest extends AndroidViewModel {
	public final  MutableLiveData<String> name = new MutableLiveData<>();
	...
}

step3: 在Activity或Fragment中对LiveData进行添加观察者进行监听

public class ActivityTest extends AppCompatActivity {
	...
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        viewModel.name.observe(this, new Observer<String>() {
            @Override
            public void onChanged(@Nullable String name) {
                // dosomething...
            }
        });
    }
}

我们可以根据LiveData值的变化来做对应的事情,且不用担心生命周期越界的问题。

LiveData核心方法

方法名

作用

observe(LifecycleOwner owner,Observer observer)

注册和宿主生命周期关联的观察者

observeForever(Observer observer)

注册观察者,不会反注册,需自行维护

setValue(T data)

发送数据,没有活跃的观察者时不分发。只能在主线程。

postValue(T data)

和setValue一样。不受线程环境限制,

onActive

当且仅当有一个活跃的观察者时会触发

inActive

不存在活跃的观察者时会触发

LiveData的衍生类及功能

MutableLiveData

该类十分简单,主要开放了LiveData的发消息接口

public class MutableLiveData<T> extends LiveData<T> {
      @Override
      public void postValue(T value) {
        super.postValue(value);
    }

    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}

设计初衷:考虑单一开闭原则,LiveData只能接受消息,避免拿到LiveData对象既能发消息也能收消息的混乱使用。

MediatorLiveData

合并多个LiveData, 即一对多统一观察,一个经典的场景是:在向服务器请求数据时,优先展示本地数据库的数据,然后由请求的响应决定是否要更新数据库,如下图所示:

// ResultType: Type for the Resource data.
// RequestType: Type for the API response.
public abstract class NetworkBoundResource<ResultType, RequestType> {
    // MediatorLiveData 数据组合者
    private final MediatorLiveData<Resource<ResultType>> result = new MediatorLiveData<>();
    private Executor executor;
    @MainThread
    protected NetworkBoundResource(Executor mExecutor) {
        this.executor = mExecutor;
        // 首先初始化一个Loading的status 空result
        result.setValue(Resource.loading(null));
        // 然后从数据库中获取持久化数据
        LiveData<ResultType> dbSource = loadFromDb();
        // 数据组合者监听数据库中的数据
        result.addSource(dbSource, data -> {
            // dbSource第一次回调,用来判断数据有效期,此时取消监听
            result.removeSource(dbSource);
            // 业务自行定义是否需要fetch最新的数据
            if (shouldFetch(data)) {
                fetchFromNetwork(dbSource);
            } else {
                // 数据有效,重新观察一次,观察者会立马收到一次回调(LiveData粘性事件机制)
                result.addSource(dbSource, newData -> result.setValue(Resource.success(newData)));
            }
        });
    }
    
    private void fetchFromNetwork(final LiveData<ResultType> dbSource) {
        LiveData<ApiResponse<RequestType>> apiResponse = createCall();
        // 这里数据虽无效,但是可以先给UI展示
        result.addSource(dbSource, newData -> setValue(Resource.loading(newData)));
        result.addSource(apiResponse, response -> {
            result.removeSource(apiResponse);
            result.removeSource(dbSource);

            if (response != null) {
                if (response.isSuccessful()) {
                    executor.execute(() -> {
                        saveCallResult(processResponse(response));
                        executor.execute(() ->
                             // 这里我们拿到的最新的数据需要主动通知监听者,以拿到从服务端拿到的最新数据
                                result.addSource(loadFromDb(),
                                        newData -> setValue(Resource.success(newData)))
                        );
                    });
                } else {
                    onFetchFailed();
                    result.addSource(dbSource,
                            newData -> setValue(Resource.error(response.errorMessage, newData)));
                }
            } else {
                result.addSource(dbSource,
                        newData -> setValue(Resource.error("Request failed, server didn't 		response", newData)));
            }
        });
    }
}

上面的例子MediatorLiveData同时监听了数据库中的LiveData和服务端的LiveData

Transformations

这是一个数据转化工具类,共两个主要方法:

  1. 静态转化 -- map(@NonNull LiveData source, @NonNull final Function<X, Y> mapFunction)
MutableLiveData<Integer> data = new MutableLiveData<>();

//数据转换
LiveData<String> transformData = Transformations.map(data, input ->   String.valueOf(input));
//使用转换后生成的transformData去观察数据
transformData.observe( this, output -> {

});

//使用原始的livedata发送数据
data.setValue(10);
  1. 动态转化 -- LiveData switchMap(
    @NonNull LiveData source,
    @NonNull final Function<X, LiveData> switchMapFunction)
MutableLiveData userIdLiveData = ...;
 // 用户数据和用户id紧密相关,当我们改变userId的liveData的同时还会主动通知userLiveData更新
LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
     repository.getUserById(id));

 void setUserId(String userId) {
      this.userIdLiveData.setValue(userId);
 }

// 不要像下面这么做
private LiveData getUserLiveData(String userId) {
    // DON'T DO THIS
    return repository.getUserById(userId);
}

四. LiveData的实现机制

vaddinsession的生命周期 livedata生命周期_vaddinsession的生命周期

LiveData注册观察者触发消息分发流程:

  1. observe 注册时,可以主动跟宿主生命周期绑定,不用反注册:
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        //1. 首先来个断言,这个方法只能在主线程调用,observeForever也是。
        assertMainThread("observe");
        //2.其次把注册进来的observer包装成 一个具有生命周边边界的观察者
        //它能监听宿主被销毁的事件,从而主动的把自己反注册,避免内存泄漏
        //此时观察者是否处于活跃状态就等于宿主是否可见
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        //3.接着会判断该观察是否已经注册过了,如果是则抛异常,所以要注意,不允许重复注册
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        //4.这一步才是关键
        //利用Lifecycle,把观察者注册到进去,才能监听到宿主生命周期状态的变化,对不对?
        //根据Lifecycle文章中的分析,一旦一个新的观察者被添加,Lifecycle也会同步它的状态和宿主一致对不对?此时会触发观察者的onStateChanged方法
        owner.getLifecycle().addObserver(wrapper);
}
  1. LifecycleBoundObserver 监听宿主的生命周期(这里我们还记得之前在Lifecycle解析中提到addObserver时也会对observer进行包装,这时一样的),并且宿主不可见时不分发任何数据:
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
            super(observer);
        }

        @Override
        boolean shouldBeActive() {
        //使用observer方法注册的观察者都会被包装成LifecycleBoundObserver
        //观察者是否活跃就等于宿主 的状态是否大于等于STARTED,
        //如果页面当前不可见,你发送了一条消息,此时是不会被分发的,可以避免后台任务抢占资源,当页面恢复可见才会分发。
        //注意:如果使用observerForever注册的观察者,
        //会被包装成AlwaysActiveObserver,它的shouldBeActive一致返回true.即便在页面不可见也能收到数据
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
                //在这里如果监听到宿主被销毁了,则主动地把自己从livedata的观察者中移除掉
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            //否则说明宿主的状态发生了变化,此时会判断宿主是否处于活跃状态
            activeStateChanged(shouldBeActive());
        }
    }
  1. ObserverWrapper 状态变更后,如果观察者处于活跃状态会触发数据的分发流程:
abstract class ObserverWrapper{
        final Observer<? super T> mObserver;
        boolean mActive;
        int mLastVersion = START_VERSION//这里就等于-1,没有主动和LiveData的mVersion对齐,为黏性事件埋下了伏笔
        
        void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;
            }
            //更改观察者的状态
            mActive = newActive;
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            //如果此时有且只有一个活跃的观察者则触发onActive
            LiveData.this.mActiveCount += mActive ? 1 : -1;
            if (wasInactive && mActive) {
                onActive();
            }
            //没有任何一个活跃的观察者则触发onInactive
            //利用这个方法被触发的时机,可以做很多事,比如懒加载,资源释放等
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                onInactive();
            }
            //如果此时观察者处于活跃状态,下面就开始分发数据了
            //请注意,这里传递了this = observer
            if (mActive) {
                dispatchingValue(this);
            }
        }
}
  1. dispatchingValue 数据分发流程控制:
void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
            //如果传递的观察者不为空,则把数据分发给他自己。这个流程是新注册观察者的时候会被触发
                considerNotify(initiator);
                initiator = null;
            } else {
                //否则遍历集合中所有已注册的的观察者,逐个调用considerNotify,分发数据
                for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> 		                       iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }
  1. considerNotify 数据真正分发的地方,需要满足三个套件:
private void considerNotify(ObserverWrapper observer) {
        //观察者当前状态不活跃不分发
        if (!observer.mActive) {
            return;
        }
        //观察者所在宿主是否处于活跃状态,否则不分发,并且更改观察者的状态为false
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        //此处判断观察者接收消息的次数是否大于等于 发送消息的次数
        //但是observer被创建之初verison=-1 
        //如果此时LiveData已经发送过数据了。这里就不满足了,就出现黏性事件了,后注册的观察者收到了前面发送的消息。
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        //每分发一次消息,则把观察者和LiveData的version对齐,防止重复发送
        observer.mLastVersion = mVersion;
        //最后的数据传递
        observer.mObserver.onChanged((T) mData);
    }

普通消息分发流程。即调用 postValue,setValue 才会触发消息的分发:

vaddinsession的生命周期 livedata生命周期_数据_02

五. 总结

Android开发大部分主要的工作就是从服务器获取数据,将其转为渲染为UI展示给用户。本质上我们所做的逻辑都是“数据驱动”,所有的View都是数据的一种状态,数据映射的过程中我们需要去考虑生命周期的限制--即数据的活跃性。LiveData结合Lifecycle帮我们规范了这个流程,完美诠释了observer、lifecycle-aware、data holder 这个铁三角,开发者在遵循这个开发流程的过程中,便完成了UI -> ViewModel -> Data的单项依赖。