1 Overview
LiveData
It is an observable data storage class, LiveData
using the observer pattern, whenever the data changes, the objectLiveData
will be notified , and we can update in these objectsObserver
Observer
UI
ViewModel
Objects provide data for specific interface components (such as Fragment or Activity) and contain data processing business logic, which will be LiveData
used together
Next, we will first introduce how to use LiveData, and write a LiveData Demo, and then combine ViewModel to refactor the code of LiveData Demo
2. Instructions for using LiveData
LiveData<T>
Is an abstract class, it has 2 subclasses: MutableLiveData<T>
and MediatorLiveData<T>
, when writing code, is the subclass created. Let's first look at MutableLiveData<T>
the usage method, and then introduce how to use it in the following examplesMediatorLiveData<T>
2.1. Create a LiveData object
If the object class we want to observe is String
, create an MutableLiveData
object through the following code
MutableLiveData<String> liveData = new MutableLiveData<>();
2.2, observe the LiveData object
The data changes monitored by observe
the method , whenever a change occurs, the method will be called back and the content of the value will be returnedLiveData
LiveData
onChanged
String s
liveData.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "onChanged: " + s);
}
});
2.3. Update the LiveData object
LiveData
The passed setValue
method assigns a value, and the method LiveData
will be called back after each value is assignedonChanged
liveData.setValue("add for test");
When assigning aLiveData
value , the log will be output:"add for test"
onChanged
2021-03-20 21:59:21.483 26430-26430/com.example.livedatademo I/LiveDataDemo: onChanged: add for test
3, editing LiveData Demo
We create a Demo with only one main interface to display a number. This number will be displayed starting from 59, and then the number will decrease by 1 every second until the number becomes 0 TextView
.TextView
Demo running effect
Next, let's look at the difference between 不使用LiveData
and 使用LiveData
to implement this Demo
3.1. Do not use LiveData
-
The code description is as follows:
1. In the method ofMainActivity
adding onecountDown
, byCountDownTimer
creating a countdown timer
2.new CountDownTimer(1 * 60 * 1000, 1 * 1000)
The meaning of the two parameters: the first parameter indicates that the Timer总时长
is 60 seconds; the second parameter indicates that the timer每隔1秒
will be updated once and called backonTick
Method
3. Every timeonTick
the method is called, the contenttextView.setText(String.valueOf(l / 1000));
set once will be calledTextView
-
The corresponding code is as follows:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "LiveDataDemo";
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
countDown();
}
private void countDown() {
new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
@Override
public void onTick(long l) {
Log.i(TAG, "onTick: " + l);
textView.setText(String.valueOf(l / 1000));
}
@Override
public void onFinish() {
}
}.start();
}
}
- Disadvantages:
业务逻辑(倒计时的功能)
andUI逻辑(TextView更新)
not separated, coupled together
3.2, using MutableLiveData
为了方便演示,我们直接在MainActivity中操作LiveData,实际项目中不要这样编写代码,应该把LiveData放到ViewModel中
Google's recommended way to use LiveData
-
The code description is as follows:
1.MainActivity
Add along
type inMutableLiveData
2.onTick
Call each time the method is called back,liveData.setValue(l);
and set the latest value toLiveData
3. Set a listener for the called method. WheneverLivewData
the value of the method changes, the method will be called back . what is set inobserve
LiveData
onChanged
onChanged
TextView
-
The corresponding code is as follows:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "LiveDataDemo";
private final MutableLiveData<Long> liveData = new MutableLiveData<>();
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
// TODO:为了方便演示,我们直接在MainActivity中操作LiveData,实际项目中不要这样编写代码,应该把LiveData放到ViewModel中
liveData.observe(this, new Observer<Long>() {
@Override
public void onChanged(Long aLong) {
textView.setText(String.valueOf(aLong / 1000));
}
});
countDown();
}
public void countDown() {
new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
@Override
public void onTick(long l) {
Log.i(TAG, "onTick: " + l);
// TODO:为了方便演示,我们直接在MainActivity中操作LiveData,实际项目中不要这样编写代码,应该把LiveData放到ViewModel中
liveData.setValue(l);
}
@Override
public void onFinish() {
}
}.start();
}
}
3.3. Use MediatorLiveData
MediatorLiveData
Multiple sources can be merged and the object's observer will be triggered LiveData
whenever any of the original LiveData
source objects changes .MediatorLiveData
For example, if you have LiveData
an object , you can MediatorLiveData
add the following source to the object:
LiveData
Objects associated with data stored in the database .LiveData
An object associated with data accessed from the network .
Activity
Simply observe MediatorLiveData
the object to receive updates from both sources. Next, we will implement this example.
3.3.1. Monitor the changes of two data sources
-
The code description is as follows:
1. Create anMediatorLiveData<String>
objectfinal MediatorLiveData<String> liveDataMerger = new MediatorLiveData<>();
2. Create two
MutableLiveData
: liveData1 represents the network data source, liveData2 represents the local data sourceprivate MutableLiveData<String> liveData1 = new MutableLiveData<>(); private MutableLiveData<String> liveData2 = new MutableLiveData<>();
3. The method to call
MediatorLiveData
,addSource
respectively addliveData1
and to the data source to be monitoredliveData2
MediatorLiveData
liveDataMerger.addSource(liveData1, new Observer<String>() { @Override public void onChanged(String s) { Log.i(TAG, "addSource1 onChanged: " + s); liveDataMerger.setValue(s); } }); liveDataMerger.addSource(liveData2, new Observer<String>() { @Override public void onChanged(String s) { Log.i(TAG, "addSource2 onChanged: " + s); liveDataMerger.setValue(s); } });
4. Set
MediatorLiveData
the monitoring, when the two data sources change,onChanged
the method will be called back, and the changed content will be displayed onTextView
theliveDataMerger.observe(this, new Observer<String>() { @Override public void onChanged(String s) { Log.i(TAG, "liveDataMerger onChanged: " + s); textView.setText(s); }
});
3.3.2. Write the code that simulates the update of two data sources
Write a mergeTes
method, which is created 2个Timer
to simulate the situation of network data update and local data update. The value 3
will be set to the network data every second liveData1
, and the value 10
will be set to the local data every second liveData2
. The corresponding code is as follows:
public void mergeTest() {
new CountDownTimer(1 * 60 * 1000, 3 * 1000) {
@Override
public void onTick(long l) {
liveData1.setValue("网络有数据更新了" + l/1000);
}
@Override
public void onFinish() {
}
}.start();
new CountDownTimer(1 * 60 * 1000, 10 * 1000) {
@Override
public void onTick(long l) {
liveData2.setValue("本地数据库更新了" + l/1000);
}
@Override
public void onFinish() {
}
}.start();
}
3.3.4, MediatorLiveData running effect
Every 3 seconds, TextView
the update is as follows:
Network data has been updated
Every 10 seconds, TextView
the update is as follows:
Local data has been updated
4. Use of ViewModel
4.1 Create ViewModel class
Create a MyViewModel class that inherits from ViewModel
import androidx.lifecycle.ViewModel;
public class MyViewMode extends ViewModel {
}
4.2 Use in Activity
public class MainActivity extends AppCompatActivity {
private MyViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewModel = new ViewModelProvider(this).get(MyViewModel.class);
}
}
If you encounter new ViewModelProvider(this)
an error, add dependencies to app/build.gradleimplementation "androidx.lifecycle:lifecycle-viewmodel:2.3.0"
5. Combining with ViewModel, refactor LiveData Demo
5.1. Add LiveData to ViewModel
Add 3 MutableLiveData in MyViewModel, corresponding to those used in MainActivity. And write the get method of these 3 LiveData
public class MyViewModel extends ViewModel {
private MutableLiveData<Long> liveData = new MutableLiveData<>();
private MutableLiveData<String> liveData1 = new MutableLiveData<>();
private MutableLiveData<String> liveData2 = new MutableLiveData<>();
public MutableLiveData<Long> getLiveData() {
return liveData;
}
public MutableLiveData<String> getLiveData1() {
return liveData1;
}
public MutableLiveData<String> getLiveData2() {
return liveData2;
}
}
5.2. Add logic for manipulating data in ViewModel
The two methods of countDown and mergeTest are operating data, copy these two methods from MainActivity to MyViewModel
public class MyViewModel extends ViewModel {
private static final String TAG = "MyViewMode";
... ...
public void countDown() {
new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
@Override
public void onTick(long l) {
Log.i(TAG, "onTick: " + l);
liveData.postValue(l);
}
@Override
public void onFinish() {
}
}.start();
}
public void mergeTest() {
new CountDownTimer(1 * 60 * 1000, 3 * 1000) {
@Override
public void onTick(long l) {
liveData1.postValue("网络有数据更新了" + l / 1000);
}
@Override
public void onFinish() {
}
}.start();
new CountDownTimer(1 * 60 * 1000, 10 * 1000) {
@Override
public void onTick(long l) {
liveData2.postValue("本地数据库更新了" + l / 1000);
}
@Override
public void onFinish() {
}
}.start();
}
}
5.3 Modify the code related to liveData in MainActivity
1. Delete countDown and use viewModel.countDown() instead;
viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.countDown();
2. Replace liveData.observe with viewModel.getLiveData().observe
viewModel.getLiveData().observe(this, new Observer<Long>() {
@Override
public void onChanged(Long aLong) {
textView.setText(String.valueOf(aLong/1000));
}
});
3. Delete liveData related code
After the above modifications, the refactoring related to liveData has been completed, and the code of MainActivity is as follows:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "LiveDataDemo";
private TextView textView;
private MutableLiveData<String> liveData1 = new MutableLiveData<>();
private MutableLiveData<String> liveData2 = new MutableLiveData<>();
private MyViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.countDown();
.... ...
}
5.4. Modify MyViewModel
Add the liveDataMerger field and write the getLiveDataMerger() method. The content in this method is copied from MainActivity
public class MyViewModel extends ViewModel {
private static final String TAG = "MyViewMode";
.... ...
private MediatorLiveData<String> liveDataMerger;
public MediatorLiveData<String> getLiveDataMerger() {
if (liveDataMerger == null) {
liveDataMerger = new MediatorLiveData<>();
}
liveDataMerger.addSource(liveData1, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "addSource1 onChanged: " + s);
liveDataMerger.setValue(s);
}
});
liveDataMerger.addSource(liveData2, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "addSource2 onChanged: " + s);
liveDataMerger.setValue(s);
}
});
return liveDataMerger;
}
... ...
}
5.5. Modify the code related to MediatorLiveData in MainActivity
1. Use viewModel.getLiveDataMerger().observe to replace liveDataMerger.observe
viewModel.getLiveDataMerger().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "liveDataMerger onChanged: " + s);
textView.setText(s);
}
});
2. Use viewModel.mergeTest(); replace mergeTest()
viewModel.mergeTest();
3. Delete the code related to liveDataMerger
5.6 Code of MainActivity after refactoring
public class MainActivity extends AppCompatActivity {
private static final String TAG = "LiveDataDemo";
private TextView textView;
private MyViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.getLiveData().observe(this, new Observer<Long>() {
@Override
public void onChanged(Long aLong) {
textView.setText(String.valueOf(aLong / 1000));
}
});
viewModel.getLiveDataMerger().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "liveDataMerger onChanged: " + s);
textView.setText(s);
}
});
viewModel.mergeTest();
// viewModel.countDown();
}
}
5.7 Code of MyViewModel after refactoring
public class MyViewModel extends ViewModel {
private static final String TAG = "MyViewMode";
private MutableLiveData<Long> liveData = new MutableLiveData<>();
private MutableLiveData<String> liveData1 = new MutableLiveData<>();
private MutableLiveData<String> liveData2 = new MutableLiveData<>();
private MediatorLiveData<String> liveDataMerger;
public MediatorLiveData<String> getLiveDataMerger() {
if (liveDataMerger == null) {
liveDataMerger = new MediatorLiveData<>();
}
liveDataMerger.addSource(liveData1, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "addSource1 onChanged: " + s);
liveDataMerger.setValue(s);
}
});
liveDataMerger.addSource(liveData2, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, "addSource2 onChanged: " + s);
liveDataMerger.setValue(s);
}
});
return liveDataMerger;
}
public MutableLiveData<Long> getLiveData() {
return liveData;
}
public MutableLiveData<String> getLiveData1() {
return liveData1;
}
public MutableLiveData<String> getLiveData2() {
return liveData2;
}
public void countDown() {
new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
@Override
public void onTick(long l) {
Log.i(TAG, "onTick: " + l);
liveData.postValue(l);
}
@Override
public void onFinish() {
}
}.start();
}
public void mergeTest() {
new CountDownTimer(1 * 60 * 1000, 3 * 1000) {
@Override
public void onTick(long l) {
liveData1.postValue("网络有数据更新了" + l / 1000);
}
@Override
public void onFinish() {
}
}.start();
new CountDownTimer(1 * 60 * 1000, 10 * 1000) {
@Override
public void onTick(long l) {
liveData2.postValue("本地数据库更新了" + l / 1000);
}
@Override
public void onFinish() {
}
}.start();
}
}
6, reference
LiveData OverviewLiveData MediatorLiveData
ViewModel Overview