前言
让我们从最简单的使用角度,让我们明白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
仅更新处于活跃生命周期状态(例如STARTED
或RESUMED
)的观察器。您可以在此处详细了解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_STARTED或Lifecycle.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
我们在前面已经进行了介绍。而MediatorLiveData
是MutableLiveDat
的子类。
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()
评论区