1. 程式人生 > >Android程式碼架構之MVP

Android程式碼架構之MVP

1、引言

9月到了,不知道同學們的暑假作業有沒有抄完,想到學校已經離我遠去,還真的有點傷感呢,不過沒關係,即使離開了學校咱還是可以繼續學習是不是,好吧,讓我們來開始今天的學習MVP架構

2、為什麼要學習MVP架構?

相信很多同學之前都跟我這個菜雞一樣,寫程式碼的時候都是想到哪兒寫到哪兒,從左到右,從上到下,寫完之後執行一下,沒有Bug繼續往下寫,這樣做似乎一直也沒出什麼問題 但是,我們發現這樣做的結果就是我們的某個類中往往寫了大量的程式碼和邏輯,一個專案中行數過千的類估計也不在少數,在寫了一段時間後別人很難看懂我們的思路,有時甚至是我們自己也會被這上千行的程式碼搞得頭皮發麻,其實,將所有邏輯都寫在一個類中這首先就不符合我們的軟體設計原則中的單一職責原則,那麼怎麼樣才能使我們的程式碼變得更優雅,每一個類都各司其職呢?答案就是本片文章的主角——MVP架構

3、MVP架構的概念

要理解MVP的概念我們需要分別理解這M,V,P在我們的專案中分別負責做哪些事,他們在專案中具體對應哪些類? 首先說M,M是Model的縮寫,是模型的意思,他在我們的專案中通常負責一些資料的獲取與操作,在專案中具體對應那些實體模型類和一些寫網路或本地資料獲取邏輯的類;V是View的縮寫,,通常負責介面的展示和人機互動的邏輯編寫,在專案中具體表現為XML佈局,自定義的佈局類,Activity和Fragment等,P是Presenter的縮寫,字面意思是主持人,其實他就是M和View的互動中間人,我們之前寫程式碼的時候之所以會出現將所有邏輯和程式碼都寫在一個類中的情況其實就是因為我們沒有將邏輯程式碼和檢視操作程式碼分離,導致我們的activity,fragment中既有他們自己該做的檢視相關的工作還有本來不應該屬於他們的邏輯程式碼操作,如我們能通過Presenter這個中間人將所有的邏輯程式碼都寫到Model中,使我們的檢視層只做他們該做的事豈不美哉!不知道上面說了一大通諸位理解了沒有,為了方便大家理解,去網上盜波圖先
從上圖中我們發現,View和Model互不干涉,各司其職,如果他們想發生關係必須通過Presenter這個中間人來處理,光說不練假把式,說得再多,不會敲還是沒用,下面我將會通過一個註冊的Demo來給大家演示MVP的寫法

4、程式碼演示

首先可以先給大家看一下沒有采用MVP架構時註冊的寫法
package com.example.machenike.netdemo10;

import android.app.ProgressDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import 
android.view.View; import android.widget.EditText; import android.widget.Toast; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; public class RegisterActivity extends AppCompatActivity { private EditText mUsername; private EditText mPassword; private String mUsername1; private String mPassword1; private ProgressDialog mProgressDialog; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_register); mUsername = (EditText) findViewById(R.id.username); mPassword = (EditText) findViewById(R.id.password); } public void register(View view) { mUsername1 = mUsername.getText().toString().trim(); mPassword1 = mPassword.getText().toString().trim(); User user = new User(); user.setUserName(mUsername1); user.setPassword(mPassword1); mProgressDialog = ProgressDialog.show(this, "註冊", "玩命兒註冊中....."); RetrofitClient.getInstance().getAPI().postRequest(user).enqueue(new Callback<UserResult>() { @Override public void onResponse(Call<UserResult> call, Response<UserResult> response) { mProgressDialog.dismiss(); if (response.isSuccessful()){ UserResult userResult = response.body(); if (userResult!=null){ if (userResult.getErrcode()==1){ Toast.makeText(RegisterActivity.this,"註冊成功!!",Toast.LENGTH_SHORT).show(); return; } Toast.makeText(RegisterActivity.this,userResult.getErrmsg(),Toast.LENGTH_SHORT).show(); return; } Toast.makeText(RegisterActivity.this,"未知錯誤!",Toast.LENGTH_SHORT).show(); } } @Override public void onFailure(Call<UserResult> call, Throwable t) { mProgressDialog.dismiss(); Toast.makeText(RegisterActivity.this,"註冊失敗",Toast.LENGTH_SHORT).show(); } }); } }
從上面的程式碼中我們可以看出彈吐司,進度條,網路請求等一系列程式碼全部擠在了RegisterActivity這個類中,有些同學可能會說了,這不是看著挺好的麼。是,是挺好的,可這只是一個Demo如果在真實的開發環境下,你的Activity中都只會是這點程式碼麼?所以我們需要將裡面的程式碼進行拆分,我們可以將裡面的邏輯分為兩大類,一個是與檢視相關的,一個是與檢視無關的,由於Activity本身就屬於檢視層,所以很顯然,與檢視相關的程式碼我們可以保留,與檢視無關的拆分到Model中,在這裡哪些與檢視相關呢,哪些與檢視無關呢?很顯然,彈吐司,進度條這些與檢視有關,網路請求與檢視無關,所以我們要拆分出去的程式碼主要就是這一段
下面我將會貼出採用MVP的註冊的寫法,讓大家理解拆分的過程
首先給大家看一下我的專案結構
在這個工程中我建了m,v,p,net四個包,在m中還有個entity放置實體類的二級包,net包中是我封裝的網路請求客戶端,這裡不做重點討論,m,v,p這個三個包中分別放置MVP對應的檔案,我們會發現,基本上每個類都一個介面與之對應,這裡也是借鑑了一些面向介面程式設計的思想,看完了專案結構,給大家看一下Model中我們是怎麼寫的
首先看介面
接著看他的實現類
package com.example.machenike.mvpdemo.m;

