Jetpack组件之LiveData

2022-06-29 15:40:47 浏览数 (2)

首语

ViewModel 以注重生命周期的方式存储和管理界面相关的数据,当数据发生变化时,可通过接口的方式通知页面,但是有很多数据要通知时,需要定义大量的接口显得十分冗余,为此,Jetpack提供了LiveData组件解决这种问题,简化了开发过程。 LiveData 是一种可观察的数据存储器类。它是一个数据的容器,将数据包装起来,使数据成为被观察者,当数据发生变化时,观察者能够获得通知。 LiveData 具有生命周期感知能力,它遵循其他应用组件(如 ActivityFragmentService)的生命周期。这种感知能力确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。

优势

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

依赖

代码语言:javascript复制
    implementation 'androidx.lifecycle:lifecycle-livedata:2.3.0'
    //包含了 viewmodel 和 livedata,lifecycle-extensions 中的 API 已弃用
    //implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

使用

LiveData是一个抽象类,不能直接使用,通常使用的是它的直接子类MutableLiveData。通过LiveData ViewModel 完成一个计数器的例子。

代码语言:javascript复制
public class HomeViewModel extends ViewModel {
    //value字段用MutableLiveData包装,通常在ViewModel类中完成
    private MutableLiveData<Integer> value;

    public MutableLiveData<Integer> getCurrentValue() {
        if (value == null) {
            value = new MutableLiveData<>();
            value.setValue(0);
        }

        return value;
    }
}

定义完LiveData后,就要利用它来完成ViewModel与页面间的通信。

代码语言:javascript复制
 HomeViewModel homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
 MutableLiveData<Integer> currentValue = homeViewModel.getCurrentValue();

 currentValue.observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                textView.setText(String.valueOf(integer));
            }
 });
 button.setOnClickListener(view -> {
            homeViewModel.addValue();
 });

在页面中,通过LiveData.observe()对LiveData所包装的数据进行观察,我们也可以通过Livedata的postValue()或Livedata的setValue()来完成修改数据,postValue()用在非UI线程中,setValue()用在UI线程中。 更新页面的LiveData对象存储在ViewModel中,而不将其存储在ActivityFragment中。

代码语言:javascript复制
 public void addValue() {
        if (value.getValue() != null) {
            value.setValue(value.getValue()   1);
        }
 }

通常,LiveData仅在数据发生更改时才发送更新,并且仅发送给活跃观察者。一种例外情况是,观察者从非活跃状态更改为活跃状态时也会收到更新。此外,如果观察者第二次从非活跃状态更改为活跃状态,则只有在自上次变为活跃状态以来值发生了更改时,它才会收到更新。

扩展LiveData

LiveData的优势之一就是共享资源,将LiveData类实现一个自定义单例。

代码语言:javascript复制
public class LiveDataUtil extends LiveData<Integer> {

    private static LiveDataUtil sInstance;
    private LiveDataUtil manager;

    private SimplePListener listener = new SimpleListener() {
        @Override
        public void onPriceChanged(Integer value) {
            setValue(value);
        }
    };

    @MainThread
    public static LiveDataUtil getInstance(String symbol) {
        if (sInstance == null) {
            sInstance = new LiveDataUtil(symbol);
        }
        return sInstance;
    }

    private LiveDataUtil(String symbol) {
        manager = new LiveDataUtil(symbol);
    }

    @Override
    protected void onActive() {
        manager.requestUpdates(listener);
    }

    private void requestUpdates(SimpleListener listener) {
        this.listener = listener;
    }

    @Override
    protected void onInactive() {
        manager.removeUpdates(listener);
    }

    private void removeUpdates(SimpleListener listener) {
        this.listener = listener;
    }
}

ActivityFragment中使用它获取数据。

代码语言:javascript复制
LiveDataUtil.getInstance("flag").observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                Log.e("yhj", "onChanged: " integer);
            }
});

转换LiveData

计数器的例子中,value的类型是Integer,而给Textview设置文本类型是String,我们需要对其中的类型进行更改,LiveData中提供了Transformations类来进行LiveData的转换,类似于RxJava的操作符。

代码语言:javascript复制
//aLiveData监听bLiveData内容的变化,变化时将bLiveData转换为相应的内容并通知aLiveData  
liveData = Transformations.map(homeViewModel.getCurrentValue(), String::valueOf);
 
//aLiveData监听bLiveData内容的变化,变化时从bLiveData获取相应的cLiveData,
//添加到aLiveData的监听列表里,即aLiveData同时监听bLiveData和cLiveData,bLiveData的变化只会switch cLiveData,cLiveData变化才会通知aLiveData
aLiveData=Transformations.switchMap(bLiveData, new Function<Integer, LiveData<String>>() {
            @Override
            public LiveData<String> apply(Integer input) {
                return cLiveData;
            }
});

