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é)充岛,增加可測試性與可擴(kuò)展性壁却,但缺點(diǎn)是,View即依賴于Controller又依賴于Model,在修改UI內(nèi)容時裸准,也需要修改對應(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)樵谶@個模式下榔昔,所有的交互都發(fā)生在Presenter內(nèi)部驹闰,也利于脫離用戶接口來測試業(yè)務(wù)邏輯。缺點(diǎn)是會導(dǎo)致視圖和Presenter的交互過于頻分撒会,Presenter與具體的View沒有直接關(guān)聯(lián)嘹朗,而是通過定義好的接口進(jìn)行交互,這會導(dǎo)致有大量的接口生成诵肛,造成代碼繁具屹培,很難維護(hù),這也是很多像我一樣從入坑到放棄這種模式的原因。
3.MVVM
MVP的特質(zhì)是“依賴倒置”褪秀,MVVM的特質(zhì)是“數(shù)據(jù)驅(qū)動”蓄诽。
學(xué)這個的目的,是因?yàn)檫@句話媒吗,顯然說明二者并沒有誰演化自誰的關(guān)系了若专,JetPack MVVM是MVVM模式在Android開發(fā)中的具體落實(shí),“數(shù)據(jù)驅(qū)動”這個理解有點(diǎn)難蝴猪,看帖子不理解就找個教程寫個Demo理解一下,B站是個學(xué)習(xí)網(wǎng)站膊爪,很多優(yōu)質(zhì)UP主提供了幫助自阱,這里貼一下一個小Demo:
一個計(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操作時使用
* @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吓歇,篇幅有限這里僅僅對新生事物粘貼一下:
<?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就會提示出一個選項(xiàng)“convert to data binding layout" 的內(nèi)容, 轉(zhuǎn)換后會多出來<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綁定了我們在ViewModel中定義的undo()操作簇搅,也就是回退一步∪硗拢可以看到瘩将,到目前為止,我們好像只關(guān)心了布局文件和ViewModel的內(nèi)容,對于頁面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中太簡潔了潮针,常見的FindViewById( ),setOnclickListener( ),setText( )統(tǒng)統(tǒng)不見蹤影倚喂,以及我們可能要定義的常量每篷,Bean什么的也沒了,這就很香了端圈。
玩玩這個Demo,比如刪除布局文件中的一些內(nèi)容焦读,常規(guī)方式可能就會出現(xiàn)空指針,控件找不到造成的Crash,但現(xiàn)在不會了舱权,MVVM模式用于解決“視圖調(diào)用一致性問題”這句話也誠不欺我矗晃,這時候再拿出KunMinX大佬的圖:
再品品,想總結(jié)些內(nèi)容宴倍,品出來的是:“我是小白喧兄,特別的白”、
帖子前面提到了Repository 啊楚,這里面并沒有說吠冤,本著的是不懂就不裝懂的原則,況且程序員有的東西懂了怎么用恭理,不一定會表達(dá)拯辙,繼續(xù)學(xué)習(xí),Android推出的JetPack內(nèi)容是時候?qū)W學(xué)了: