前言
說到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