import android.util.Log;

import com.example.machenike.mvpdemo.m.entity.User;
import com.example.machenike.mvpdemo.m.entity.UserResult;
import com.example.machenike.mvpdemo.net.RetrofitClient;
import com.example.machenike.mvpdemo.p.RegisterPresenter;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

/**
 * Created by MACHENIKE on 2017/9/1.
 */
public class RegisterModeImpl implements RegisterModel{
    private RegisterPresenter mRegisterPresenter;

    public RegisterModeImpl(RegisterPresenter registerPresenter) {
        mRegisterPresenter = registerPresenter;
    }

    @Override
public void register(User user) {
        Log.e("============","RegisterModeImpl");
        //顯示進度條
mRegisterPresenter.showProgress();
        RetrofitClient.getInstance().getAPI().register(user).enqueue(new Callback<UserResult>() {
            @Override
public void onResponse(Call<UserResult> call, Response<UserResult> response) {
                //隱藏進度條
mRegisterPresenter.hideProgress();
                if (response.isSuccessful()){
                    UserResult userResult = response.body();
                    if (userResult==null){
                        //展示錯誤資訊
mRegisterPresenter.showMessage("未知錯誤");
                        return;
                    }
                    if (userResult.getErrcode()!=1){
                        //展示錯誤資訊
mRegisterPresenter.showMessage(userResult.getErrmsg());
                        return;
                    }

                    //註冊成功
mRegisterPresenter.showMessage("註冊成功!");
                }
            }

            @Override
public void onFailure(Call<UserResult> call, Throwable t) {
                //隱藏進度條
mRegisterPresenter.hideProgress();
                //展示錯誤資訊
mRegisterPresenter.showMessage("註冊失敗");
            }
        });
    }
}
我們發現Model的實現類完成了我們剛才寫在Activity中的網路請求,我們還發現Model的構造方法中傳入了一個Presenter的物件,是的,Presenter作為橋樑,如果我們的Model中不持有其物件我們該怎樣將資料傳輸給View呢
接著,我們來看一下Presenter的程式碼,首先是介面
然後是實現類
我們發現Presenter的構造方法中傳入了一個Activity也就是View的物件,除此之外,在Presenter的構造中還建立了一個Model的物件,並將自己的物件穿給了Model,除了構造方法外,Presenter中還有四個方法,其中三個與View相關一個與Model相關,是的,作為中間人要不偏不倚,雨露均沾,我們在getDataFromModel中呼叫register方法,在其他三個方法中呼叫View相關的另外三個方法
最後只剩下Activity也就是View的程式碼了,同樣先看介面
接著實現類
在這裡我們發現,Activity中只有與檢視相關的程式碼,當我們想要獲取資料的時候只要呼叫一聲中間人
new RegisterPresenterImpl(this).getDataFromModel(new User(userName,passWord));
中間人就會去Model中拿到資料並返回