1.LiveData介绍
LiveData是一个可被调查的数据容器类,它可以包含任何类型的数据。具体来说,可以将LiveData理解为一个数据的容器,它将数据包装起来,使数据成为调查者,当该数据产生改变时,调查者可以取得告诉。与常规的可调查类不同,LiveData可以感知(如Activity,Fragment或Service)的生命周期。
简略来说,LiveData具有如下优势
-
LiveData遵从调查者模式。当生命周期状况产生改变时,LiveData会告诉Observer目标,可以在这些Observer目标中更新界面。
-
不会内存泄漏
-
假如调查者的生命周期处于非活泼状况(如返回栈中的Activity),则它不会接纳任何LiveData事情,但是,当非活泼状况变成活泼状况时会马上接纳最新的数据(后台的Activity返回前台时)
-
当config导致Activity/Fragment重建时,不需求再手动的办理数据的存储与康复。
2.LiveData和ViewModel的关系
ViewModel用于存放页面所需求的各种数据,关于页面来说,它并不关怀ViewModel中的业务逻辑,它只关怀需求展现的数据是什么,而且希望在数据产生改变时,可以及时得到告诉并作出更新。
LiveData的作用便是,在ViewModel中的数据产生改变时告诉页面,用于包装ViewModel中哪些需求被外界调查的数据。
3.LiveData运用办法
首先,在ViewModel视图模型中定义LiveData数据,如 MutableLiveData
在该类中供给了postValue和setValue两个函数
- setValue() 只能在主线程中调用给LiveData设置数据
- postValue()用于在非主线程中给LiveData设置数据
然后,在Activity组件中,调用LiveData参数的observer办法,增加数据改变监听器 androidx.lifecycle.Observer,一旦LiveData数据产生了改动,就会回调Observer监听器中的onChanged函数
3.ViewModel+LiveData简略运用
3.1 在ViewModel中
首先在ViewModel中定义LiveData数据,因为LiveData是一个抽象类,不能直接运用它,所以咱们通常运用的都是它的直接子类MutableLiveData,然后定义了一个Integer类型的值,当该值产生改动时,会触发LiveData设置的Observer监听器。
3.2 在Activity中
在Activity体系组件中,绑定ViewModel,从ViewModel中获取LiveData显现到UI界面中,并为该LiveData设置Observer监听器,监听LiveData的数据改变 发动Timer定时器,修改ViewModel中的LiveData数据,在LiveData数据产生改动时,会自动回调Observer监听器的onChanged办法
public class MainActivity extends AppCompatActivity {
private MyViewModel myViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = findViewById(R.id.textview);
myViewModel = new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()))
.get(MyViewModel.class);
//将ViewModel中的数据设置到视图View组件中
textView.setText(String.valueOf(myViewModel.getSecond().getValue()));
//设置LiveData监听
myViewModel.getSecond().observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
//将ViewModel中的数据设置到视图View组件中
textView.setText(String.valueOf(integer));
}
});
startTimer();
}
public void startTimer() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
//获取ViewModel中的数据
Integer value = myViewModel.getSecond().getValue();
//将ViewModel中的数据自增1
myViewModel.getSecond().postValue(value + 1);
}
}, 1000, 1000);
}
}
3.3 运转作用:
应用发动后,在界面中发动定时器,对ViewModel中的LiveData数据进行累加,LiveData设置了Observer监听,数据改动时回调Observer的onChanged办法更新UI显现。就算切换屏幕反向,也不会影响数据累加显现
竖屏状况下
切换横屏数据依然存在
4.ViewModel+LiveData+Fragment运用
在Activity体系组件中设置两个Fragment,两个Fragment之间通过ViewModel+LiveData进行通讯
在其中一个Fragment中设置SeekBar拖动条,将数值设置到别的一个Fragment中的TextView中显现
4.1 ViewModel + LiveData代码
自定义ViewModel子类承继ViewModel,在ViewModel中,定义LiveData类型的数据,此处挑选运用MutableLiveData< Integer >数据类型,当该值产生改变的时分,会触发LiveData设置的Observer监听器
4.2 Activity组件代码
在该Activity组件中,维护了两个Fragment,两个Fragment之间凭借ViewModel+LiveData进行通讯
布局文件: 在Activity中设置了两个Fragment,他们之间凭借ViewModel+LiveData进行通讯
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".HomeActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainerView1"
android:name="com.wzj.livedata.Fragment1"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainerView2"
android:name="com.wzj.livedata.Fragment2"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline" />
</androidx.constraintlayout.widget.ConstraintLayout>
4.3 Fragment代码
第一个Fragment布局
Fragment中创建了一个SeekBar拖动条组件
第一个Fragment代码
先将ViewModel中的LiveData数据中的进展值给SeekBar,意图是为了在屏幕旋转的时分,可以随时康复数据
然后在SeekBar的拖动数据中,修改ViewModel中的LiveData数据 当数据修改时,对应的Fragment2中的TextView会改写显现新的数据
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_1, container, false);
//获取拖动条
SeekBar seekBar = view.findViewById(R.id.seekBar);
//获取ViewModel
HomeViewModel homeViewModel = new ViewModelProvider(requireActivity(), ViewModelProvider.AndroidViewModelFactory.getInstance(requireActivity().getApplication()))
.get(HomeViewModel.class);
seekBar.setProgress(homeViewModel.getProgress().getValue());
//设置进展条拖动事情
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
homeViewModel.getProgress().setValue(i);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
return view;
}
}
第二个Fragment布局
Fragment2中只要一个TextView
第二个Fragment代码
在 Fragment2 中 , 只放了一个 TextView 组件 , 该组件显现的是 ViewModel 中的 LiveData 数据 , 当该 LiveData 数据产生改动时 , 对应 TextView 显现也随之更新 ;
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_2, container, false);
//获取文本
TextView textView = view.findViewById(R.id.textViews);
//获取ViewModel
HomeViewModel homeViewModel = new ViewModelProvider(requireActivity(), ViewModelProvider.AndroidViewModelFactory.getInstance(requireActivity().getApplication())).get(HomeViewModel.class);
//设置数据
textView.setText(String.valueOf(homeViewModel.getProgress().getValue()));
homeViewModel.getProgress().observe(requireActivity(), new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
textView.setText(String.valueOf(integer));
}
});
return view;
}
}
5.Map和switchMap
5.1 Map
map函数首要的功用便是依据原LiveData,对其原LiveData的值进行改动然后生成一个新的LiveData。依据原LiveData。新的LiveData的值必须依据旧的LiveData中的值
Transformations.map
函数的作用是创建一个新的LiveData目标,这个新的LiveData目标的值是原始LiveData目标的值通过函数处理后的成果。这个函数接纳两个参数,一个LiveData目标和一个函数.这个函数将被应用到LiveData目标的每一个值上
举例说明:
在ViewModel中运用 Transformations.map将原LiveData进行更改
首先咱们定义一个实体类:
这便是一个一般的实体类,没什么好说的
public class User {
private String firstName;
private String lastName;
private int age;
public User(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
然后定义ViewModel
public class UserViewModel extends ViewModel {
private MutableLiveData<User> userLiveData;
private LiveData<String> userName;
private int countReserved;
public UserViewModel() {
this.countReserved = countReserved;
this.userLiveData = new MutableLiveData<>();
//运用转换函数map,创建一个新的LiveData目标
this.userName = Transformations.map(userLiveData, user -> user.getFirstName() + " " + user.getLastName());
}
public MutableLiveData<User> getUserLiveData() {
return userLiveData;
}
public void setUserLiveData(MutableLiveData<User> userLiveData) {
this.userLiveData = userLiveData;
}
public LiveData<String> getUserName() {
return userName;
}
public void setUserName(LiveData<String> userName) {
this.userName = userName;
}
public int getCountReserved() {
return countReserved;
}
public void setCountReserved(int countReserved) {
this.countReserved = countReserved;
}
}
注意看第11行的代码,
this.userName = Transformations.map(userLiveData, user -> user.getFirstName() + " " + user.getLastName());
这段代码的作用是创建一个新的LiveData目标userName,它的值是userLiveData中User目标的firstName和lastName,每当userLiveData的值产生改变时,userName的值也会相应的更新
在Activity中
public class UserActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user);
TextView tv_user = findViewById(R.id.tv_user);
Button btn_user = findViewById(R.id.btn_user);
UserViewModel userViewModel = new ViewModelProvider(this).get(UserViewModel.class);
btn_user.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
User user = new User("姓名1", "姓名2", 888);
//给ViewModel设置数据
Toast.makeText(UserActivity.this, user.getFirstName() +" "+ user.getLastName() +" "+ user.getAge(), Toast.LENGTH_SHORT).show();
userViewModel.getUserLiveData().postValue(user);
}
});
userViewModel.getUserName().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
tv_user.setText(userViewModel.getUserName().getValue());
}
});
}
}
在Activity中,咱们给User设置的值是两个姓名加年纪参数,然后在显现的时分,咱们用的是UserName的值 而不必本来的UserLiveData,所以打印出来的时分,应该只要两个姓名 而没有了年纪这个参数
5.2 switchMap
switchMap是依据传入的LiveData的值,然后判断这个值,然后再去切换或许构建新的LiveData。手动生成新的LiveData。
6.总结
LiveData之所以可以成为Activity与ViewModel之间通讯的桥梁,而且还不会有内存泄漏的危险,靠的便是Lifecycles组件。LiveData在内部运用了Lifecycles组件来自我感知生命周期的改变,从而可以在Activity毁掉的时分及时释放引用,避免产生内存泄漏的问题。
别的,因为要减少性能消耗,当Activity处于不行见状况的时分(比方手机息屏,或许被其他Activity遮挡),假如LiveData中的数据产生了改变,是不会告诉给调查者的。只要当Activity从头康复可见状况时,才会将数据告诉给调查者。而LiveData之所以可以完成这些细节的优化,依托的还是Lifecycles组件。
还有一个小细节,假如在Activity处于不行见状况的时分,LiveData产生了多次数据改变,当Activity康复可见状况时,只要最新的那份数据状况才会告诉给调查者,前面的数据在这种情况下相当于已通过期了,会被直接丢弃。