(1)MVVM初見

MVVM架構(gòu)模式已經(jīng)非常火了,我還是只知其一不知其二,搜了很多帖子學(xué)習(xí),這里整理一下學(xué)習(xí)筆記。

a.便于理解,先把一些字面意思放在這里:
Model 模型(數(shù)據(jù)層)

是應(yīng)用程序中處理程序數(shù)據(jù)邏輯的部分,通常負(fù)責(zé)從本地?cái)?shù)據(jù)庫存取,遠(yuǎn)程存取數(shù)據(jù)。

View 視圖(UI顯示)

是應(yīng)用程序中處理數(shù)據(jù)顯示的部分,通常視圖都是依據(jù)模型來創(chuàng)建的。

Controller 控制者,管理者,調(diào)度員

在應(yīng)用程序中處理用戶交互的部分,通??刂破髫?fù)責(zé)從視圖讀取數(shù)據(jù),控制用戶輸入,并向模型發(fā)送數(shù)據(jù)。

Presenter 主持人
ViewModel 視圖模型
Repository 倉庫,存放處
1.MVC

MVC的模式優(yōu)點(diǎn)在于分離UI與業(yè)務(wù)職責(zé),增加可測(cè)試性與可擴(kuò)展性,但缺點(diǎn)是,View即依賴于Controller又依賴于Model,在修改UI內(nèi)容時(shí),也需要修改對(duì)應(yīng)的Model,降低架構(gòu)的靈活性,View和Model的職責(zé)存在部分重疊,耦合度高,實(shí)際操作中很難按照MVC模式嚴(yán)格去分離。

2.MVP

Model-View-Presenter ;MVP 是從經(jīng)典的模式MVC演變而來,它們的基本思想有相通的地方Controller/Presenter負(fù)責(zé)邏輯的處理,Model提供數(shù)據(jù),View負(fù)責(zé)顯示。
MVP模式的優(yōu)點(diǎn)是模型與視圖完全分離,我們可以修改視圖而不影響模型,因?yàn)樵谶@個(gè)模式下,所有的交互都發(fā)生在Presenter內(nèi)部,也利于脫離用戶接口來測(cè)試業(yè)務(wù)邏輯。缺點(diǎn)是會(huì)導(dǎo)致視圖和Presenter的交互過于頻分,Presenter與具體的View沒有直接關(guān)聯(lián),而是通過定義好的接口進(jìn)行交互,這會(huì)導(dǎo)致有大量的接口生成,造成代碼繁具,很難維護(hù),這也是很多像我一樣從入坑到放棄這種模式的原因。

3.MVVM

MVP的特質(zhì)是“依賴倒置”,MVVM的特質(zhì)是“數(shù)據(jù)驅(qū)動(dòng)”。
學(xué)這個(gè)的目的,是因?yàn)檫@句話,顯然說明二者并沒有誰演化自誰的關(guān)系了,JetPack MVVM是MVVM模式在Android開發(fā)中的具體落實(shí),“數(shù)據(jù)驅(qū)動(dòng)”這個(gè)理解有點(diǎn)難,看帖子不理解就找個(gè)教程寫個(gè)Demo理解一下,B站是個(gè)學(xué)習(xí)網(wǎng)站,很多優(yōu)質(zhì)UP主提供了幫助,這里貼一下一個(gè)小Demo:


籃球比賽計(jì)分器

一個(gè)計(jì)分器,點(diǎn)擊加分向?qū)?yīng)的隊(duì)加相應(yīng)的分?jǐn)?shù),點(diǎn)擊回退取消上一步的操作,點(diǎn)擊重置將兩隊(duì)的比分清零。
首先定義MyViewModel,繼承自ViewModel,在這里我們定義數(shù)據(jù),并處理邏輯。

public class MyViewModel extends ViewModel {
    // 定義兩隊(duì)的分?jǐn)?shù)
    // MutableLiveData可修改的LiveData,因?yàn)榉謹(jǐn)?shù)需要變化,LiveData
    private MutableLiveData<Integer> aTeamScore;
    private MutableLiveData<Integer> bTeamScore;
    //緩存兩隊(duì)當(dāng)前分?jǐn)?shù),用于回退一步
    private int aBack, bBack;

    public MutableLiveData<Integer> getATeamScore() {
        if (aTeamScore == null) {
            aTeamScore = new MutableLiveData<>();
            aTeamScore.setValue(0);
        }
        return aTeamScore;
    }

    public MutableLiveData<Integer> getBTeamScore() {
        if (bTeamScore == null) {
            bTeamScore = new MutableLiveData<>();
            bTeamScore.setValue(0);
        }
        return bTeamScore;
    }

    /**
     * 給A隊(duì)加分,在添加之前保留A、B兩隊(duì)的分?jǐn)?shù),用于在執(zhí)行undo操作時(shí)使用
     * @param p 要加的分?jǐn)?shù)
     */
    public void aTeamAdd(int p) {
        aBack = aTeamScore.getValue();
        bBack = bTeamScore.getValue();
        aTeamScore.setValue(aTeamScore.getValue() + p);
    }

