Android Jetpack之ViewModel、LiveData

Android Jetpack之LifeCycle

Android Jetpack之DataBinding ViewModel LiveData Room

前言

Jetpack是一个由多个技术库组成的套件,可帮助开发者遵循最佳做法,减少样板代码并编写可在各种Android版本和设备中一致运行的代码,让开发者精力集中编写重要的代码。

一、LiveData

LiveData 是一种持有可被观察的数据存储类。和其他可被观察的类不同的是LiveData 可以在 Activity ,fragment 或者 service 生命周期发生改变时通知更新。LiveData 已经是必不可少的一环了,例如 MVVM 以及 MVI 开发模式中,都用到了 LiveData。

(注意⚠️源码是Java代码)

LIveData 的优势:

  • 确保界面符合数据状态:数据发生变化时,就会通知观察者。我们可以再观察者回调中更新界面,这样就无需在数据改变后手动更新界面了。
  • 没有内存泄漏,因为关联了生命周期,页面销毁后会进行自我清理。
  • 不会因为Activity 停止而导致崩溃,页面处于非活跃状态时,他不会接收到任何 LiveData 事件。
  • 共享资源,可以使用单例模式扩展 LiveData 对象,以便在应用中共享他们。
  • 屏幕翻转数据状态保留
  • 不再需要手动处理生命周期
  • 数据始终保持最新状态

二、使用案例

LiveData 是一种可用于任何数据的封装容器,通常 LiveData 存储在 ViewModel 对象中。

class JokesDetailViewModel : ViewModel() {
    //创建 LiveData
    private val _state by lazy { MutableLiveData<JokesUIState>() }
    val state : LiveData<JokesUIState> = _state
    private fun loadChildComment(page: Int, commentId: Int, parentPos: Int, curPos: Int) {
        viewModelScope.launch {
            launchHttp {
                jokesApi.jokesCommentListItem(commentId, page)//请求数据
            }.toData {
                //通知观察者
                _state.value = JokesUIState.LoadMoreChildComment(it.data, parentPos, curPos)
            }
        }
     }
}    
//观察 LiveData
viewModel.state.observe(this, Observer {
    //更新 UI 
})

三、LiveData 实现原理

  • Observer:观察者接口;
  • LiveData:发送已经添加观察的逻辑都在其中;
  • ObserverWrapper:抽象的观察者包装类;
  • LifecycleBoundleObserver:继承 ObserverWrapper;
  • LifecycleEventObserver,生命周期相关回调;

observe(this,Observer{})

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        //如果已经销毁,直接退出
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            return;
        }
        //包装类(下面源码)
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        //判断是否已添加
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
           //....
        }
        //之前添加过
        if (existing != null) {
            return;
        }
        //注册 lifecycle,生命周期改变时会回调
        owner.getLifecycle().addObserver(wrapper);
    }

LifecycleBoundObserver(owner, observer)

class LifecycleBoundObserver 
                extends ObserverWrapper implements LifecycleEventObserver {
    @NonNull
    final LifecycleOwner mOwner;
    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
        super(observer);
        mOwner = owner;
    }
    //当前状态大于或者等于 STARTED 返回 true
    @Override
    boolean shouldBeActive() {
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }
    //生命周期相关回调
    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
        //如果已经销毁,移除观察者 
        if (currentState == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        Lifecycle.State prevState = null;
       //循环更新状态
        while (prevState != currentState) {
            prevState = currentState;
            //修改活跃状态
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }
    @Override
    boolean isAttachedTo(LifecycleOwner owner) {
        return mOwner == owner;
    }
    @Override
    void detachObserver() {
        mOwner.getLifecycle().removeObserver(this);
    }
}
  • 继承 ObserverWrapper 的包装类--activeStateChanged(..)方法。
  • 实现LifecycleEventObserver 接口--onStateChanged(..)方法。

ObserverWrapper.activeStateChanged()

void activeStateChanged(boolean newActive) {
  //如果等于之前状态
  if (newActive == mActive) {
    return;
  }
  mActive = newActive;
  //活跃  1,不活跃 -1 (下面有源码)
  changeActiveCounter(mActive ? 1 : -1);
  //如果状态变成了活跃状态,直接调用 dispatchingValue,传入当前的观察者
  if (mActive) {
    dispatchingValue(this); //(下面有源码)
  }
}
//修改活跃数量
void changeActiveCounter(int change) {
    int previousActiveCount = mActiveCount;//之前活跃的数量
    mActiveCount  = change;//总活跃数量
    if (mChangingActiveState) {//如果正在更改,退出
        return;
    }
    mChangingActiveState = true;//更改中
    try {
        while (previousActiveCount != mActiveCount) {
            boolean needToCallActive = previousActiveCount == 0 && mActiveCount > 0;
            boolean needToCallInactive = previousActiveCount > 0 && mActiveCount == 0;
            previousActiveCount = mActiveCount;
            //如果当前是第一个激活的,调用 onActive
            if (needToCallActive) {
                onActive();//当活动的观察者从0 变成 1的时候调用
            //如果没有激活的为 0 ,调用 onInactive  
            } else if (needToCallInactive) {
                onInactive();//活跃的观察者变成 0 时调用
            }
        }
    } finally {
        mChangingActiveState = false;
    }
}
//分发数据
void dispatchingValue(@Nullable ObserverWrapper initiator) {
  if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;  //如果正在分发,退出  
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        //如果观察者不为空 
        if (initiator != null) {
            considerNotify(initiator);//对数据进行派发,通知观察者
            initiator = null;
        } else {
          //如果为空,遍历所有的观察者,将数据发送给所有观察者
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}
//数据进行派发,通知观察者
private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    //再次进行判断,如果不活跃,则会更新状态,然后退出
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    //观察者版本是否低于当前的版本
    //初始值 mLastVersion 为 -1,mVersion 为 0
    //小于等于表示没有需要更新的数据
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    //更新版本
    observer.mLastVersion = mVersion;
    //通知观察者
    observer.mObserver.onChanged((T) mData);
}

