一在辆、MVC(Model-View-Controller)
- 模型層(Model)
負(fù)責(zé)存儲(chǔ)证薇、檢索、操縱來自數(shù)據(jù)庫或者網(wǎng)絡(luò)的數(shù)據(jù) - 視圖層(View)
用戶界面匆篓,一般采用XML文件進(jìn)行界面的描述 -
控制層(Controller)
業(yè)務(wù)邏輯處理
1. 工作原理
- 當(dāng)用戶出發(fā)事件的時(shí)候浑度,view層會(huì)發(fā)送指令到controller層,自己不執(zhí)行業(yè)務(wù)邏輯鸦概。
- Controller執(zhí)行業(yè)務(wù)邏輯并且操作Model箩张,但不會(huì)直接操作View,可以說它是對(duì)View無知的窗市。
- model層更新完數(shù)據(jù)然后對(duì)視圖進(jìn)行更新先慷,用戶得到反饋。
2. MVC代碼實(shí)例
- 1.先實(shí)現(xiàn)一個(gè) model咨察,需要有通知View更新的能力,當(dāng)model加載成功论熙,模擬從網(wǎng)絡(luò)或者本地獲取數(shù)據(jù),需要告知View更新:
public class MvcModel {
String mId;
MvcActivity mActivity;
public MvcModel (MvcActivity activity) {
this.mActivity = activity;
}
public void loadModel (){
//模擬從網(wǎng)絡(luò)或者本地獲取數(shù)據(jù)
mId = "20170923";
mActivity.updateUI(this);
}
}
- 2.View
View需要發(fā)出點(diǎn)擊事件摄狱,并且傳遞Controller,同時(shí)需要根據(jù)Model更新UI:
public class MvcActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
//省略
final MvcController controller = new MvcController(this);
mFindBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
controller.loadData();
}
});
}
public void updateUI (MvcModel model) {
mID.setText(model.mId);
}
}
- Controller :有時(shí)候我們的Activity既充當(dāng)View也充當(dāng)Controller脓诡, 這里為了更好的理解MVC无午,將Activity進(jìn)行了拆解。
public class MvcController {
MvcActivity mActivity;
MvcModel mModel;
MvcController (MvcActivity activity) {
this.mActivity = activity;
}
public void loadData (){
mModel = new MvcModel(mActivity);
mModel.loadModel();
}
}
3. MVC調(diào)用鏈
- View:OnClick ->
- Controller:loadData->
- Model:loadModel->
- View:updateUI
4. MVC優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):
把業(yè)務(wù)邏輯全部分離到Controller中祝谚,模塊化程度高宪迟。當(dāng)業(yè)務(wù)邏輯變更的時(shí)候,不需要變更View和Model踊跟,只需要Controller換成另外一個(gè)Controller就行了踩验。 - 缺點(diǎn):
1、 Controller測(cè)試?yán)щy商玫。因?yàn)橐晥D同步操作是由View自己執(zhí)行箕憾,而View只能在有UI的環(huán)境下運(yùn)行。在沒有UI環(huán)境下對(duì)Controller進(jìn)行單元測(cè)試的時(shí)候拳昌,Controller業(yè)務(wù)邏輯的正確性是無法驗(yàn)證的:Controller更新Model的時(shí)候袭异,無法對(duì)View的更新操作進(jìn)行斷言。
2炬藤、 xml作為view層御铃,控制能力實(shí)在太弱,Activity基本上都是View和Controller的合體沈矿,既要負(fù)責(zé)視圖的顯示又要加入控制邏輯上真,承擔(dān)的功能很多,導(dǎo)致代碼量很大羹膳。如想去動(dòng)態(tài)的改變一個(gè)頁面的背景睡互,或者動(dòng)態(tài)的隱藏/顯示一個(gè)按鈕,這些都沒辦法在xml中做陵像,只能把代碼寫在activity中就珠,造成了activity既是controller層。
3醒颖、 view層和model層之間存在耦合妻怎。
二、MVP(Model-View-Presenter)
- 模型層(Model)
負(fù)責(zé)存儲(chǔ)泞歉、檢索逼侦、操縱來自數(shù)據(jù)庫或者網(wǎng)絡(luò)的數(shù)據(jù)。 - 視圖層(View)
用戶界面腰耙,一般采用XML文件進(jìn)行界面的描述偿洁。 - 邏輯處理層(Presenter)
作為View與Model交互的中間紐帶,處理與用戶交互的負(fù)責(zé)邏輯沟优。
1. 工作原理
- View 接受用戶請(qǐng)求
- View 傳遞請(qǐng)求給Presenter
- Presenter做邏輯處理,修改Model
- Model 通知Presenter數(shù)據(jù)變化
- Presenter 更新View
2. MVP代碼實(shí)例
- MVP中Model睬辐、View挠阁、Presenter中的聯(lián)系件
public interface MvpContract {
interface View {
void updateUI();
}
interface Model {
void loadModel();
}
interface Presenter {
void loadData ();
}
}
- 還在MVC的例子上變動(dòng)宾肺,需要先對(duì)Model進(jìn)行封裝,當(dāng)loadModel后侵俗,不直接通知View更新锨用,而是通知Presenter。
public class MvpModel implements MvpContract.Model{
String mId;
OnGetListener mListener;
public MvpModel (OnGetListener listener) {
this.mListener = listener;
}
@Override
public void loadModel (){
//模擬從網(wǎng)絡(luò)或者本地獲取數(shù)據(jù)
mId = "20170923";
if (mListener != null) {
mListener.onSuccess(this);
}
}
interface OnGetListener {
void onSuccess (MvpModel model);
}
}
- View需要發(fā)出點(diǎn)擊事件隘谣,并且傳遞給Presenter 增拥,最后也由Presenter去通知View更新UI:
public class MvpActivity extends AppCompatActivity implements MvpContract.View {
@Override
protected void onCreate(Bundle savedInstanceState) {
//省略
final MvpPresenter presenter = new MvpPresenter(this);
mFindBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
presenter.loadData();
}
});
}
@Override
public void updateUI (MvpModel model) {
mID.setText(model.mId);
}
}
- Presenter ,接收到來自View的操作命令后寻歧,進(jìn)行邏輯處理掌栅,處理Model,修改完成后 通知View進(jìn)行修改码泛。
public class MvpPresenter implements MvpContract.Presenter {
MvpContract.View mView;
MvpContract.Model mModel;
MvpPresenter (MvpContract.View view) {
this.mView= view;
}
@Override
public void loadData (){
mModel = new MvpModel(new MvpModel.OnGetListener() {
@Override
public void onSuccess(MvpModel model) {
mView.updateUI(model);
}
});
mModel.loadModel ();
}
}
3. MVP調(diào)用鏈
- View:OnClick ->
- Presenter:loadData->
- Model:loadModel->
- Presenter:onSuccess->
- View:updateUI
4. MVP優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):
1猾封、便于測(cè)試。Presenter對(duì)View是通過接口進(jìn)行噪珊,在對(duì)Presenter進(jìn)行不依賴UI環(huán)境的單元測(cè)試的時(shí)候晌缘。可以通過Mock一個(gè)View對(duì)象痢站,這個(gè)對(duì)象只需要實(shí)現(xiàn)了View的接口即可磷箕。然后依賴注入到Presenter中,單元測(cè)試的時(shí)候就可以完整的測(cè)試Presenter業(yè)務(wù)邏輯的正確性阵难。
2岳枷、避免了傳統(tǒng)開發(fā)模式中View和Model耦合的情況,提高了代碼可擴(kuò)展性多望、組件復(fù)用能力嫩舟、團(tuán)隊(duì)協(xié)作的效率。 - 缺點(diǎn):
1怀偷、 View(Activity)需要持有Presenter的引用家厌,同時(shí),Presenter也需要持有View(Activity)的引用椎工,增加了控制的復(fù)雜度饭于;
2、MVC中Activity的代碼很臃腫维蒙,轉(zhuǎn)移到MVP的Presenter中掰吕,同樣造成了Presenter在業(yè)務(wù)邏輯復(fù)雜時(shí)的代碼臃腫。
三颅痊、MVVM(Model-View-ViewModel)
- 模型層(Model)
負(fù)責(zé)存儲(chǔ)殖熟、檢索、操縱來自數(shù)據(jù)庫或者網(wǎng)絡(luò)的數(shù)據(jù) - 視圖層(View)
用戶界面斑响,一般采用XML文件進(jìn)行界面的描述 -
視圖-模型層(ViewModel)
負(fù)責(zé)View和Model之間的通信菱属,以此分離視圖和數(shù)據(jù)钳榨。
1. 工作原理
- View 接收用戶交互請(qǐng)求
- View 將請(qǐng)求轉(zhuǎn)交給ViewModel
- ViewModel 操作Model數(shù)據(jù)更新
- Model 更新完數(shù)據(jù),通知ViewModel數(shù)據(jù)發(fā)生變化
- ViewModel 更新View數(shù)據(jù)
2. MVVM代碼實(shí)例
- 1.Model
public class MvvmModel {
String mId;
OnGetListener mListener;
public MvvmModel (OnGetListener listener) {
this.mListener = listener;
}
public void loadModel (){
//模擬從網(wǎng)絡(luò)或者本地獲取數(shù)據(jù)
mId = "20170923";
if (mListener != null) {
mListener.onSuccess(this);
}
}
interface OnGetListener {
void onSuccess (MvvmModel model);
}
}
- 2.ViewModel
public class MvvmViewModel extends BaseObservable{
public ObservableField<String> mId = new ObservableField<>();
public MvvmViewModel() {
}
public void doGetId(View view){
loadData();
}
public void loadData() {
MvvmModel model = new MvvmModel(new MvvmModel.OnGetListener() {
@Override
public void onSuccess(MvvmModel model) {
mId.set(model.mId);
}
});
model.loadModel();
}
}
- 3.接著使用databinding語法 對(duì) xml 進(jìn)行數(shù)據(jù)綁定,我們將 Click事件纽门、輸出結(jié)果都綁定到ViewModel上薛耻。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable name="viewModel" type="com.liujc.mvvm.MvvmViewModel"/>
</data>
<LinearLayout
<TextView
android:id="@+id/id"
android:layout_width="match_parent"
android:layout_height="20dp"
android:text="@{viewModel.mId}"/>
<Button
android:id="@+id/findBtn"
android:onClick="@{viewModel.doGetId}"
android:text="獲取Id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</layout>
- 4.最后在View(Activity)中引入ViewModel :
public class MvvmActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
ActivityMvvmBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_mvvm);
final MvvmViewModel viewModel = new MvvmViewModel();
binding.setViewModel(viewModel);
}
}
3.MVVM優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):
1、低耦合赏陵。View可以獨(dú)立于Model變化和修改饼齿,一個(gè)ViewModel可以綁定到不同的”View”上,當(dāng)View變化的時(shí)候Model可以不變蝙搔,當(dāng)Model變化的時(shí)候View也可以不變缕溉。
2、可重用性杂瘸。你可以把一些視圖邏輯放在一個(gè)ViewModel里面倒淫,讓很多view重用這段視圖邏輯。
3败玉、獨(dú)立開發(fā)敌土。開發(fā)人員可以專注于業(yè)務(wù)邏輯和數(shù)據(jù)的開發(fā)(ViewModel),設(shè)計(jì)人員可以專注于頁面設(shè)計(jì)运翼,生成xml代碼返干。
4、ViewModel解決MVP中View(Activity)和Presenter相互持有對(duì)方應(yīng)用的問題血淌,界面由數(shù)據(jù)進(jìn)行驅(qū)動(dòng)矩欠,響應(yīng)界面操作無需由View(Activity)傳遞,數(shù)據(jù)的變化也無需Presenter調(diào)用View(Activity)實(shí)現(xiàn)悠夯,使得數(shù)據(jù)傳遞的過程更加簡(jiǎn)潔癌淮,高效。 - 缺點(diǎn):
1沦补、ViewModel中存在對(duì)Model的依賴乳蓄。
2、數(shù)據(jù)綁定使得 Bug 很難被調(diào)試夕膀。你看到界面異常了虚倒,有可能是你 View 的代碼有 Bug,也可能是 Model 的代碼有問題产舞。
3魂奥、IDE不夠完善(修改ViewModel的名稱對(duì)應(yīng)的xml文件中不會(huì)自動(dòng)修改等)。
四易猫、DataBinding
DataBinding是2015年谷歌 I/O大會(huì)上介紹了一個(gè)數(shù)據(jù)綁定框架耻煤,以前我們可能需要在每個(gè)Activity里寫很多的findViewById,不僅麻煩,還增加了代碼的耦合性违霞,如果我們使用DataBinding嘴办,就可以拋棄那么多的findViewById,省時(shí)省力买鸽。
雙向綁定的概念讓傳統(tǒng)的布局文件由被動(dòng)轉(zhuǎn)為主動(dòng),數(shù)據(jù)驅(qū)動(dòng)UI贯被,而且View與ViewModel實(shí)現(xiàn)了完美的解耦眼五,這也解決了MVP模式下的缺點(diǎn)。
五彤灶、小結(jié)
- 從MVC看幼、MVP到MVVM,實(shí)際上是模型和視圖的分離過程幌陕。MVC中模型和視圖沒有完全分離诵姜,造成Activity代碼臃腫,MVP中通過Presenter來進(jìn)行中轉(zhuǎn)搏熄,模型和視圖徹底分離棚唆,但由于V和P互相引用,代碼不夠優(yōu)雅心例。ViewModel通過Data Binding實(shí)現(xiàn)了視圖和數(shù)據(jù)的綁定宵凌,解決了這種MVP的缺陷。
- 可參考一套Android App基礎(chǔ)框架
架構(gòu)設(shè)計(jì):從MVC止后、MVP到MVVM
網(wǎng)絡(luò)訪問:支持REST瞎惫、HTTPS及SPDY的Retrofit+Okhttp
響應(yīng)式編程:RxJava/RxAndroid解決方案
依賴注入:Dagger2和ButterKnife使用 - 框架的選擇
任何的項(xiàng)目框架,都是為項(xiàng)目服務(wù)的译株。沒有絕對(duì)的好壞之分瓜喇,只有更合適的選擇。在項(xiàng)目進(jìn)展的不同階段歉糜,做出最合適的調(diào)整乘寒,才是是更適合團(tuán)隊(duì)項(xiàng)目發(fā)展的框架。謹(jǐn)記任何的項(xiàng)目設(shè)計(jì)现恼,都是要圍繞項(xiàng)目發(fā)展階段肃续,團(tuán)隊(duì)成員規(guī)模,和團(tuán)隊(duì)整體能力而定的叉袍。切莫為了設(shè)計(jì)而設(shè)計(jì)始锚,為了框架而框架≡洌快速瞧捌,高效的配合整個(gè)團(tuán)隊(duì)進(jìn)展項(xiàng)目,才是最合適的架構(gòu)。