前言
说到MVVM,大家肯定能想到JetPack中的DataBinding,一个实现数据和UI绑定的框架,构建MVVM模式的一个工具。然而,在实际的项目使用中就是:“一千个读者就有一千个哈姆雷特”。对于一些新手来说,可能不理解它是怎么划分的,今天就让我们一起来打造一个属于自己的MVVM吧。
MVVM
MVVM全称Model View ViewModel,其优点:低耦合,数据和业务逻辑处于一个独立的ViewModel中,ViewModel只需要关注数据和业务逻辑,不需要和View层打交道。等等(这里就不详细说明了)
(结构图,个人理解)
Model
Model主要是封装数据存储或操作的一些逻辑。
项目划分: 例如:网络请求、数据库操作等。
View
View用于处理界面的逻辑且不参与业务逻辑相关的操作,只负责显示由ViewModel提供的数据,View层不做任何业务逻辑、不涉及操作数据、不处理数据,UI和数据严格的分开,对应于Activity、Fragment和XML。
项目划分:(Activity、Fragment)只做数据显示。
ViewModel
ViewModel是处理业务逻辑和业务数据相关的中枢,ViewModel层不会持有任何控件的引用。
项目划分:对Model层返回的数据做处理,并将数据通过JetPack部分框架(如DataBinding、LiveData)去刷新UI。
概念已经分析的差不多了,剩下的就可以动动手开搞了。
首先封装Model:
可以根据上面分析那样去封装,比如封装些网络请求实例对象,数据库实例对象,gson数据等。
open class BaseModel {
val apiService : ApiService by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
RetrofitManager.create(ApiService::class.java)
}
val gson : Gson by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
GsonBuilder().disableHtmlEscaping().create()
}
fun getMap(): TreeMap<String, String> {
return TreeMap(Comparator { o1, o2 ->
o1.compareTo(o2) //用正负表示大小值
})
}
fun TreeMap<String,String>.toJson() : String{
return gson.toJson(this)
}
fun String.getRequestBody() : RequestBody {
return this.toRequestBody("application/json;charset=UTF-8".toMediaType())
}
}
紧接着就是ViewModel,因为我用的RxJava,对RxJava进行了部分封装,还关联的页面的生命周期,页面销毁的时候可以自动注销页面的订阅。
open class BaseViewModel : ViewModel(),IViewModel {
val errorLiveData: MutableLiveData<String> = MutableLiveData()
private var compositeDisposable = CompositeDisposable()
override fun onCreate(owner: LifecycleOwner) {
//创建
}
override fun onDestroy(owner: LifecycleOwner) {
//销毁
detachView()
//移除生命周期监听观察者
owner.lifecycle.removeObserver(this)
}
override fun onLifecycleChanged(owner: LifecycleOwner, event: Lifecycle.Event) {
//生命周期状态改变
}
//泛型可以为 <T : BaseBean> ,也可以为 <T : List<BaseBean>>
//此处为Observable的拓展函数,你也可以改为Flowable的拓展函数
fun <T : BaseBean> Observable<T>.onResult(
next: Consumer<T>,
error: Consumer<Throwable> = Consumer {
errorLiveData.postValue(it.message)
}
) {
val disposable = this.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(next, error)
addSubscription(disposable)
}
private infix fun <T : BaseBean> Observable<T>.onResult(
next: Consumer<T>
) {
this.onResult(next,Consumer {
errorLiveData.postValue(it.message)
})
}
fun <T : BaseBean> Observable<T>.onResult(
next : (T) -> Unit
){
this onResult Consumer {
//这里进行返回判断
if (!TextUtils.equals(it.errorCode,"0")){
errorLiveData.value = it.errorMsg
return@Consumer
}
next(it)
}
}
private fun addSubscription(disposable: Disposable) {
compositeDisposable.add(disposable)
}
private fun detachView() {
//保证activity结束时取消所有正在执行的订阅
if (!compositeDisposable.isDisposed) {
compositeDisposable.clear()
}
}
}
最后就是View了,这里的封装只跟上面的关联。
open class BaseActivity : AppCompatActivity() {
@MainThread
fun <VM : BaseViewModel> createVModel(clazz: Class<VM>): VM {
val viewModel = ViewModelProvider(this)[clazz]
//绑定页面的生命周期
lifecycle.addObserver(viewModel)
//绑定默认的错误回调
bindErrorData(viewModel)
return viewModel
}
private fun bindErrorData(vm : BaseViewModel){
vm.errorLiveData.observe(this, Observer {
onError(it)
})
}
//公开个错误方法让子类回调处理
open fun onError(t:String){
if (!TextUtils.isEmpty(t)){
Toast.makeText(this,t,Toast.LENGTH_SHORT).show()
}
}
}
篇幅问题,我这里就不贴出子类代码了,完整的代码在github上,如果觉得对你有点帮助的可以点给免费的star。
项目地址
好了,说得再多也不如自己动手试试,不管谁的代码,自己写了,掌握了就是自己的代码。如有问题,欢迎指出。
github: https://github.com/cithrf/RxDemo