This article is first published in the wechat public account “Android development journey”, welcome to follow, get more technical dry goods
Wan-android Jetpack VersionAndroid Jetpack architecture for componentized application developmentWelcome to star
Project address of Flutter wan-AndroidFlutter Wan – AndroidWelcome to star
preface
In the previous article we looked at the specific use of Lifecycle. To understand this, we will look at LiveData and ViewModel together and use a simple Demo to illustrate how they work together.
So why do we use LiveData and ViewModel? What advantages do they have?
Before LiveData, general state distribution we used EventBus or RxJava, which were prone to memory leaks and required us to manually manage the life cycle. LiveData circumvents these problems. LiveData is a data container that holds Activity and Fragment lifecycles. Notify its observer to update the UI when the data source changes. It also notifies only observers in the Active state to update the screen, and does not notify an observer if Paused or Destroyed is in the state. So you don’t have to worry about memory leaks.
The ViewModel separates the view and logic. The Activity or Fragment is only responsible for the UI display. The ViewModel is responsible for specific network requests or database operations. This avoids view bloatiness and code coupling. As you can see from the ViewModel lifecycle below, the ViewModel is not destroyed when the screen rotates and the Activity is destroyed and recreated, which helps us retrieve data to update the UI after the Activity is recreated. It has an advantage over onSaveInstanceState because onSaveInstanceState is not suitable for recovery operations with a large amount of data. It can only recover a small amount of data that is serialized and deserialized, whereas ViewModel supports not only a large amount of data, Serialization and deserialization operations are not required.
Basic usage
First we add a dependency to build.gradle:
def lifecycle_version = "2.1.0."
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"/
Copy the code
Then we create the ViewModel class, which inherits the ViewModel and creates LiveData in it:
class SecondViewModel : ViewModel() {var userData: MutableLiveData<UserInfo> = MutableLiveData() /** *getUserInfo() {
val user = UserInfo("Bill", (1000.. 5000).random()) userData.postValue(user) } }Copy the code
Because LiveData is an abstract class, MutableLiveData is an implementation of it. PostValue and setValue are defined to notify observers of updates. PostValue is an asynchronous operation. SetValue is a synchronization operation.
We then create the ViewModel in the Activity and bind the UI component to the LiveData to update the data.
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activiry_second) val secondViewModel = ViewModelProviders.of(this).get(SecondViewModel::class.java) secondViewModel.userData.observe(this, Observer { mTvShow.text ="Name:${it.name}Salary: \ n${it.salary}"})/simulation data update * * * * / mBtnData setOnClickListener {secondViewModel. GetUserInfo ()}}}Copy the code
Here we get the ViewModelProvider via ViewModelProviders and get the ViewModel instance via get. We register a click event with the Button button to simulate data updates from the data source.
Data conversion
You can use the doubling operator if you want to make changes to the data you get from the server. There are two types: Map and switchMap. SwitchMap needs to return a LiveData object.
Transformations.map
Transformations.map(secondViewModel.userData, object : Function<UserInfo, UserInfo> {
override fun apply(userInfo: UserInfo): UserInfo {
userInfo.name = "Zhang"
return userInfo
}
}).observe(this, Observer {
mTvShow.text = "Name:${it.name}Salary: \ n${it.salary}"
})
Copy the code
This reassigns name to “Joe”.
Transformations.switchMap
For example, if we need to rely on other data for query, we can use switchMap.
Transformations.switchMap(secondViewModel.userData, object : Function<UserInfo, LiveData<UserInfo>> {
override fun apply(userInfo: UserInfo): LiveData<UserInfo> {
return secondViewModel.getUserName(userInfo)
}
}).observe(this, Observer {
mTvShow.text = "Name:${it.name}Salary: \ n${it.salary}"
})
Copy the code
Define the getUserName method in ViewModel
fun getUserName(userInfo: UserInfo): LiveData<UserInfo> {
userInfo.name = userInfo.name + "switchMap"
switchMapData.value = userInfo
return switchMapData
}
Copy the code
MediatorLiveData
When we need multiple data sources on a page, if we use LiveData individually, we will have a lot of observes defined in the Activity, resulting in a lot of unnecessary code. MediatorLiveData is designed to solve this problem. It can merge multiple LiveData together by defining an Observe once.
var data1: MutableLiveData<UserInfo> = MutableLiveData()
var data2: MutableLiveData<UserInfo> = MutableLiveData()
var mediatorLiveData: MediatorLiveData<UserInfo> = MediatorLiveData()
val user1 = UserInfo("Li si 1", (1000.. 5000).random()) val user2 = UserInfo("Li si 2", (1000.. 5000).random()) data1.postValue(user1) data2.postValue(user2) mediatorLiveData.addSource(data1, object : Observer<UserInfo> { override fun onChanged(info: UserInfo) { mediatorLiveData.value = info } }) mediatorLiveData.addSource(data2, object : Observer<UserInfo> { override fun onChanged(info: UserInfo) { mediatorLiveData.value = info } })Copy the code
For this we define two MutableLiveData to represent normal data fetching. MediatorLiveData merges datA1 and datA2 together to form a new LiveData using the addSource method. The onChanged callback represents a callback when datA1 and datA2 data sources send changes. Notification UI refreshes data.
We use:
secondViewModel.mediatorLiveData.observe(this, Observer {
if (it.name.contains("1")) {
mTvShow.text = "Name:${it.name}Salary: \ n${it.salary}"
} else {
mTvShowOther.text = "Name:${it.name}Salary: \ n${it.salary}"}})Copy the code
Here, different data source types are simply judged by name. This displays the data that datA1 and datA2 first requested in the interface. OnChanged monitors datA1 and datA2 data changes. Here we add a new button to the XML layout file to simulate data source changes.
Then simulate the refresh method first in ViewModel:
/** * Simulates data1 and data2 data source changes */ funupdate() {
val updateUser1 = UserInfo("Update", (1000.. 5000).random()) val updateUser2 = UserInfo("Update", (1000.. 5000).random()) data1.postValue(updateUser1) data2.postValue(updateUser2) }Copy the code
After the postValue execution completes, the onChanged method accepts the callback and notifies the UI to update the data.
Extension LiveData
LiveData considers the observer to be active if the observer’s life cycle is STARTED or RESUMED. If we know when active and Inactive are, then we can implement LiveDta ourselves. So LiveData provides two methods, onActive() and onInactive(). And gave the official Demo:
class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
private val stockManager = StockManager(symbol)
private val listener = { price: BigDecimal ->
value = price
}
override fun onActive() {
stockManager.requestPriceUpdates(listener)
}
override fun onInactive() {
stockManager.removeUpdates(listener)
}
}
Copy the code
The onActive method is called indicating that there is an active observer, so a listener is added to update the data. The onInactive method is called indicating that there are no active observers and the listener should be removed.
We use StockLiveData:
class MyFragment : Fragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) {
StockLiveData.get(symbol).observe(this, Observer<BigDecimal> { price: BigDecimal? ->
// Update the UI.
})
}
Copy the code
Scan the qr code below to follow the public account for more technical dry goods.
Recommended reading
Still not sure what Android Jetpack is? You went out
Android Jetpack Architecture Component – Lifecycle Into the pit Guide
Android Jetpack architecture component – Room in detail