合并多个LiveData

同时监听多个LiveData,只要其中一个LiveData更新则收到通知,通过LiveData的子类MediatorLiveData来实现。

代码语言:javascript复制
MediatorLiveData<String> mediatorLiveData = new MediatorLiveData<>();
mediatorLiveData.addSource(liveData1, new Observer<String>() {
            @Override
            public void onChanged(String s) {
             mediatorLiveData.setValue(s);
            }
});
mediatorLiveData.addSource(liveData2, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                mediatorLiveData.setValue(s);
            }
 });

mediatorLiveData.observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
            }
});

LiveData实现事件总线

实现事件总线的方式有许多种,诸如EventBus和RxJava实现的RxBus。可参考之前的两篇文章。这两个框架都需要对生命周期进行特殊处理。LiveData自带生命周期感知能力,它本身可感知数据更新,因此,通过LiveData可以实现一个自带生命周期感知能力的事件总线框架。

代码语言:javascript复制
public class LiveDataBus {

    private static class Lazy {
        static LiveDataBus sLiveDataBus = new LiveDataBus();
    }

    public static LiveDataBus get() {
        return Lazy.sLiveDataBus;
    }

    private ConcurrentHashMap<String, StickyLiveData> mHashMap = new ConcurrentHashMap<>();

    public StickyLiveData with(String eventName) {

        StickyLiveData liveData = mHashMap.get(eventName);
        if (liveData == null) {
            liveData = new StickyLiveData(eventName);
            mHashMap.put(eventName, liveData);
        }
        return liveData;
    }

   
    public class StickyLiveData<T> extends LiveData<T> {

        private String mEventName;

        private T mStickyData;

        private int mVersion = 0;

        public StickyLiveData(String eventName) {

            mEventName = eventName;
        }

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

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

        public void setStickyData(T stickyData) {
            this.mStickyData = stickyData;
            setValue(stickyData);
        }

        public void postStickyData(T stickyData) {
            this.mStickyData = stickyData;
            postValue(stickyData);
        }

        @Override
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
            observerSticky(owner, observer, false);
        }

        public void observerSticky(LifecycleOwner owner, Observer<? super T> observer, boolean sticky) {
            super.observe(owner, new WrapperObserver(this, observer, sticky));
            owner.getLifecycle().addObserver(new LifecycleEventObserver() {
                @Override
                public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
                    if (event == Lifecycle.Event.ON_DESTROY) {
                        mHashMap.remove(mEventName);
                    }
                }
            });
        }


        private class WrapperObserver<T> implements Observer<T> {
            private StickyLiveData<T> mLiveData;
            private Observer<T> mObserver;
            private boolean mSticky;

            //标记该liveData已经发射几次数据了,用以过滤老数据重复接收
            private int mLastVersion = 0;

            public WrapperObserver(StickyLiveData liveData, Observer<T> observer, boolean sticky) {


                mLiveData = liveData;
                mObserver = observer;
                mSticky = sticky;

                //比如先使用StickyLiveData发送了一条数据。StickyLiveData#version=1
                //那当我们创建WrapperObserver注册进去的时候,就至少需要把它的version和 StickyLiveData的version保持一致
                //用以过滤老数据,否则 岂不是会收到老的数据?
                mLastVersion = mLiveData.mVersion;
            }

            @Override
            public void onChanged(T t) {
                //如果当前observer收到数据的次数已经大于等于了StickyLiveData发送数据的个数了则return
                if (mLastVersion >= mLiveData.mVersion) {
                    //但如果当前observer它是关心 黏性事件的,则给他。
                    if (mSticky && mLiveData.mStickyData != null) {
                        mObserver.onChanged(mLiveData.mStickyData);
                    }
                    return;
                }

                mLastVersion = mLiveData.mVersion;
                mObserver.onChanged(t);
            }
        }

    }
}

LiveData的原理

LiveData的observe()方法接收两个参数,第一个是LifecycleOwner,第二个是Observer,源码中ObserverActivity的生命周期关联在一起,因此,LiveData能够感知页面的生命周期,也可避免引发的内存泄漏问题。

代码语言:javascript复制
 @MainThread
 public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        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);
  }

LiveData还有一个observeForever(),和observe()没有太大区别,主要区别在于,当LiveData发生变化时,无论页面处于什么状态,都能收到通知。因此,使用完以后一定要调用removeObserver()停止对LiveData的观察,否则会造成内存泄漏。

代码语言:javascript复制
 @MainThread
 public void removeObserver(@NonNull final Observer<? super T> observer) {
        assertMainThread("removeObserver");
        ObserverWrapper removed = mObservers.remove(observer);
        if (removed == null) {
            return;
        }
        removed.detachObserver();
        removed.activeStateChanged(false);
 }

0 人点赞