本Demo使用 Okhttp3、Retrofit2诅岩、Rxjava2 ,使用AutoDispose解決RxJava內存泄漏
Github:
https://github.com/RookieExaminer/MvpDemo
什么是MVP芳撒,為什么要用MVP萨西?
網上介紹MVP的很多汹来,百度一下你就知道欧穴,至于為什么要用MVP洼畅,當然是由于它的優(yōu)勢了:
1.代碼簡潔
此處的簡潔是邏輯的簡潔,而不是代碼本身 舉個栗子
比如購物車界面霍殴,有很多請求網絡的地方:獲取購物車商品列表窍蓝、購物車商品的刪除、購物車商品的購買等等繁成, 這么多網絡請求吓笙,如果都寫在一個Activity,而且還有大量邏輯判斷巾腕,那這個Activity的行數~ 看著就讓人頭痛面睛, 即便寫了注釋,維護起來也是比較麻煩的
2.降低耦合尊搬,方便維護
MVP的使用叁鉴,使Activity中的網絡請求剝離出來 成為model、presenter佛寿,model只負責網絡的請求幌墓、pesenter負責處理請求網絡后的數據處理:加載中 成功 or 失敗 取消加載;最后View進行界面的展示
Start 看圖:
嗯哼冀泻? 不是 Model常侣、Presenter、View這三個 么弹渔,怎么又多出來個Contract胳施,這又是什么鬼?
這就涉及到MVP的缺點了肢专,正所謂舞肆,金無足赤,人無完人博杖,MVP既然有優(yōu)點當然也有它的缺點了
MVP在實現代碼簡潔的同時椿胯,額外增加了大量的接口、類剃根,不方便進行管理哩盲,于是Contract就登場了。
Contract 百度翻譯 : 合同;契約;協議
Contract 如其名跟继,是一個契約种冬,將Model镣丑、View舔糖、Presenter 進行約束管理,方便后期類的查找莺匠、維護金吗。
下面演示下登陸的MVP實現方式:
(示例代碼由開發(fā)項目中剝離到Demo中,登陸接口使用的是玩安卓的登陸API:http://www.wanandroid.com/blog/show/2)
首先,創(chuàng)建一個登陸的Contract:
public interface MainContract {
interface Model { }
interface View extends BaseView { }
interface Presenter { }
}
其次創(chuàng)建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接口 創(chuàng)建對應的聯網請求的方法卫袒,將Presenter提交的字段放到聯網請求中宵呛,發(fā)送給服務器
View 接口 創(chuàng)建在界面上顯示加載中、取消加載以及登陸成功夕凝、失敗的方法
Presenter 接口 創(chuàng)建 登陸的方法宝穗,以及需要提交的字段 (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;
}
}
時間有限蔑穴,暫時就先這樣忠寻,具體可下載Demo查看 ↓
本Demo: https://github.com/RookieExaminer/MvpDemo
MVP快速生成類的插件: https://github.com/githubwing/MVPHelper
參考:
Android MVP架構搭建:
http://www.jcodecraeer.com/a/anzhuokaifa/2017/1020/8625.html?1508484926
Android架構中添加AutoDispose解決RxJava內存泄漏:
http://www.reibang.com/p/8490d9383ba5