Android-MVP設計模式(基礎)

MVP 全稱:Model-View-Presenter

MVP 是從經典的模式MVC演變而來,它們的基本思想有相通的地方:Controller/Presenter負責邏輯的處理,Model提供數據,View負責顯示。


模式描述

Model-view-presenter (MVP)使用者界面 設計模式的一種,被廣范用于便捷自動化單元測試和在呈現邏輯中改良分離關注點(separation of concerns)。

  • Model 定義使用者界面所需要被顯示的資料模型,一個模型包含著相關的業務邏輯。
  • View 視圖為呈現使用者界面的終端,用以表現來自 Model 的資料,和使用者命令路由再經過 Presenter 對事件處理后的資料。
  • Presenter 包含著元件的事件處理,負責檢索 Model 取得資料,和將取得的資料經過格式轉換與 View 進行溝通。

下面一個圖就能很好展示它們之間的關系

Paste_Image.png

那我們開始實例講解

現在我們做一個小Demo。輸入一個qq號,能查到QQ號碼吉兇
請求示例:
http://api.jisuapi.com/qqluck/query?qq=22222&appkey=yourappkey

Json返回示例

{
"status": "0",
"msg": "ok",
"result": {
    "qq": "22222",
    "score": "62",
    "luck": "兇",
    "content": "煩悶懊惱,事事難展,自防災禍,始免困境",
    "character": "要面包不要愛情",
    "characterdetail": "責任心重,尤其對工作充滿熱誠,是個徹頭徹尾工作狂。但往往因為過分專注職務,而忽略身邊的家人及朋友,是個寧要面包不需要愛情的理性主義者。"
}
}
Paste_Image.png

項目結構

Paste_Image.png

其中api 是接口。bean 是javabean 用來作數據模型。model來進行網絡請求獲取數據。presenter來進行事件處理。把處理好的數據通過一定方式傳給view ,也就是我們的activity,然后展示給用戶看。
所以這就結構就看著非常簡單。model 只做網絡請求,presenter把請求的數據處理好 傳給view,然后view在展示給用戶看。這中間view和model是沒有任何的關聯,只是有個中間著presenter來進行相應的處理

我們接下來先把api 和bean 填上,代碼如下

網絡請求用的retrofit框架

compile 'com.squareup.retrofit2:retrofit:2.2.0'
compile 'com.squareup.retrofit2:converter-gson:2.2.0'
Paste_Image.png

MyService類,簡單封裝網絡請求

public class MyService {

public static String baseUrl="http://api.jisuapi.com/qqluck/";
public static Retrofit mRetrofit=new Retrofit.Builder()
        .baseUrl(baseUrl)
        .addConverterFactory(GsonConverterFactory.create())
        .build();
public static <T> T getApi(Class<T> mClass){
    return mRetrofit.create(mClass);

}
}

DemoApi類, api接口類

public interface DemoApi {
@POST("query")
Call<QqluckData> loadqQuery(@Query("qq") int qq, @Query("appkey") String appkey);

}

接下來開始寫view 。因為我們要做一個qq兇吉,那么就要把展示結果給用戶看。所以先寫一個接口.只有一個方法。用來接收presenter傳過了的數據,展示給用戶看

public interface IMainView {
void showInfo(String response);
}

然后用Maintivivty來繼承IMainView接口,實現其中的方法,獲得數據,展示給用戶看,代碼如下

public class MainActivity extends AppCompatActivity implements IMainView {
private TextView mluck;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mluck= (TextView) findViewById(R.id.luck);
}

public void onClick(View v){
    switch (v.getId()){
        case R.id.send:

            break;

    }

}

@Override
public void showInfo(String response) {
    mluck.setText(response);
}
}

現在基本完成了一半,下面接著寫model 的網絡請求
根據上面的請求示例,我們需要傳一個qq號,和一個appkey。appkey去注冊申請一個就好,在這就不多說,另外需要一個Callback回調函數。需要把請求到的數據傳給presenter。model也到這差不多了。

