【Android】LiveData的使用以及源码浅析

LiveData的使用

简介:

  1. 介绍:LiveData是一种可观察的数据存储器类;比较鲜明的特征就是LIveData具有生命周期的感知能力,能够响应组件(活动服务碎片等)生命周期,以确保LiveData仅更新处于活跃生命周期状态下的组件观察者;

livedata是数据容器->livedata默认实现了观察者模式->数据是被观察的->当数据发生变化->通知观察者;

  1. 总结:
    1. 是一个类,是一个储存信息的类,可以观察;
    2. 可以感知组件的生命周期;
    3. 更新处于活跃状态下的生命周期;
  1. 那么问题来了,什么可以被称之位活跃状态?

观察者的组件的生命周期位于Started或Resumed状态;

  1. 活动或者是碎片能再生命周期结束的时候立刻解除对数据的订阅,当组件的状态变化destroyded 时->观察者 LifecycleObserver 组件的状态也变为 Destroyed ->系统会自动移除观察者;
  2. 当我们更新livedata中的值时,会触发所有注册这个livedata的观察者(处于活跃状态的);

优势:

  1. 自动更新界面数据:遵循观察者模式,当数据发生改变时,livedata会通知Observer对象, 观察者会替你进行更新;
  2. 不会因为活动的停止就崩溃,因为当组件变为非活跃状态时,只会不接受livedata事件 ;而且不需要手动处理生命周期,livedata会自动管理这些事件 ,做出响应->比如当旋转屏幕/由不可见变得可见时->会接受最新的数据;
  3. 如果livedata是单例的话,可以分享数据;

LiveData的使用:

  1. 在viewmodel中:创建一个livedata实例,储存数据;
  2. 在活动/碎片中:创建observer的实例,通过onchanged方法,响应liveData数据的变化;
  3. 在活动/碎片中:将两者相关联,通过Livedata的observer方法;
  1. 还有一个方法:

    1. 使用obServeForever(Observer)的方式来注册观察者;
    2. 通过removeObserver(Observer) 来移除观察者;
  2. 这种情况下,观察者始终会被认为是活跃的状态,始终都会接受到livedata的通知;

所以一定要记得移除啊,否则一直都会订阅,Livedata一直处于激活状态,导致活动不会被系统回收,造成了内存泄漏;

创建livedata的对象

livedata可以承载任何数据的容器,通常用于viewModel对象中,并且通过getter方法进行访问;

livedata是抽象类,我们通常使用MutableLiveData,是livedata的子类;

Java 复制代码
public class BookViewModel extends ViewModel {
    private MutableLiveData<Book> mutableLiveData;
    public MutableLiveData<Book> getBook(){
        if(mutableLiveData == null){
            mutableLiveData = new MutableLiveData<>();
        }
        return mutableLiveData;
    }
}

为什么不放在活动/碎片中?

  1. 活动/碎片中,你应该去做UI的更新,不能储存数据喔!!!
  2. 不随着活动/碎片生命周期,这样在配置更改之后livedata才能继续保存啊

观察livedata对象

在活动/碎片中的oncreate方法中开始观察livedata:

  1. 确保活动/碎片变为活跃状态之前具有可以显示的数据;当处于活跃状态时,会从正在观察的livedata对象中获取最新值;
  2. onresume会多次调用;

如果观察者从第二次非活跃变为活跃时,只有在上次变为活跃状态以来值发生变化,才会更新

Java 复制代码
     Book book1 = new Book("ll","jj");

        binding.setBookin(book1);
        //获得viewmodel对象
        BookViewModel bookViewModel = new ViewModelProvider(this).get(BookViewModel.class);
        //创建观察者
        final Observer<Book> observer = new Observer<Book>() {
            @Override
            public void onChanged(Book book) {
            //更新UI
                binding.setBookin(book);
            }
        };
        //观察这个livedata,使得活动作为lifecycleowner
        bookViewModel.getBook().observe(this,observer);

调用observe之后,如果给livedata设置值了,会调用Onchanged方法,否则不会;

观察者的两种方式:

方式一:

Java 复制代码
 //创建观察者
    final Observer<Book> observer = new Observer<Book>() {
        @Override
        public void onChanged(Book book) {
        //更新UI
            binding.setBookin(book);
        }
    };
    //观察这个livedata,使得活动作为lifecycleowner
    bookViewModel.getBook().observe(this,observer);

就是new一个观察者,通过Livedata的Obsetve方法

方式二:

结合databinding:

databinding.setLifecycleOwner(this);

修改值

livedata没有公开可用的方法来更新存储的数据,MutableLiveData setvalue(T)(修改值),postvalue(T)可以修改livedata对象中的值,所以通常会使用MutableLiveData;

  1. setvalue(T):必须用在主线程;
  2. postvalue(T):任意线程都可以使用,底层会使用:handler.post;
Java 复制代码
binding.bt.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
       bookViewModel.getBook().setValue(new Book("ljxxhswy","swyxhljx"));
    }
});

源码分析:

liveData的重要方法有两个,所以我们着重讲:observe方法和setvalue方法

observe

Java 复制代码
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
//判断是否被销毁
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {  
        return;
    }
    //利用LifecycleBoundObserver包装起来
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    //如果有缓存,直接从缓存中读取observe
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    //如果添加过,缓存的observe和owner的observe不一致会报错  
    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);
}

observe方法小结:

  1. 判断是不是被销毁,如果销毁直接返回;
  2. 包装;
  3. 判断是不是被添加过,如果被添加过直接返回;
  4. 如果没有被添加过,把包装后的添加进去;

