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

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

零. 前言

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

大纲android

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

一. LiveData 是什么?数据库

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

上面这短短的一段话,却有着重大的意义,举一个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);
  }
}

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

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

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

  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, 即一对多统一观察,一个经典的场景是:在向服务器请求数据时,优先展现本地数据库的数据,而后由请求的响应决定是否要更新数据库,以下图所示:

    img

    // 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);
    2. 动态转化 -- 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的实现机制

livedata2

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 才会触发消息的分发:

img

五. 总结

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

相关文章
相关标签/搜索