梳理流程:

1、通过 observe 添加一个观察者,这个观察者会被 LifecycleBoundObserver 进行一个封装,LifecycleBoundObserver继承 ObserverWrapper,并且实现了 LifecycleEventObserver。之后就会将观察添加到 Observers 中,最后注册页面生命周期的 observer。

2、当生命周期发生变化后,就会回调到 LifecycleBoundObserve 中的 onStateChanged 方法中。如果生命周期是销毁的,就会移除观察者,如果不是就会循环更新当前状态。

3、在更新状态的时候就会判断是否为活跃状态,如果是活跃状态就会进行分发,分发的时候如果观察者为 null ,就会遍历所有的观察者进行分发,否则就分发传入的观察者。

4、最后会再次判断活跃状态,已经判断观察者版本是否低于当前版本,如果都满足,就会更新观察者。

onActive 与 onInactive

如果观察者的生命周期处于 STARTED 或者 RESUMED 状态,LiveData 就会认为观察者处于活跃状态。

例如,下载数据需要在活跃状态下进行,或者需要实时的监听后台数据,就可以重新下面方法,并完成对应逻辑。

class StockLiveData(url: String) : LiveData<String>() {
    private val downloadManager = DownloadManager(url)
    override fun onActive() {
      if(!downloadManager.isStart()){
          downloadManager.start()
      }
    }
    override fun onInactive() {
         downloadManager.stop()
    }
}

当具有活跃的观察者时,就会调用 onActive 方法。

当没有任何活跃的观察者时,就会调用 onInactive 方法。

当然这只是我想到的场景,开发中可以根据不同的业务场景做出不同的判断。

四、LiveData 相关源码

MutableLiveData

由于 LiveData 的发送数据方法是 protected 修饰私有受保护的,所以不能直接调用。因此使用MutableLiveData 继承 LiveData 将发送数据的方法改为了 public。

public class MutableLiveData<T> extends LiveData<T> {
    /**
     * Creates a MutableLiveData initialized with the given {@code value}.
     */
    public MutableLiveData(T value) {
        super(value);
    }
    /**
     * Creates a MutableLiveData with no value assigned to it.
     */
    public MutableLiveData() {
        super();
    }
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }
    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}

Transformations.map()

在数据分发给观察者之前对其中存储的值进行更改,返回一个新的 LiveData,可以使用此方法。

val strLiveData = MutableLiveData<String>()
 val strLengthLiveData = Transformations.map(strLiveData) {
    it.length
 }
 strLiveData.observe(this) {
   Log.e("---345---> str:", "$it");
 }
 strLengthLiveData.observe(this) {
  Log.e("---345---> strLength:", "$it");
 }
strLiveData.value = "hello word"
E/---345---> str:: hello word
E/---345---> strLength:: 10

Transformations.switchMap()

对上面的做了个判断,根据不同的需求返回不同的 LiveData。也可以通过 id 去判断,返回对应的 livedata 即可。

val idLiveData = MutableLiveData<Int>()
val userLiveData = Transformations.switchMap(idLiveData) { id->
      getUser(id)
}

合并多个 LiveData

val live1 = MutableLiveData<String>()
val live2 = MutableLiveData<String>()
val mediator = MediatorLiveData<String>()
mediator.addSource(live1) {
  mediator.value  = it
  Log.e("---345---> live1", "$it");
}
mediator.addSource(live2){
  mediator.value  = it
  Log.e("---345---> live2", "$it");
}
mediator.observe(this, Observer {
  Log.e("---345---> mediator", "$it");
})
live1.value = "hello"
E/---345---> mediator: hello
E/---345---> live1: hello

通过 MediatorLiveData 将两个 MutableLiveData 合并到一起,这样当任何一个发生变化,MediatorLiveData 都可以感知到。

五、LiveData分发问题

数据粘性事件

例如再没有观察者的时候发送数据,此时 mVersion 1,等到真正添加了观察者后,生命周期也是活跃的,那么就会将这个数据重新分发到观察者。所以说发送数据这个操作是粘性的。

