【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方法;

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

相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
duyacode2 天前
FastAPI为什么成为默认答案
程序员
AFinalStone2 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen2 天前
属性动画原理与高级动画实现
android·kotlin·canvas