侧边栏壁纸
博主头像
Z同学博主等级

工作磨平激情前,坚持技术的热忱。 欢迎光临Z同学的技术小站。 分享最新的互联网知识。

  • 累计撰写 274 篇文章
  • 累计创建 55 个标签
  • 累计收到 74 条评论

Jetpack-lifecycle 组件LiveData 和ViewModel学习

Z同学
2021-08-09 / 0 评论 / 0 点赞 / 708 阅读 / 7,040 字
温馨提示:
本文最后更新于 2021-11-18,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

让我们从最简单的使用角度,让我们明白Lifecycle组件

依赖

我们以前使用Lifecycle 组件的时候,通常都是直接依赖

    implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'

但现在不用这样了。因为在最新版本之中extensions已经废弃。 最高版本 也就 2.2.0版本了。 不会有2.3版本了。

已经改为了下面的模式。根据自己的需要导入需要的库即可。

  // ViewModel
        implementation("androidx.lifecycle:lifecycle-viewmodel:2.3.1")
        // LiveData
        implementation("androidx.lifecycle:lifecycle-livedata:2.3.1")
        // 只有生命周期(没有ViewModel或LiveData)
        implementation("androidx.lifecycle:lifecycle-runtime:2.3.1")

        // Saved state module for ViewModel
        implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.1")

上面的例子是java的maven 库。如果需要Kotlin 的可以通过我们的
Android Jetpack 库介绍 - Z同学 (zinyan.com) 这篇文章了解需要导入的库

注意从extensions 切换到新的maven之后,会有部分API被废弃。需要我们重新调整。 在官网之中有相关介绍。

lifecycle 最重要的两个库 分别是ViewModel 和 LiveData 。 让我们从这两个库入手来了解lifecycle到底是干什么用的。

ViewModel

它主要用来保存和界面相关的一些数据,当界面发生重建时,不会影响到数据。但是绑定的Activity或者Fragment被销毁时,ViewModel将会被释放。

例如:横竖屏切换等界面发生重构,但是数据不希望它重新获取。

但是如果界面被回收了。我们又希望数据也被回收。

ViewModel 就是用来解决这个问题的。

从架构来说,就是mvvm架构的概率。将数据和视图分开。减少耦合。

最简单的例子来让我们明白ViewModel的意义:

第一步 添加maven依赖

implementation("androidx.lifecycle:lifecycle-viewmodel:2.2.0")

第二步 创建我们自己的ViewModel

package com.zinyan.demo;

import androidx.lifecycle.ViewModel;
public class MainViewModel extends ViewModel {
    
    public String title = "zinyan  的 ViewModel 示例";
    public int num = 0;
}

第三步 将ViewModel 绑定到Activity 之中

public class MainActivity extends AppCompatActivity {
    MainViewModel viewModel;
    private TextView tvTitle;

    @Override
    protected void onCreate(@Nullable  Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //得到ViewModel 对象。 并将MainViewModel 对象绑定到当前Activity 之中
        viewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(MainViewModel.class);
        tvTitle = findViewById(R.id.textView);
        //可以直接将我们定义的Model 之中的数据进行赋值
        tvTitle.setText(viewModel.title + ":" + viewModel.num);
    }

上面例子是绑定到Activity之中了。 我们也可以绑定到Fragment之中。

 viewModel = new ViewModelProvider(“Activity”, new ViewModelProvider.NewInstanceFactory()).get(MainViewModel.class);

 viewModel = new ViewModelProvider(“Fragment”, new ViewModelProvider.NewInstanceFactory()).get(MainViewModel.class);

第四步 重构Activity 看看数据是否会发生变化

tvTitle.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        viewModel.num++;
        ((TextView) v).setText(viewModel.title + ":" + viewModel.num);
        if (viewModel.num > 6 && getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
            //让屏幕改为横版
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        }
    }
});
<activity
    android:name=".MainActivity"
    android:screenOrientation="portrait">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

我们会发现,结果是ViewModel 的数据没有被屏幕重构造成重新开始。

ViewModel 的生命周期

生命周期图

因为ViewModel 的生命周期问题。所以在ViewModel之中绝对不能引用View,也不能存储Activity 和Fragment的上下文引用。

如果想释放ViewModel 之中的数据,可以重构onCleared() 方法。在ViewModel 被回收时,释放数据。

总结

ViewModel 就是为了将数据与UI 进行分开统一管理。同时也避免数据长时间持有造成内存压力。

其实我们自己创建一个全局变量进行存储也是一样可以实现的,无非就是Activity 销毁的时候释放一下。只是ViewModel库实现了这个功能,我们可以少创造一些轮子。

如果ViewModel 之中的数据发生了变化后,该如何通知Activity?我们说了ViewModel 不要持有Activity对象和View 对象。

以前我们都是通过定义接口。来实现两者之间的通讯。但是这样会创建大量的接口方法和函数。显得很繁琐。那么有没有更好的方法?

LiveData 就是为了解决这个情况而创建的。

LiveData

LiveData是一个可被观察的数据容器类

LiveData将数据封装成"被观察者",我们将UI注册成"观察者"。 这个时候当数据发生变化,就会通知"观察者",UI也就能够进行更新了。

同时,它具有生命周期感知能力。也就是说它的生命周期将绑定到Activity,Fragment,service。当这些对象被回收后LiveData 也将自动被销毁。

ViewModel 是用于存储页面所需的数据对象。也可以包括一些业务逻辑过程,例如数据获取,数据处理等等。页面只需要关注要展示的数据。

