上一遍博客介绍了RxJava+Retrofit2的使用。在前段时间,刷招聘简历的时候,发现有一部分的公司会要求MVP模式的理解和具体使用。在现在越来越复杂的业务,我们的Activity的负担也是越来越大,因此接着这篇我结合MVP模式来介绍一下自己对RxJava+Retrofit2+MVP的使用。
MVP的理解
RxJava+Retrofit2+MVP三者结合使用
MVP的理解
因为我相信大家在开发App时,肯定会发现,Activity的负担非常重,既要初始化控件,又要写一些逻辑操作的展示等等,有时候很多Activity中的代码都充当了Controller和Model的角色,所以你会发现Activity违背单一职责原则,负担过重。所以,就出现了这么一种架构模式,叫MVP。
而当将架构改为MVP以后,Presenter的出现,将Actvity视为View层,Presenter负责完成View层与Model层的交互。现在是这样的:
- 视图(View)对应于Activity,负责View的绘制以及与用户交互
- 模型(Model)依然是业务逻辑和实体模型
- 主持人(Presenter)相当于协调者,是模型与视图之间的桥梁,负责完成View于Model间的交互,将模型与视图分离开来。
借下图一用,View与Model并不直接交互,而是使用Presenter作为View与Model之间的桥梁。其中Presenter中同时持有 Viwe层以及Model层的Interface的引用,而View层持有Presenter层Interface的引用。当View层某个界面需要展示 某些数据的时候,首先会调用Presenter层的某个接口,然后Presenter层会调用Model层请求数据,当Model层数据加载成功之后会调 用Presenter层的回调方法通知Presenter层数据加载完毕,最后Presenter层再调用View层的接口将加载后的数据展示给用户。这 就是MVP模式的整个核心过程。
这样分层的好处就是大大减少了Model与View层之间的耦合度。一方面可以使得View层和Model层单独开发与测试,互不依赖。另一方面 Model层可以封装复用,可以极大的减少代码量。当然,MVP还有其他的一些优点,这里不再赘述。
但是你可以发现这里超级多累简直就是类数量爆炸,代码复杂度和学习成本高,还有在某些场景下presenter的复用会产生接口冗余。所以MVP模式是不是适合你使用?在使用之前需要思考一下是否有必要对自己的项目动刀?
RxJava+Retrofit2+MVP三者结合使用
首先看看我们用到的库
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.1.0'
compile 'com.google.code.gson:gson:2.7'
compile 'com.jakewharton:butterknife:7.0.1'
compile 'com.squareup.okhttp3:okhttp:3.3.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.3.0'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
}
再看看包目录结构,相信有部分的同学曾纠结应该如何怎么布置自己的项目目录结构,可以参考一下我的方式;
View:用户在登录界面交互触发的事件,我们需要处理到的方法。这里需要我们想象一下在我们的Activity中一共会出现的交互有哪些。
public interface IUserLoginView {
void loginSuccess();
void loginFail(int toast, String reason);
void showLoading();
void hideLoading();
void saveCommonUserInfo(UserInfo info);
}
负责登录的Activity
public class LoginActivity extends MvpActivity<UserLoginPresenter> implements IUserLoginView {
private Context mContext;
@Bind(R.id.editText_name)
EditText mEtGwNumber;
@Bind(R.id.editText_pwd)
EditText mEtPassword;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mContext = this;
}
@Override
protected UserLoginPresenter createPresenter() {
return new UserLoginPresenter(this);
}
@OnClick(R.id.button_login)
public void onClickLogin() {
Map<String, String> maps = new HashMap<>();
maps.put("name", mEtGwNumber.getText().toString());
maps.put("pwd", mEtPassword.getText().toString());
mvpPresenter.login(maps);
}
@Override
public void loginSuccess() {
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
}
@Override
public void loginFail(int toast, String reason) {
if (reason.isEmpty()) {
Toast.makeText(LoginActivity.this, getString(toast), Toast.LENGTH_LONG).show();
} else {
Toast.makeText(LoginActivity.this, reason, Toast.LENGTH_LONG).show();
}
}
@Override
public void showLoading() {
DialogUtils.getInstance().popRemindDialog(mContext, "正在登录");
}
@Override
public void hideLoading() {
DialogUtils.getInstance().disMissRemind();
}
@Override
public void saveCommonUserInfo(UserInfo info) {
//这里将登录接口返回的数据,根据实际情况存储在SP或者SQLite
}
}
这里会继承一个MvpActivity,管理Presenter对Activity的生命周期,防止Presenter持有Activity对象可能导致的内存泄漏问题。
public abstract class MvpActivity<P extends BasePresenter> extends BaseActivity {
protected P mvpPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
mvpPresenter = createPresenter();
super.onCreate(savedInstanceState);
}
protected abstract P createPresenter();
@Override
protected void onDestroy() {
super.onDestroy();
if (mvpPresenter != null) {
mvpPresenter.detachView();
}
}
}
用户点击登录按钮,实现IUserLoginModel接口,传入一个Map集合对象,返回Subscription类型
public interface IUserLoginModel {
Subscription login(Map<String, String> maps, OnLoginListener listener);
}
对IUserLoginModel接口进行结果监听
public interface OnLoginListener {
void loginSuccess(UserInfo userInfo);
void loginFailed(int toast, String reason);
void requestCompleted();
}
通过UserLoginModel去实现IUserLoginModel接口,结合RxJava+Retrofit2实现。这里的具体内容可以参数我上篇博客。
public class UserLoginModel implements IUserLoginModel {
@Override
public Subscription login(Map<String, String> maps, final OnLoginListener listener) {
Observable<JSONObject> observable = RetrofitManage.getInstance().getHttpServiceConnection().login(maps);
return observable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io()) //在io线程中处理网络请求
.observeOn(AndroidSchedulers.mainThread()).subscribe(new SubscriberCallBack<>(new ApiCallback<JSONObject>() {
@Override
public void onSuccess(JSONObject model) {
UserInfo userInfo = JsonHandleAdapter.getUserLoginInfo(model.toString());
listener.loginSuccess(userInfo);
}
@Override
public void onFailure(int msg, String reason) {
listener.loginFailed(msg, reason);
}
@Override
public void onCompleted() {
listener.requestCompleted();
}
}));
}
}
到View和Model的中转站Presenter,这里将Model返回的结果进行判断处理。
首先Presenter的基类BasePresenter
public class BasePresenter<V> implements Presenter<V> {
public V mvpView;
private CompositeSubscription mCompositeSubscription;
@Override
public void attachView(V view) {
this.mvpView = view;
}
@Override
public void detachView() {
this.mvpView = null;
onUnSubscribe();
}
//RxJava取消注册,以避免内存泄露
public void onUnSubscribe() {
if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
mCompositeSubscription.unsubscribe();
}
}
public void addSubscription(Subscription subscription) {
if (mCompositeSubscription == null) {
mCompositeSubscription = new CompositeSubscription();
}
mCompositeSubscription.add(subscription);
}
}
泛型Presenter:
public interface Presenter<V> {
void attachView(V view);
void detachView();
}
最终我们的UserLoginPresenter登录Presenter,我们这里登录Presenter,简化了不少,但是具体思路是这样。
public class UserLoginPresenter extends BasePresenter<IUserLoginView> {
private IUserLoginModel iUserLoginModel;
public UserLoginPresenter(IUserLoginView view) {
attachView(view);
iUserLoginModel = new UserLoginModel();
}
public void login(Map<String, String> maps) {
mvpView.showLoading();
addSubscription(iUserLoginModel.login(maps, new OnLoginListener() {
@Override
public void loginSuccess(UserInfo userInfo) {
mvpView.loginSuccess();
if (null != userInfo)
mvpView.saveCommonUserInfo(userInfo);
}
@Override
public void loginFailed(int toast, String reason) {
mvpView.loginFail(toast, reason);
}
@Override
public void requestCompleted() {
mvpView.hideLoading();
}
}));
}
}
注意上述代码,我们的Presenter完成View和Model的交互。首先我们在View接收到用户的操作,我们通过实现Presenter的登录方法,在Model的登录方法中完成耗时操作网络请求,请求后的结果回调给Presenter,然后Presenter在调用View的对象,执行View对应的方法。
总结:
这里小小的登录接口,就需要到很多实现接口类,所以在我们的实际情况中,到底要不要全部采用MVP模式呢?需要大家具体情况具体分析。不要盲目认为MVP模式好厉害,好整洁就使用,毕竟我们需要保证我们的程序正常运行,代码如期迭代,其他的同学看你的代码不会晕掉。
另外我的封装方式不一定适用你,但是这个方式是在我线上的项目中已经实行了。不过我感觉还是需要继续迭代、继续优化、继续学习。谢谢大家!
源码点击下载: