本文簡述了在實際項目中使用MVP架構遇到的問題和相應處理,最終整理出升級版的MVP架構。java
該系列文章會不斷更新Android項目開發中一些好的架構和小技巧android
系列一 Android架構系列-基於MVP建立適合本身的架構
系列二 Android架構系列-如何優美的寫Intent
系列三 Android架構系列-開發規範
系列四 Android架構系列-封裝本身的okhttp
系列五 Android架構系列-MVP架構的實際應用git
在系列文章的第一篇文章中介紹了使用MVP架構。詳細能夠回看該文章github
MVP的結構以下圖:緩存
MVP是一種代碼的分層思想,其實沒有用到任何庫,只是告訴了你如何規整的放置代碼。使各個層次的代碼各司其職,增長易讀性和可測試性。網絡
可是真實開發中發現,MVP是一種模塊中高內聚的模式,Presenter層接管了Activity中的邏輯實現。相應出現瞭如下幾個問題:架構
Presnter層和View層是一一對應的,因此Presnter層和View層生命週期是一致的。異步
可是如今全部邏輯寫在Presenter層中,若是其餘地方須要調用就只能經過靜態方法調用,不能再次new 一個 Presenter實例ide
實際開發中常常會有在B模塊調用A模塊的部分邏輯。post
好比發帖時要判斷用戶是否登陸,而且獲取當前登陸用戶信息。即在發帖模塊要獲取用戶模塊的數據和邏輯。
若是邏輯寫在Presenter中,則其餘模塊只能直接讀取當前用戶緩存,而後在本身模塊解析。仍是增長了模塊間的耦合。
在這裏將Model層命名爲Interactor。咱們將每一個模塊內部的原子邏輯(一個功能而不是一系列邏輯功能)都寫在interactor中,Presenter層只負責接收view事件,調用interactor功能,再回饋view。
在此,一個Presenter能夠持有多個模塊的Interactor,這樣就能夠訪問相應功能邏輯和數據。而且不須要在本身模塊對其餘模塊數據進行解析處理。
該優化後的分層和普通的MVP最大的區別在於,將Presenter層解放出來,裏面再也不放具體邏輯,直接調用邏輯。
分析各個層:
下面簡述一個Sample 登陸代碼
LoginContract層:
public interface LoginContract {
interface View extends BaseView {
/** * 跳轉Home */
void goHome();
}
interface Presenter extends BasePresenter {
/** * login * @param phone * @param password */
void onLogin(String phone, String password);
}
interface Interactor {
/** * do login * @param phone * @param password * @param callback */
void doLogin(String phone, String password, LoginCallback callback);
interface LoginCallback {
void onSuccess(UserInfo user_info);
void onFailure(String msg);
}
/** * 是否登陸 * @return */
boolean isLogin();
/** * 獲取當前登陸用戶 * @return */
UserInfo getLoginUser();
}
}複製代碼
LoginActivity:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ButterKnife.bind(this);
mPresenter = new LoginPresenter(this);
}
@OnClick(R.id.btnLogin)
public void onLogin() {
mPresenter.onLogin(editPhone.getText().toString(), editPassword.getText().toString());
}
@OnClick(R.id.txtRegister)
public void goRegister() {
ToastUtils.showShort(GlobalApp.getInstance().getContext(), "goRegister");
}
@OnClick(R.id.txtForgetPwd)
public void goForgetPwd() {
ToastUtils.showShort(GlobalApp.getInstance().getContext(), "goForgetPwd");
}
@Override
public void showToast(String msg) {
ToastUtils.showShort(GlobalApp.getInstance().getContext(), msg);
}
@Override
public void goHome() {
//跳轉Home頁面
ToastUtils.showShort(GlobalApp.getInstance().getContext(), "登陸成功, 跳轉Home頁面");
Intent intent = new Intent(this, HomeActivity.class);
startActivity(intent);
finish();
}複製代碼
LoginPresnter:
public class LoginPresenter implements LoginContract.Presenter {
private LoginContract.View mView;
private LoginContract.Interactor mInteractor;
public LoginPresenter(LoginContract.View view) {
mView = view;
mInteractor = new LoginInteractor();
}
@Override
public void start() {
}
@Override
public void onLogin(String phone, String password) {
if(StringUtils.isEmpty(phone)) {
mView.showToast("Empty phone");
return;
}
if(StringUtils.isEmpty(password)) {
mView.showToast("Empty password");
return;
}
mInteractor.doLogin(phone, password, new LoginContract.Interactor.LoginCallback() {
@Override
public void onSuccess(UserInfo user_info) {
mView.goHome();
}
@Override
public void onFailure(String msg) {
mView.showToast(msg);
}
});
}
}複製代碼
LoginInteractor:
public class LoginInteractor implements LoginContract.Interactor {
private MyOkHttp mApi;
private ACache mCache;
//緩存key
private final String CACHE_KEY_USERINFO = "CACHE_KEY_USERINFO";
public LoginInteractor() {
mApi = MyOkHttp.get();
mCache = ACache.get(GlobalApp.getInstance().getContext());
}
@Override
public void doLogin(String phone, String password, final LoginCallback callback) {
//模擬異步網絡請求登陸
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
UserInfo userInfo = new UserInfo();
userInfo.uid = "1212121";
userInfo.userName = "tsy12321";
userInfo.token = "wqw13w12312wsqw12";
//存入緩存
mCache.put(CACHE_KEY_USERINFO, userInfo);
callback.onSuccess(userInfo);
}
}, 2000);
}
@Override
public boolean isLogin() {
UserInfo userInfo = (UserInfo) mCache.getAsObject(CACHE_KEY_USERINFO);
if(!StringUtils.isEmpty(userInfo.uid) && !StringUtils.isEmpty(userInfo.token)) {
return true;
}
return false;
}
@Override
public UserInfo getLoginUser() {
return (UserInfo) mCache.getAsObject(CACHE_KEY_USERINFO);
}
}複製代碼
由以上示例能夠看出,具體的邏輯都放在了Interactor層。下面展現其餘模塊如何調用Login模塊的邏輯或者數據。
假如Home頁面點擊發帖,須要判斷當前登陸狀態。則在HomePresenter中同時持有LoginInteractor的實例。
public class HomePresenter implements HomeContract.Presenter {
private HomeContract.View mView;
private HomeContract.Interactor mInteractor;
private LoginContract.Interactor mLoginInteractor;
public HomePresenter(HomeContract.View view) {
mView = view;
mInteractor = new HomeInteractor();
mLoginInteractor = new LoginInteractor();
}
@Override
public void start() {
}
@Override
public void onPost() {
//判斷用戶有沒有登陸
if(!mLoginInteractor.isLogin()) {
// 跳轉登陸
// TODO: 16/8/30
return;
}
//跳轉發帖頁面
// TODO: 16/8/30
}
}複製代碼
具體的代碼在Github項目:BaseAndroidProject
不管是MVP仍是什麼架構,最終的目的都是寫出易讀性和測試性強的代碼。因此不要對於架構鑽牛角尖,過分設計不可取。在實際開發中架構天然會跟着升級。謹記!均衡合理!!
更多文章關注個人公衆號