MVVMCapybara 一個Android 架構

一個基於Android MVVM的架構🐖

以下文章,我會把本架構稱爲 Capybara

放下Github鏈接先https://github.com/Ubitar/MVVMCapybara

Capybara 使用databinding + fragmentation搭建,僅包含ActivityFragment等基礎組件等功能,
可能有些人喜歡用 navigation,架構有分層,大家可以自己fork下來刪減定製。

  • 前言
  • 結構簡述
  • 食用方法
  • 常見問題

前言

在閱讀 Capybara 前,我會默認大家都看過databinding、fragmentation、LiveData的使用方法,及kotlin的使用,kt真香。

不過我希望先閱讀一下下面鏈接裏的大佬文章,加深對MVVM和數據驅動的瞭解。
https://www.zhihu.com/question/30976423/answer/106134677
https://www.jianshu.com/p/1fcda521fcda 禁止在layout中寫複雜邏輯

當然,如果你做過Vue或者微信小程序那更好理解了,即時那時你會嫌棄安卓的MVVM,或者充滿黑人問號

結構簡述

Capybara 主要通過讓組件繼承IView 、IViewModel 、IModel這3個接口來實現的。

結構

View 層可以爲你的Activity、Fragment或者DialogFragment
Model 層爲爲你的業務提供網絡請求服務或者數據庫讀存服務
ViewModel 中間層 則是負責處理你的業務邏輯,從Model中獲取數據進行處理,並對View進行更新的

圖中BaseMvvMActivity(我寫錯成了BseMvvMActivtivty了)、BaseViewModelBaseModel爲MVVM的實現抽象類,在不同的生命週期中實現並調用了MVVM接口。
BaseMvvMFragment也類似有同樣的實現,只不過因其生命週期,調用方法的位置有些不同,具體內容需要大家去瀏覽源碼。

如果你想支持例如Popup 之類的組件,也可以瞭解架構的大體走向後通過實現上方所提及的3個接口進行實現,當然,前提是你的這類popup組件得有一個說得過去的生命週期。

食用方法

1、新建一個佈局文件,裏面就只有一個按鈕
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable
            name="viewModelName"
            type="com.example.example.demo1.Demo1ViewModel" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:animateLayoutChanges="true"
        android:orientation="vertical">
        <Button
            android:id="@+id/btn1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text='@{"點擊這個按鈕  "+viewModelName.count}'
            android:textAllCaps="false"
            app:onClick="@{viewModelName.onClickBtn1}"
            tools:text="點擊這個按鈕" />
    </LinearLayout>
</layout>
2、新建一個ViewModel,繼承自BaseActivityViewModel

**注意:**這個ViewModel是繼承自ActivityViewModel的,因爲我們接下來要創建的是其相應的Activity,如果你要創建的是Fragment,那麼你需要繼承的則是BaseFragmentViewModel,同理,DialogBaseDialogViewModel

class Demo1ViewModel(application: Application) : BaseActivityViewModel<BaseModel>(application) {

    val count = MutableLiveData<Int>(0)

    /** 這個是 MVVM 中的  Model層,如沒有網絡或數據庫需求,傳NUll即可 */
    override fun getModel(): Class<BaseModel>? = null

    //用於初始化RecyclerView adapter的事件監聽
    override fun initEvent() {
        super.initEvent()
    }

    //初始化數據或者獲取數據
    override fun initData() {
        super.initData()
    }

    fun onClickBtn1(view: View) {
        count.value = count.value!! + 1
    }

}
3、新建你的Activity ,繼承 BaseActivity並實現getLayoutId()getViewModelId()提供佈局Id和ViewModel在佈局中的變量名稱
class Demo1Activity: BaseActivity<ActivityDemo1Binding, Demo1ViewModel>() {

    /** 你的佈局Id **/
    override fun getLayoutId(inflater: LayoutInflater, savedInstanceState: Bundle?): Int = R.layout.activity_demo1

    /** 佈局中ViewModel的Name  */
    override fun getViewModelId(): Int =BR.viewModelName

    /**
     * 下方函數運行順序(從上往下順序運行)
     *
     *  initParams()
     *  initViewModelParams()
     *  initView()
     *  onBindObservable()
     *  ViewModel.initEvent()
     *  ViewModel.initData()
     *
     */

    //用於接收並處理從上一個界面傳遞過來的數據
    override fun initParams() {
        super.initParams()
    }

    //如果有需要,可以通過這個函數把初始數據傳到ViewModel
    override fun initViewModelParams() {
        super.initViewModelParams()
        viewModel.count.value = 100
    }

    // 初始化RecyclerView或者其他View
    override fun initView() {
        super.initView()
    }

    // 註冊ViewModel中變量值的改變,可用於ViewModel向View傳遞信息或操作
    override fun onBindObservable() {
        super.onBindObservable()
    }
}
4、或許你的項目裏會有讀取數據庫或者網絡的時候,那麼你就需要實現相應的Model層,編寫一個Model層文件
class Demo3Model(
    private val viewModel: Demo3ViewModel
) : BaseModel(viewModel) {

    fun toLogin(account: String, password: String) {
        //模擬登錄
        Flowable.timer(1000, TimeUnit.MILLISECONDS)
            .doOnSubscribe { ToastUtils.showShort("加載中") }
                //這個是AutoDispose  如果要Rxlifecycle你要去想辦法自己更換
//            .`as`(AutoDisposeUtil.fromOnDestroy(viewModel.lifecycle.get()!!))
            .subscribe({
                viewModel.afterLoginSuccess()
            }, {
                println(it)
            }).isDisposed
    }

    override fun onCleared() {
        super.onCleared()
    }
}

在ViewModel處編寫getModel()函數的返回值,同時別忘了修改類上方的泛型

class Demo3ViewModel(application: Application) : BaseActivityViewModel<Demo3Model>(application) {

    //改這裏,還有上方的泛型
    override fun getModel(): Class<Demo3Model>? = Demo3Model::class.java

    var account = MutableLiveData<String>("")
    var password = MutableLiveData<String>("")

    fun onClickLogin(view: View) {
        // model是從父類繼承的Model層的引用
        model.toLogin(account.value ?: "", password.value ?: "")
    }

    fun afterLoginSuccess() {
        ToastUtils.showShort("登陸成功")
    }

}

關於fragmentation在這個架構上的應用可以查看源碼demo4

常見問題

1、這架構膠水代碼好多

額。。。。這個我也想解決,但是奈何我還不會寫插件一鍵生成

2、MVVM好用不

這個問題就很刁鑽了,MVVM和MVP一個自底向上,一個自頂向下,寫法完全相反,而且MVVM並不是android原生就有的,稍微複雜點的邏輯不能寫在xml(或者說太多if條件),xml報錯的時候也一臉懵逼,只能一個一個註釋排查,你想從不是MVVM架構的項目中複製代碼?對不起,大部分你都要重寫。
但是好處還是有的,感觸最深的還是改了只改變一個變量就顯示隱藏了多個View,其次是@BindingAdapter對UI操作的封裝,其他的還要自己去體會。

大哥,覺得可以的話Gayhub給個Star吧,他們是有交互效果的啊

知道這是啥不

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章