当生命周期变化时,会先通知LifecycleBoundObserver的onStateChanged 方法,让这个方法又会回调observer 的 onChange 方法;

所以先来看看onStateChanged

Java 复制代码
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
    @NonNull final LifecycleOwner mOwner;

    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<T> observer) {
        super(observer);
        mOwner = owner;
    }

    @Override
    boolean shouldBeActive() {
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }

    @Override
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
        if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        activeStateChanged(shouldBeActive());
    }
}
  1. 生命周期变化时,如果destory了,会直接移除观察者,这也是为什么不需要我们去手动移除观察着的原因;
  2. activeStateChanged(shouldBeActive());当lifecycle的state是激活状态时,shouldBeActive()的方法返回值是true;

activeStateChanged(shouldBeActive()):

Java 复制代码
void activeStateChanged(boolean newActive) {
//如果和上一次状态一致,直接返回
    if (newActive == mActive) {
        return;
    }
    // immediately set active state, so we'd never dispatch anything to inactive
    // owner
    mActive = newActive;
    boolean wasInactive = LiveData.this.mActiveCount == 0;
    LiveData.this.mActiveCount += mActive ? 1 : -1;
    if (wasInactive && mActive) {
        onActive();
    }
    if (LiveData.this.mActiveCount == 0 && !mActive) {
        onInactive();
    }
    if (mActive) {
        dispatchingValue(this);
    }
}

里面有三个变量:

  1. mActive:记录当前的状态;
  2. wasInactive :记录之前有没有激活状态;
  3. mActiveCount:记录激活状态的总数;

里面有三个方法:

  1. onActive():所以它是当前是激活状态,而且之前没有激活状态的observe回调,适合做一些后台的开始工作;
  2. onInactive():当最后一个观察者变为非活跃的时候调用,适合做停止后台工作;
  3. dispatchingValue(this):当当前是活跃状态时会回调;

当当前是激活状态,会触发:dispatchingValue(this);

Java 复制代码
private void dispatchingValue(@Nullable ObserverWrapper initiator) {
    //如果正在处理返回
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
        
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            considerNotify(initiator);
            initiator = null;
        } else { 
        //如果initiator为null,会遍历所有的Observe,分发;
            for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
  
    mDispatchingValue = false;
}

considerNotify 方法:

Java 复制代码
private void considerNotify(ObserverWrapper observer) {
   // 如果状态不是在活跃中,直接返回,所以不会回调Onchange方法,不会更新数据的原因;
    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;
    //noinspection unchecked
        // 调用外部的 mObserver 的 onChange 方法
    observer.mObserver.onChanged((T) mData);
}

setValue

Java 复制代码
@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}
  1. 会加一个版本号,然后设置值;
  2. 有一个巧妙构思,如果当前状态是destory的话,会版本号+1,然后由于处于destory状态dispatchingValue(null)会直接返回;当回到前台的时候,此时生命周期的变化会触发onstatechange回调,最后还是会触发dispatchingValue(null),此时版本号不一致,会更新数据;这也就是为什么重新回到前台会更新的原因;

postValue

Java 复制代码
protected void postValue(T value) {
   boolean postTask;
   //判断有没有任务在执行;
   synchronized (mDataLock) {
       postTask = mPendingData == NOT_SET;
       mPendingData = value;
   }
   //如果有任务直接返回
   if (!postTask) {
       return;
   }
   AppToolkitTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
   @Override
   public void run() {
       Object newValue;
       synchronized (mDataLock) {
           newValue = mPendingData;
           //会更新执行的任务
           mPendingData = NOT_SET;
       }
       setValue((T) newValue);
   }
}
  1. 从上述代码中,我们可以看到使用了同步锁,这是因为postvalue在子线程中,如果不加锁,会出现很多问题;
  2. 然后使用postTask标记是否有线程在执行任务;
  3. 最后调用AppToolkitTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);会到主线程去更新UI;

observeForever

Java 复制代码
@MainThread
public void observeForever(@NonNull Observer<T> observer) {
    AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing != null && existing instanceof LiveData.LifecycleBoundObserver) {
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    wrapper.activeStateChanged(true);
}

private class AlwaysActiveObserver extends ObserverWrapper {

    AlwaysActiveObserver(Observer<T> observer) {
        super(observer);
    }

    @Override
    boolean shouldBeActive() {
        return true;
    }
}

AlwaysActiveObserver这个类没有实现GenericLifecycleObserver 方法接口;

实现GenericLifecycleObserver接口->生命周期变化时,会回调 onStateChange ->没有实现->生命周期变化不会通知->所以不会主动removeobserve方法;

本次分享到此结束,谢谢大家!

相关推荐
用户69371750013842 小时前
10.Kotlin 类:延迟初始化:lateinit 与 by lazy 的对决
android·后端·kotlin
NineData2 小时前
保姆级!Oracle→达梦零停机迁移攻略,5 步操作,业务零影响!
数据库·程序员
正经教主2 小时前
【Git】Git06:Git 管理 Android 项目教程(含GitHub)
android·git
安卓理事人2 小时前
安卓多种通知ui更新的方式(livedata,rxjava,eventbus等)
android·ui·echarts
BS_Li2 小时前
【Linux系统编程】Ext系列文件系统
android·linux·ext系列文件系统
zhangphil3 小时前
Android宽高不均等Bitmap缩放为指定宽高FitCenter到正方形Bitmap,Kotlin
android·kotlin
别或许3 小时前
13.用户管理
android
SimonKing3 小时前
你的IDEA还缺什么?我离不开的这两款效率插件推荐
java·后端·程序员
q***96585 小时前
springboot3整合knife4j详细版,包会!(不带swagger2玩)
android·前端·后端