    public void bTeamAdd(int p) {
        aBack = aTeamScore.getValue();
        bBack = bTeamScore.getValue();
        bTeamScore.setValue(bTeamScore.getValue() + p);
    }

    /**
     * 清零
     */
    public void reset() {
        aBack = aTeamScore.getValue();
        bBack = bTeamScore.getValue();
        aTeamScore.setValue(0);
        bTeamScore.setValue(0);
    }

    public void undo() {
        aTeamScore.setValue(aBack);
        bTeamScore.setValue(bBack);
    }
}

布局文件的內(nèi)容除了常規(guī)控件的內(nèi)容,也就是使用了JetPack提供的DataBinding,篇幅有限這里僅僅對(duì)新生事物粘貼一下:

<?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="data"
            type="com.nxhope.community.state.MyViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".ui.BasketballScoring">

      <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginBottom="8dp"
            android:text="@{String.valueOf(data.getATeamScore())}"
            android:textColor="#E91E63"
            android:textSize="80sp"
            app:layout_constraintBottom_toTopOf="@+id/guideline10"
            app:layout_constraintEnd_toStartOf="@+id/guideline7"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline9"
            tools:text="120" />

     <Button
            style="@style/button_one"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginBottom="8dp"
            android:onClick="@{()->data.aTeamAdd(1)}"
            android:text="+1"
            app:layout_constraintBottom_toTopOf="@+id/guideline11"
            app:layout_constraintEnd_toStartOf="@+id/guideline7"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline10" />

    <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="60dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginBottom="8dp"
            android:onClick="@{()->data.undo()}"
            app:layout_constraintBottom_toTopOf="@+id/guideline14"
            app:layout_constraintEnd_toStartOf="@+id/guideline7"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline13"
            app:srcCompat="@drawable/ic_baseline_undo_24" />

   </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

布局文件轉(zhuǎn)換為data-bingding layout這步可以不用手寫,windows用戶選中布局的根節(jié)點(diǎn)alt+enter就會(huì)提示出一個(gè)選項(xiàng)“convert to data binding layout" 的內(nèi)容, 轉(zhuǎn)換后會(huì)多出來<data>節(jié)點(diǎn)中的內(nèi)容,我們只需要name中命名,在type中導(dǎo)入我們定義好的ViewModel即可。

剩下的事情就是為控件綁定ViewModel提供的數(shù)據(jù)或者操作了,TextView中
android:text="@{String.valueOf(data.getATeamScore())}"為A隊(duì)分?jǐn)?shù)顯示區(qū)域綁定數(shù)據(jù),Button中
android:onClick="@{()->data.aTeamAdd(1)}"綁定了為A隊(duì)加1的操作,當(dāng)然加2加3就類似了,ImageButton綁定了我們?cè)赩iewModel中定義的undo()操作,也就是回退一步??梢钥吹?,到目前為止,我們好像只關(guān)心了布局文件和ViewModel的內(nèi)容,對(duì)于頁面Activity反而還沒下手,不像常規(guī)操作,之所以這樣,是因?yàn)锳ctiviy中真的沒啥可處理了,看代碼:

public class BasketballScoring extends AppCompatActivity {
    private MyViewModel viewModel;
    private ActivityBasketballScoringBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //DataBinding
        binding = DataBindingUtil.setContentView(this, R.layout.activity_basketball_scoring);
        //ViewModel
        viewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(MyViewModel.class);
        binding.setData(viewModel);
        binding.setLifecycleOwner(this);
    }
}

Activity中我們只是做了將布局和ViewModel綁定,當(dāng)然這里還寫了一些LiveData和Lifecycle的內(nèi)容(我還沒研究透徹,這里按下不表,聽下回分說),不是重點(diǎn),重點(diǎn)是,Activity中太簡(jiǎn)潔了,常見的FindViewById( ),setOnclickListener( ),setText( )統(tǒng)統(tǒng)不見蹤影,以及我們可能要定義的常量,Bean什么的也沒了,這就很香了。
玩玩這個(gè)Demo,比如刪除布局文件中的一些內(nèi)容,常規(guī)方式可能就會(huì)出現(xiàn)空指針,控件找不到造成的Crash,但現(xiàn)在不會(huì)了,MVVM模式用于解決“視圖調(diào)用一致性問題”這句話也誠不欺我,這時(shí)候再拿出KunMinX大佬的圖:



再品品,想總結(jié)些內(nèi)容,品出來的是:“我是小白,特別的白”、
帖子前面提到了Repository ,這里面并沒有說,本著的是不懂就不裝懂的原則,況且程序員有的東西懂了怎么用,不一定會(huì)表達(dá),繼續(xù)學(xué)習(xí),Android推出的JetPack內(nèi)容是時(shí)候?qū)W學(xué)了:


Jetpack
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容