Android架構:MVP模式實例

現在的安卓開發已經很成熟,不像幾年前大部分人還處於技術摸索階段,當技術問題不再是安卓開發的難題時,更多人開始關注架構設計,代碼質量,更想易於測試,維護方便,邏輯清晰。大家試圖讓所有的代碼都高度解耦,各層分離,從而達到目的。MVP的架構模式,就這樣應運而生。這裏我希望以最簡單的例子,讓大家快速入門MVP模式。

認識MVP

MVP就是Model,View,Presenter的縮寫。
對比MVC,MVP模式的特點是V層和M層不可互相接觸,需要通過主導器Presenter進行關聯,P層進行所有的邏輯處理,V層僅僅負責視圖展示,M層負責獲取數據。
下面是MVC和MVP對比圖:
這裏寫圖片描述
圖片出處

實例

依然是最簡單的登錄頁面,我通過一個登錄業務,來詳述MVP在項目中的使用,文後會附上代碼地址。
這裏寫圖片描述

既然要解耦,必然是面向接口的編程,就按照寫代碼的順序從Model開始看:
LoginModel.java


/**
 * Created by yuankundong on 2016/03/03.
 */
public interface LoginModel {

    void toLogin(String username, String password, OnLoginListener onLoginListener);
}

對於Model層,沒有什麼多餘的方法,就是完成登錄,把結果返回給Presenter。這裏我又寫了一個接口OnLoginListener,爲了給Presenter回調,讓View和Model分離,當然,如果我們的框架中使用了otto之類的東東,那就不需要這個接口。接着看上面接口的實現:
LoginModelImpl.java

/**
 * Created by yuankundong on 2016/03/03.
 */
public class LoginModelImpl implements LoginModel{

    String url = "http://xxxxx";
    public final static int TIME_OUT_CODE = 99;
    public final static String TIME_OUT_MSG = "網絡連接不可用,請稍後重試";
    @Override
    public void toLogin(final String username, final String password, final OnLoginListener onLoginListener) {

        StringRequest stringRequest = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {

            @Override
            public void onResponse(String response) {

                Type type = new TypeToken<UserBean>() {
                }.getType();
                Gson gson = new Gson();
                UserBean userBean = gson.fromJson(response, type);
                if (userBean.isSuccess()) {
                    onLoginListener.OnSuccess(userBean);
                } else {
                    onLoginListener.OnError(userBean.getCode(), userBean.getMsg());
                }

            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                onLoginListener.OnError(TIME_OUT_CODE, TIME_OUT_MSG);
            }
        }) {
            protected Map<String, String> getParams() throws AuthFailureError {
                Map<String, String> map = new HashMap<>();
                map.put("phone", username);
                map.put("password", MD5Utils.stringToMD5(password));
                return map;
            }
        };
        BaseApplication.requestQueue.add(stringRequest);
    }
}

這裏使用Volley網絡框架,假設登錄成功後,返回的json數據是這個類型:

{
    "code": 0,
    "msg": "登錄成功"
}

那麼我的實體Bean可以這麼設計:

/**
 * Created by yuankundong on 2016/03/03.
 */
public class UserBean {
    private int code;    // 返回碼,0爲成功
    private String msg;      // 返回信息


    public UserBean(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public boolean isSuccess() {
        return code == 0;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

上述代碼就完成了Model層,因爲Presenter是M和V的聯絡人,所以,我們再把View層寫了,同樣,需要先寫接口,這個接口裏面我們寫什麼呢,就是在頁面上,會發生的所有事,比如,獲取用戶名密碼,點擊登錄後有進度條提示,登錄反饋後取消提示等。

/**
 * Created by yuankundong on 2016/03/03.
 */
public interface LoginView {

    //取得用戶名
    String getUsername();

    //取得密碼
    String getPassword();

    //登錄成功後
    void loginSuccess();

    //登錄失敗後
    void loginError(String msg);

    //展示進度條
    void showProgress();

    //隱藏進度條
    void hideProgress();

    //當需要彈出提示信息時
    void showMessage(String msg);
}

我們能想到的都寫進來了,接着我們寫這個接口的實現類,就是個Activity。

/**
 * Created by yuankundong on 2016/03/03.
 */
public class LoginActivity extends AppCompatActivity implements LoginView{
    TextView username;
    TextView password;
    Button login;
    ProgressDialog dialog;
    LoginPresenter presenter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        presenter = new LoginPresenterImpl(this);
        username = (TextView) findViewById(R.id.tv_username);
        password = (TextView) findViewById(R.id.tv_password);
        login = (Button) findViewById(R.id.btn_login);
        login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.actionLogin();
            }
        });

    }

    @Override
    public String getUsername() {
        return username.getText().toString();
    }

    @Override
    public String getPassword() {
        return password.getText().toString();
    }

    @Override
    public void loginSuccess() {
        Toast.makeText(this, "登錄成功", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void loginError(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showProgress() {
        dialog = ProgressDialog.show(this, " ", "加載中", true, true);
    }

    @Override
    public void hideProgress() {
        dialog.dismiss();
    }

    @Override
    public void showMessage(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
}

看上面的代碼,很簡潔,沒有任何邏輯代碼,這就是我們想要的效果,接下來就開始寫Presenter層了,邏輯代碼也是寫到這一層。

/**
 * Created by yuankundong on 2016/03/03.
 */
public interface LoginPresenter {

    void actionLogin();
}

實現:

/**
 * Created by yuankundong on 2016/03/03.
 */
public class LoginPresenterImpl implements LoginPresenter, OnLoginListener {
    LoginView loginView;
    LoginModel loginModel;
    public LoginPresenterImpl(LoginView loginView) {
        this.loginView = loginView;
        this.loginModel = new LoginModelImpl();

    }
    @Override
    public void actionLogin() {
        String username = loginView.getUsername();
        String password = loginView.getPassword();
        if (TextUtils.isEmpty(username)) {
            loginView.showMessage("用戶名爲空");
            return;
        }
        if (TextUtils.isEmpty(password)) {
            loginView.showMessage("密碼爲空");
            return;
        }
        loginView.showProgress();
        loginModel.toLogin(username,password,this);
    }

    @Override
    public void OnSuccess(UserBean userBean) {
        loginView.hideProgress();
        loginView.loginSuccess();
    }

    @Override
    public void OnError(int code, String msg) {
        loginView.hideProgress();
        loginView.loginError(msg);
    }
}

在LoginPresenterImpl 類中,我們實例了LoginView 和LoginModel 。登錄的邏輯就是先判斷用戶名和密碼是否爲空,不爲空再調用Model裏的登錄方法,進行登錄。因爲Model層把登錄反饋傳給OnLoginListener 這個回調接口,所有我們在這裏實現這個接口,就能獲取登錄後的反饋。OnSuccess和OnError分別是登錄成功和失敗後,通知View層應該顯示什麼。

這樣,我們用MVP寫出來的登錄就完成了,總體思想就是,Model層獲取的數據交給Presenter處理,Presenter進行處理的過程中,會通知頁面View層進行相應變化,View層本身不進行邏輯處理,MVP架構及時各有分工。
整體工程裏面還有個別不涉及本文內容的我沒有詳述,源碼地址,點擊這裏下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章