如果需要去除粘性事件,可以在添加完 observe 后去通过反射修改 mVersion 和 观察者包装类中的 mLastVersion 的值,将 mVersion 赋值给 mLastVersion 即可去掉粘性事件。

数据倒灌现象

一般情况下,LiveData 都是存放在 ViewModel 中的,当Activity重建的时候,观察者会被 remove 掉,重建后会添加一个新的观察者,添加后新的观察者版本号就是 -1,所以就会出现数据再次被接收到。

这种解决方式和上面一样,反射修改版本号就可以解决。非活跃状态的观察者转为活跃状态后,只能接收到最后一次发送的数据。一般情况下我们都需要的是最新数据,如果非要所有数据,只能重写 LiveData 了。

到此这篇关于Android Jetpack组件库LiveData源码深入探究的文章就介绍到这了,更多相关Android Jetpack LiveData内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Android Jetpack组件库LiveData源码深入探究的更多相关文章

  1. html5 canvas合成海报所遇问题及解决方案总结

    这篇文章主要介绍了html5 canvas合成海报所遇问题及解决方案总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Html5 video标签视频的最佳实践

    这篇文章主要介绍了Html5 video标签视频的最佳实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. HTML5在微信内置浏览器下右上角菜单的调整字体导致页面显示错乱的问题

    HTML5在微信内置浏览器下,在右上角菜单的调整字体导致页面显示错乱的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  4. ios – containerURLForSecurityApplicationGroupIdentifier:在iPhone和Watch模拟器上给出不同的结果

    我使用默认的XCode模板创建了一个WatchKit应用程序.我向iOSTarget,WatchkitAppTarget和WatchkitAppExtensionTarget添加了应用程序组权利.(这是应用程序组名称:group.com.lombax.fiveminutes)然后,我尝试使用iOSApp和WatchKitExtension访问共享文件夹URL:延期:iOS应用:但是,测试NSURL

  5. Ionic – Splash Screen适用于iOS,但不适用于Android

    我有一个离子应用程序,其中使用CLI命令离子资源生成的启动画面和图标iOS版本与正在渲染的启动画面完美配合,但在Android版本中,只有在加载应用程序时才会显示白屏.我检查了config.xml文件,所有路径看起来都是正确的,生成的图像出现在相应的文件夹中.(我使用了splash.psd模板来生成它们.我错过了什么?这是config.xml文件供参考,我觉得我在这里做错了–解决方法在config.xml中添加以下键:它对我有用!

  6. ios – 无法启动iPhone模拟器

    /Library/Developer/CoreSimulator/Devices/530A44CB-5978-4926-9E91-E9DBD5BFB105/data/Containers/Bundle/Application/07612A5C-659D-4C04-ACD3-D211D2830E17/ProductName.app/ProductName然后,如果您在Xcode构建设置中选择标准体系结构并再次构建和运行,则会产生以下结果:dyld:lazysymbolbindingFailed:Symbol

  7. Xamarin iOS图像在Grid内部重叠

    heyo,所以在Xamarin我有一个使用并在其中包含一对,所有这些都包含在内.这在Xamarin.Android中看起来完全没问题,但是在Xamarin.iOS中,图像与标签重叠.我不确定它的区别是什么–为什么它在Xamarin.Android中看起来不错但在iOS中它的全部都不稳定?

  8. 在iOS上向后播放HTML5视频

    我试图在iPad上反向播放HTML5视频.HTML5元素包括一个名为playbackRate的属性,它允许以更快或更慢的速率或相反的方式播放视频.根据Apple’sdocumentation,iOS不支持此属性.通过每秒多次设置currentTime属性,可以反复播放,而无需使用playbackRate.这种方法适用于桌面Safari,但似乎在iOS设备上的搜索限制为每秒1次更新–在我的情况下太慢了.有没有办法在iOS设备上向后播放HTML5视频?解决方法iOS6Safari现在支持playbackRat

  9. 使用 Swift 语言编写 Android 应用入门

    Swift标准库可以编译安卓armv7的内核,这使得可以在安卓移动设备上执行Swift语句代码。做梦,虽然Swift编译器可以胜任在安卓设备上编译Swift代码并运行。这需要的不仅仅是用Swift标准库编写一个APP,更多的是你需要一些框架来搭建你的应用用户界面,以上这些Swift标准库不能提供。简单来说,构建在安卓设备上使用的Swiftstdlib需要libiconv和libicu。通过命令行执行以下命令:gitclonegit@github.com:SwiftAndroid/libiconv-libi

  10. Android – 调用GONE然后VISIBLE使视图显示在错误的位置

    我有两个视图,A和B,视图A在视图B上方.当我以编程方式将视图A设置为GONE时,它将消失,并且它正下方的视图将转到视图A的位置.但是,当我再次将相同的视图设置为VISIBLE时,它会在视图B上显示.我不希望这样.我希望视图B回到原来的位置,这是我认为会发生的事情.我怎样才能做到这一点?编辑–代码}这里是XML:解决方法您可以尝试将两个视图放在RelativeLayout中并相对于彼此设置它们的位置.

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部