正篇
首先,让我们看看LiveData不在ViewModel中创建的情景,我们新建一个Repository单例类:
object Repository { fun getUser(userId : String) : LiveData<User> { val liveData = MutableLiveData<User>() liveData .value = User(userId, userId, 0) return liveData } }
我们在此类中写了一个getUser()方法,该方法可以接受一个userId参数,每次将传入的userId当作用户姓名去创建一个新的User对象,而且需要注意的是,getUser()方法返回的是一个包含User数据的LiveData对象,且其每次调用都会获取LiveData对象。
接着我们在ViewModel中也定义一个getUser()方法,然后用它去调用Repository的getUser()方法从而获取LiveData对象,至此,我们构建了一个不在ViewModel中创建LiveData的情景,如果按之前的写法来:
class MainViewModel(countReserved: Int) : ViewModel() { ... fun getUser(userId : String) : LiveData<User> { return Repository.getUser(userId) } }
MainActivity中:
viewModel.getUser(userId).observe(this) { user -> }
上面的写法显然是行不通的,由于每次调用getUser方法都返回了新的LiveData实例,而MainActivity中观察的都是旧的实例,于是无法正确观测的数据变化,所以导致这种情况下LiveData无法观察。
于是,为了应对这种情况,我们就可以使用switchMap()方法,它的使用场景正是在如果ViewModel中的某个LiveData对象不是自己创建的而是调用其他方法获取的,那我们就可以用该方法去将LiveData对象转换成另一个可观察的LiveData对象:
class MainViewModel(countReserved: Int) : ViewModel() { ... private val userIdLiveData = MutableLiveData<String>() val user: LiveData<User> = Transformations.switchMap(userIdLiveData) { userId -> Repository.getUser(userId) } fun getUser(userId : String) { userIdLiveData.value = userId } }
与map()方法类似,不过第二个参数不同,我们必须在这个转换函数中返回一个LiveData对象,因为switchMap()方法的工作原理即将转换函数中返回的LiveData对象转换成另一个可观察的LiveData对象。 接下来再将xml布局与MainActivity对应调整: activity.xml再加一个按钮:
<Button android:id="@+id/getUserBtn" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="Get User"/>
MainActivity中添加按钮的事件与对应LiveData观察:
val getUserBtn : Button = findViewById(R.id.getUserBtn) getUserBtn.setOnClickListener { val userId = (0..10000).random().toString() viewModel.getUser(userId) } viewModel.user.observe(this, Observer { user -> infoText.text = user.firstName })
最后运行一下,我们一直点击“Get User”按钮,可以看到数据在随机变化,如下:
结语
到目前为止,我们已经把关于Jetpack组件LiveData中的相关部分说的差不多了,后面还有Room等,等待有时间后会继续更新。