我正在使用mvvm和android体系结构组件,我是该体系结构的新手。
在我的应用程序中,我从Web服务中获取了一些数据,并在recycleView中显示了它们,效果很好。
然后有一个用于添加新数据的按钮,当用户输入数据时,它将进入Web服务,然后我必须获取数据并再次更新适配器。
这是我的活动代码:
private fun getUserCats() {
vm.getCats().observe(this, Observer {
if(it!=null) {
rc_cats.visibility= View.VISIBLE
pb.visibility=View.GONE
catAdapter.reloadData(it)
}
})
}
这是视图模型:
class CategoryViewModel(private val model:CategoryModel): ViewModel() {
private lateinit var catsLiveData:MutableLiveData<MutableList<Cat>>
fun getCats():MutableLiveData<MutableList<Cat>>{
if(!::catsLiveData.isInitialized){
catsLiveData=model.getCats()
}
return catsLiveData;
}
fun addCat(catName:String){
model.addCat(catName)
}
}
这是我的模型课:
class CategoryModel(
private val netManager: NetManager,
private val sharedPrefManager: SharedPrefManager) {
private lateinit var categoryDao: CategoryDao
private lateinit var dbConnection: DbConnection
private lateinit var lastUpdate: LastUpdate
fun getCats(): MutableLiveData<MutableList<Cat>> {
dbConnection = DbConnection.getInstance(MyApp.INSTANCE)!!
categoryDao = dbConnection.CategoryDao()
lastUpdate = LastUpdate(MyApp.INSTANCE)
if (netManager.isConnected!!) {
return getCatsOnline();
} else {
return getCatsOffline();
}
}
fun addCat(catName: String) {
val Category = ApiConnection.client.create(Category::class.java)
Category.newCategory(catName, sharedPrefManager.getUid())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ success ->
getCatsOnline()
}, { error ->
Log.v("this", "ErrorNewCat " + error.localizedMessage)
}
)
}
private fun getCatsOnline(): MutableLiveData<MutableList<Cat>> {
Log.v("this", "online ");
var list: MutableLiveData<MutableList<Cat>> = MutableLiveData()
list = getCatsOffline()
val getCats = ApiConnection.client.create(Category::class.java)
getCats.getCats(sharedPrefManager.getUid(), lastUpdate.getLastCatDate())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ success ->
list += success.cats
lastUpdate.setLastCatDate()
Observable.just(DbConnection)
.subscribeOn(Schedulers.io())
.subscribe({ db ->
categoryDao.insert(success.cats)
})
}, { error ->
Log.v("this", "ErrorGetCats " + error.localizedMessage);
}
)
return list;
}
我从活动中调用getCat,它将进入模型并将其发送到我的Web服务,成功后,我将调用getCatsOnline方法以再次从webservice中获取数据。
在我调试时,它获取数据,但不通知我的活动,这意味着观察者未在我的活动中触发。
我该如何解决?我的代码有什么问题?
答案 0 :(得分:0)
您在LiveData
和RxJava
的使用以及MVVM设计本身上犯了几个重要性不同的错误。
LiveData和RxJava
请注意,LiveData
和RxJava
是流。它们不是一次性使用的,因此您需要观察相同的LiveData
对象,更重要的是,需要更新相同的LiveData
对象。
如果您查看getCatsOnline()
方法,则每次调用该方法时,它都会创建一个全新的LiveData
实例。该实例与先前的LiveData
对象不同,因此侦听先前的LiveData
对象的任何内容都不会收到新更改的通知。
一些其他提示:
在getCatsOnline()
中,您正在另一订户内部预订Observable
。这是将RxJava
视为回叫的初学者的常见错误。这不是回叫,您需要链接这些呼叫。
请勿在 Model 层中subscribe
,因为它会中断流,并且您无法确定何时取消订阅。
永远不要使用AndroidSchedulers.mainThread()
。无需切换到 Model 层中的主线程,尤其是因为LiveData
观察者仅在主线程上运行。
请勿将MutableLiveData
暴露给其他层。只需返回LiveData
。
我要指出的最后一件事是您同时使用RxJava
和LiveData
。由于您都不熟悉这两者,因此我建议您只使用其中之一。如果您必须同时使用两者,请使用LiveDataReactiveStreams来正确地将两者桥接。
设计
如何解决所有这些问题?我猜您正在尝试执行以下操作:
(1)视图需要类别->(2)从服务器获取类别->(3)使用新的目录创建/更新可观察的list
对象,并将结果独立保存在数据库中->( 4)list
实例应自动通知活动。
很难正确实现这一点,因为您必须手动创建和更新此list
实例。您还需要担心list
实例的保留时间和保留时间。
更好的设计是:
(1)视图需要类别->(2)从数据库获取LiveData
并观察->(3)从服务器获取新类别并使用服务器响应来更新DB->(4)视图为自动通知,因为它一直在观察数据库!
这很容易实现,因为它具有以下一种依赖性:视图->数据库->服务器
示例CategoryModel:
class CategoryModel(
private val netManager: NetManager,
private val sharedPrefManager: SharedPrefManager) {
private val categoryDao: CategoryDao
private val dbConnection: DbConnection
private var lastUpdate: LastUpdate // Maybe store this value in more persistent place..
fun getInstance(netManager: NetManager, sharedPrefManager: SharedPrefManager) {
// ... singleton
}
fun getCats(): Observable<List<Cat>> {
return getCatsOffline();
}
// Notice this method returns just Completable. Any new data should be observed through `getCats()` method.
fun refreshCats(): Completable {
val getCats = ApiConnection.client.create(Category::class.java)
// getCats method may return a Single
return getCats.getCats(sharedPrefManager.getUid(), lastUpdate.getLastCatDate())
.flatMap { success -> categoryDao.insert(success.cats) } // insert to db
.doOnSuccess { lastUpdate.setLastCatDate() }
.ignoreElement()
.subscribeOn(Schedulers.io())
}
fun addCat(catName: String): Completable {
val Category = ApiConnection.client.create(Category::class.java)
// newCategory may return a Single
return Category.newCategory(catName, sharedPrefManager.getUid())
.ignoreElement()
.andThen(refreshCats())
.subscribeOn(Schedulers.io())
)
}
}
我建议您通读Google的Guide to App Architecture和其中一个livedata-mvvm示例app。