Android 中 MVC 的簡單理解https://blog.csdn.net/qq_17798399/article/details/95933628
MVP模式
相信大家對MVC都是比較熟悉了:M-Model-模型、V-View-視圖、C-Controller-控制器,MVP作爲MVC的演化版本,那麼類似的MVP所對應的意義:M-Model-模型、V-View-視圖、P-Presenter-表示器。 從MVC和MVP兩者結合來看,Controlller/Presenter在MVC/MVP中都起着邏輯控制處理的角色,起着控制各業務流程的作用。而 MVP與MVC最不同的一點是M與V是不直接關聯的也是就Model與View不存在直接關係,這兩者之間間隔着的是Presenter層,其負責調控 View與Model之間的間接交互,MVP的結構圖如下所示,對於這個圖理解即可而不必限於其中的條條框框,畢竟在不同的場景下多少會有些出入的。在 Android中很重要的一點就是對UI的操作基本上需要異步進行也就是在MainThread中才能操作UI,所以對View與Model的切斷分離是 合理的。此外Presenter與View、Model的交互使用接口定義交互操作可以進一步達到鬆耦合也可以通過接口更加方便地進行單元測試。
Model:業務邏輯和實體模型,用來操作實際的數據,包含Bean和Model的抽象接口來降低耦合。
View:就是Android中的視圖,需要建立一個View的抽象接口View Interface。通過實現View的接口來實現View與Presenter的交互,從而降低耦合。對應於Activity,負責View的繪製與用戶交互;
Presenter:View和Model的中間樞紐,處理和用戶交互的邏輯。
爲什麼要用MVP?
其實我們日常開發中的Activity,Fragment和XML界面就相當於是一個 MVC 的架構模式,Activity中不僅要處理各種 UI 操作還要請求數據以及解析。
這種開發方式的缺點就是業務量大的時候一個Activity 文件分分鐘飆到上千行代碼,想要改一處業務邏輯光是去找就要費半天勁,而且有點地方邏輯處理是一樣的無奈是不同的 Activity 就沒辦法很好的寫成通用方法。
那 MVP 爲啥好用呢?
MVP 模式將Activity 中的業務邏輯全部分離出來,讓Activity 只做 UI 邏輯的處理,所有跟Android API無關的業務邏輯由 Presenter 層來完成。
將業務處理分離出來後最明顯的好處就是管理方便,但是缺點就是增加了代碼量。
MVP框架的搭建
MVP快速生成類的插件: https://github.com/githubwing/MVPHelper
下面演示下登陸的MVP實現方式:
(示例代碼由開發項目中剝離到Demo中,登陸接口使用的是玩安卓的登陸API:http://www.wanandroid.com/blog/show/2)
首先,創建一個登陸的Contract:
public interface MainContract {
interface Model { }
interface View extends BaseView { }
interface Presenter { }
}
其次創建Presenter、Model、View 對應Contract中的接口;
public class MainPresenter implements MainContract.Presenter{}
public class MainModel implements MainContract.Model{}
public class MainActivity implements MainContract.View {}
完整的Contract:
public interface MainContract {
interface Model {
Flowable<BaseObjectBean<LoginBean>> login(String username, String password);
}
interface View extends BaseView {
@Override
void showLoading();
@Override
void hideLoading();
@Override
void onError(Throwable throwable);
void onSuccess(BaseObjectBean<LoginBean> bean);
}
interface Presenter {
/**
* 登陸
*
* @param username
* @param password
*/
void login(String username, String password);
}
}
在MainContract 中
Model接口 創建對應的聯網請求的方法,將Presenter提交的字段放到聯網請求中,發送給服務器
View 接口 創建在界面上顯示加載中、取消加載以及登陸成功、失敗的方法
Presenter 接口 創建 登陸的方法,以及需要提交的字段 (username、password)
MainModel的完整代碼:
public class MainModel implements MainContract.Model {
@Override
public Flowable<BaseObjectBean<LoginBean>> login(String username, String password) {
return RetrofitClient.getInstance().getApi().login(username,password);
}
}
Model類實現MainContract.Model 接口中的 login(String username, String password)方法,將username、password放在聯網請求中,進行請求服務器。
MainView 的完整代碼:
public class MainActivity extends BaseMvpActivity<MainPresenter> implements MainContract.View {
@BindView(R.id.et_username_login)
TextInputEditText etUsernameLogin;
@BindView(R.id.et_password_login)
TextInputEditText etPasswordLogin;
@Override
public int getLayoutId() {
return R.layout.activity_main;
}
@Override
public void initView() {
mPresenter = new MainPresenter();
mPresenter.attachView(this);
}
/**
* @return 帳號
*/
private String getUsername() {
return etUsernameLogin.getText().toString().trim();
}
/**
* @return 密碼
*/
private String getPassword() {
return etPasswordLogin.getText().toString().trim();
}
@Override
public void onSuccess(BaseObjectBean bean) {
Toast.makeText(this, bean.getErrorMsg(), Toast.LENGTH_SHORT).show();
}
@Override
public void showLoading() {
ProgressDialog.getInstance().show(this);
}
@Override
public void hideLoading() {
ProgressDialog.getInstance().dismiss();
}
@Override
public void onError(Throwable throwable) {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// TODO: add setContentView(...) invocation
ButterKnife.bind(this);
}
@OnClick(R.id.btn_signin_login)
public void onViewClicked() {
if (getUsername().isEmpty() || getPassword().isEmpty()) {
Toast.makeText(this, "帳號密碼不能爲空", Toast.LENGTH_SHORT).show();
return;
}
mPresenter.login(getUsername(), getPassword());
}
}
MainActivity 中實現 MainContract.View中的方法 ,在實現的方法中,進行進度條加載、和登陸成功or失敗的UI的展示:
@Override
void showLoading();
@Override
void hideLoading();
@Override
void onError(Throwable throwable);
void onSuccess(BaseObjectBean<LoginBean> bean);
MainPresenter 的完整代碼:
public class MainPresenter extends BasePresenter<MainContract.View> implements MainContract.Presenter {
private MainContract.Model model;
public MainPresenter() {
model = new MainModel();
}
@Override
public void login(String username, String password) {
if (!isViewAttached()) {
return;
}
mView.showLoading();
model.login(username, password)
.compose(RxScheduler.<BaseObjectBean<LoginBean>>Flo_io_main())
.as(mView.<BaseObjectBean<LoginBean>>bindAutoDispose())
.subscribe(new Consumer<BaseObjectBean<LoginBean>>() {
@Override
public void accept(BaseObjectBean<LoginBean> bean) throws Exception {
mView.onSuccess(bean);
mView.hideLoading();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
mView.onError(throwable);
mView.hideLoading();
}
});
}
}
MainPresenter 實現MainContract.Presenter 接口中的 login(String username, String password) 方法
實例化Model,在MainPresenter login(String username, String password)方法中,調用model的網絡請求,將username、password放在model的login()方法中,進行請求服務器。
請求服務器前 使用MainContract.View中的 mView.showLoading()方法,進行顯示加載中;在成功失敗的回調中,使用對應的方法,以及取消加載。
其中BasePresenter、BaseView 是對Presenter以及View進行的封裝
BaseView類:
public interface BaseView {
/**
* 顯示加載中
*/
void showLoading();
/**
* 隱藏加載
*/
void hideLoading();
/**
* 數據獲取失敗
* @param throwable
*/
void onError(Throwable throwable);
/**
* 綁定Android生命週期 防止RxJava內存泄漏
*
* @param <T>
* @return
*/
<T> AutoDisposeConverter<T> bindAutoDispose();
}
至於爲什麼不把onSuccess()方法也封裝,是因爲請求網絡,服務器返回的值是不一樣的,在Contract > View接口中根據bean類設置onSuccess()
BasePresenter類:
public class BasePresenter<V extends BaseView> {
protected V mView;
/**
* 綁定view,一般在初始化中調用該方法
*
* @param view view
*/
public void attachView(V view) {
this.mView = view;
}
/**
* 解除綁定view,一般在onDestroy中調用
*/
public void detachView() {
this.mView = null;
}
/**
* View是否綁定
*
* @return
*/
public boolean isViewAttached() {
return mView != null;
}
}