而当数据处理完毕后,要及时通知页面进行更新。LiveData 就是用来处理通知的功能。当界面被销毁后,LiveData之中存储的数据也将被回收。

类似于List 集合对象,LiveData 也是一个抽象类,我们没法直接使用。而通常直接使用它的子类: MutableLiveData

特性:

  • LiveData 可存储数据;LiveData 是一种可存储任何类型的数据的封装容器。
  • LiveData 是可观察的,这意味着当 LiveData 对象存储的数据发生更改时,观察器会收到通知。
  • LiveData 具有生命周期感知能力。当您将观察器附加到 LiveData 后,观察器就会与 LifecycleOwner(通常是 activity 或 fragment)相关联。LiveData 仅更新处于活跃生命周期状态(例如 STARTEDRESUMED)的观察器。您可以在此处详细了解 LiveData 和观察。

创建MutableLiveData 很简单

private MutableLiveData<String> mutableLiveData = new MutableLiveData<>();
private MutableLiveData<List<String>> mutableLiveData1 = new MutableLiveData<>();
//直接new 一个对象即可

之后就是给MutableLiveData 对象添加观察者,也称为观察器。

通过observe()函数来创建 观察者。

//例如 Activity 和Fragment。
LiveData.observe(“你的Activity”, new Observer<String>() {
    @Override
    public void onChanged(String s) {

    }
});
LiveData.observe(“你的Fragment”, new Observer<String>() {
    @Override
    public void onChanged(String s) {

    }
});

通过setValue()postValue() 函数 变更数据。

mutableLiveData .postValue("发送变化结果" + Math.random());

每一次调用上面的方法,都会回调到 onChanged() 得到最新更新结果。

setValue() : 可以在UI线层之中调用。

postValue() : 可以在子线层之中调用。

两个不管是什么线层调用,都会同步到onChanged() 函数。告知给观察者

创建观察者时有两个方法:

observe() : 会感知观察者的生命周期,只有页面处于激活状态(Lifecycle.State.ON_STARTEDLifecycle.State.ON_RESUME) 状态时,onChanged() 才能收到通知

observeForever() : 并不会感知观察者的生命周期,当LiveData 数据发生变化时,无论页面是什么状态,都能收到数据变化通知。

一般如果使用observeForever()后记得使用removeObserver() 方法移除观察者。否则LiveData 会一直处于激活状态,而绑定的观察者对象也将不会被系统回收。

同时: 当观察者从非活跃状态,变更为活跃状态时也会收到更新。而如果观察者第二次从非活跃状态变更为活跃状态,除非自上次变为活跃状态以来值发生了更新时,它才会收到更新。

LiveData 对象转换 -Transformations

Android 官网之中介绍LiveData 对象转换有些深奥。没办法立马理解。

下面我根据自己的理解描述一下这个函数和它的两个方法。

它主要有两个静态方法: 分别是 switchMap() 和 map() 。

使用场景1:

当我们有LiveData1 和LiveData2 两个数据对象时。

LiveData1 发生变化时,我们希望LiveData2 也发生变化。

那么就可以使用Transformations .实现链式关系了。

使用场景2:

如果我们根据LiveData1 的数据变换。然后决定第二个LiveData的数据格式。 那么也可以使用Transformations。

实例:

MutableLiveData<Integer> oneLiveData = new MutableLiveData<>();
MutableLiveData<String> twoLiveData;


twoLiveData = (MutableLiveData<String>) Transformations.switchMap(oneLiveData, new Function<Integer, LiveData<String>>() {
    @Override
    public LiveData<String> apply(Integer input) {
		 Log.e("xxx","switchMap 收到的通知" + input);
        MutableLiveData<String> temp = new MutableLiveData<String>();
        temp.postValue(input.toString());
       	//返回的 temp 就是 twoLiveData 的值了。
        return temp;
    }
});

//创建监听对象
oneLiveData.observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                Log.e("XXXX", "收到了来自oneLiveData 的参数变化通知" + integer.intValue());
            }
        });

//创建 
 twoLiveData.observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.e("XXXX", "Two 收到消息 的变化" + s);
            }
        });


oneLiveData.postValue(1)


//结果输出:
  收到了来自oneLiveData 的参数变化通知 1
  switchMap 收到的通知1
  Two 收到消息 的变化1

如果我们针对 twoLiveData 发送postValue() oneLiveData 不会产生变化。

而switchMap 和map 最大的区别也许就是返回值的不同了吧。

MutableLiveData 和MediatorLiveData

MutableLiveData我们在前面已经进行了介绍。而MediatorLiveDataMutableLiveDat 的子类。

MutableLiveData 主要是 postData()setData() 方法。

MediatorLiveData 主要是addSource() 方法: 可以实现监听另一个或者多个LiveData数据源变化。

private MediatorLiveData<Integer> mediatorLiveData = new MediatorLiveData<>();
mediatorLiveData.addSource(oneLiveData, new Observer<Integer>() {
    @Override
    public void onChanged(Integer integer) {
        //收到 来自old 的消息
    }
});
mediatorLiveData.addSource(twoLiveData, new Observer<String>() {
    @Override
    public void onChanged(String s) {
	// 收到来自 ss 的数据产生变化
    }
});
//如果要移除监听,调用removeSource方法 移除即可
mediatorLiveData.removeSource(ss);

其实
Transformations 也是使用了MediatorLiveData 的特性来实现的消息传递。

注意: 如果使用mediatorLiveData执行了addSource之后,

请不要忘记了给该mediatorLiveData 添加observer() . 否则将收不到addSource()

0

评论区