public class MainModel {

public void getData( int qq, String appkey,  Callback<QqluckData> mCallback) {

    DemoApi api = MyService.getApi(DemoApi.class);
    Call<QqluckData> qqluckDataCall = api.loadqQuery(qq, appkey);
    qqluckDataCall.enqueue(mCallback);

}
}

現在就開始寫presenter,代碼如下

public class MainPresenter {
public IMainView mIMainView;//view 的引用
public MainModel mMainModel;//model的引用

public MainPresenter(IMainView IMainView) {
    mIMainView = IMainView;
    mMainModel=new MainModel();
}

public void load(int qq){//加載數據。通過model 來獲取
    String appkey="34865d1e2ff7170f";
    mMainModel.getData(qq, appkey, new Callback<QqluckData>() {
        @Override
        public void onResponse(Call<QqluckData> call, Response<QqluckData> response) {
            //獲取到數據
            QqluckData body = response.body();
            //調用showInfo方法,傳遞數據。展示給用戶看
            mIMainView.showInfo("luck:"+body.getResult().getLuck()+"\ncontent:"+body.getResult().getContent());
        }

        @Override
        public void onFailure(Call<QqluckData> call, Throwable t) {

        }
    });

}
}

presenter 代碼沒多少。持有view 和midel的引用。 當view初始化presenter時會把view 傳過來。presenter內部在持有Model的引用。當用戶發起qq測吉兇功能時,由view 調用presenter的load方法。presenter在由model去發送網絡請求。把數據結果在傳回給view。下面我在把完整的Maintivit 貼出來

public class MainActivity extends AppCompatActivity implements IMainView {
private TextView mluck;
private MainPresenter mMainPresenter;
private EditText mEdqq;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mluck= (TextView) findViewById(R.id.luck);
    mEdqq= (EditText) findViewById(R.id.ed_qq);
    //onCreate時初始化MainPresenter,因為當前已繼承IMainView,直接傳this即可。這個時候MainPresenter就持有IMainView的引用了
    mMainPresenter=new MainPresenter(this);

}

public void onClick(View v){
    switch (v.getId()){
        case R.id.send:
            if(TextUtils.isEmpty(mEdqq.getText().toString())){
                return;
            }
//發送請求。調取MainPresenter.load的方法
      mMainPresenter.load(Integer.parseInt(mEdqq.getText().toString()));
            break;

    }

}

@Override
public void showInfo(String response) {
    //展示結果
    mluck.setText(response);

}
}

好了。到此一個簡單的mvp demo已經寫完了 。。或許有很多人在此覺得mvp 太麻煩了。一個簡單的網絡請求,寫的這么多類,這么多代碼。但是我要說的是,你看我們的activity類。代碼是不是非常少,是不是很簡潔,其他的也都分工很明確。耦合性也很低。便于后期維護,這個時候mvp 的優點便體現出來了。
對了。展示一下結果

WechatIMG3.jpeg

MVP的優點

  • 1、模型與視圖完全分離,我們可以修改視圖而不影響模型
  • 2、可以更高效地使用模型,因為所有的交互都發生在一個地方——Presenter內部
  • 3、我們可以將一個Presenter用于多個視圖,而不需要改變Presenter的邏輯。這個特性非常的有用,因為視圖的變化總是比模型的變化頻繁。
  • 4、如果我們把邏輯放在Presenter中,那么我們就可以脫離用戶接口來測試這些邏輯(單元測試)

MVP的缺點

  • 由于對視圖的渲染放在了Presenter中,所以視圖和Presenter的交互會過于頻繁。還有一點需要明白,如果Presenter過多地渲染了視圖,往往會使得它與特定的視圖的聯系過于緊密。一旦視圖需要變更,那么Presenter也需要變更了。比如說,原本用來呈現Html的Presenter現在也需要用于呈現Pdf了,那么視圖很有可能也需要變更。

如果有什么問題不懂。或者我那個地方寫的有問題。歡迎小伙伴提出。最后說一下。MVP只是個設計模式。不一定非要按部就班。我們要活學活用。適合自己的才是最好的。

源代碼
https://github.com/ccicec/AndroidMvpDemo/tree/master
A New Day

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容