简述
直接起飞吧
本文章主要详细展示了安卓mvp开发模式中如何优雅的结合Rxjava+Retrofit,完成我们的接口调用。我自称其模式为“MCP”模式,该模式介绍如下
①Model层:处理网络请求,设定网络框架中的回调线程等
②Contract层:契约层,主要定义了各种类型下的View和Presenter,这也是MCP中尤为重要的存在,是用户界面和网络接口需求的定义。
③Presenter层:用户直接可以直接操作到的一层。将Model层的结果回调给View层,取消Model和View的绑定、主动取消网络请求等。
如此一来,model层和presente紧密结合,presenter的使用很灵活,而model层和view层没有任何关系,同时presenter与view'层可完全解耦。
为什么叫mcp
图示中要么就是mvp要么就是mc,怎么会mcp呢?这并不是乱来的,model层相当于rxjava+Retrofit了,定义不变;c是契约类,定义了需要实现的view和presenter,定义也不变;p实际上是BasePresenter<T,M,V>.class一个类,这个类在使用过程中不需要重复定义,但它需要统一处理model返回的数据,绑定和解绑view,更是重中之重,所以不能忽略。
具体实现
需求定义(登录模块功能,用户模块)
日常开发中,业务逻辑都不会太简单,因此需要将很多功能模块化,如
将登录、注册、忘记密码归为“登录模块”,因此,只需要一个契约类“IContractLogin”即可;
用户信息方面,获取用户信息、修改用户信息也可以归为“用户模块”,契约类为“IContractUser”;
基础代码
得益于很多大神们的优秀博客,我自称的mcp模式下也是有对应的基础代码的。
一、项目配置和项目目录
1.app的build.gradle文件的部分配置如下
android{
......//applicationId、版本等
//启用viewbinding
buildFeatures {
viewBinding true
}
//签名
signingConfigs {
def alias = 'key0'
def password = '123456'
def filePath = '../mcp.jks'
debug {
keyAlias alias
keyPassword password
storeFile file(filePath)
storePassword(password)
}
release {
keyAlias alias
keyPassword password
storeFile file(filePath)
storePassword(password)
}
}
//打包的apk名称
applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "mcp.apk"
}
}
}
引入所需要的的包,这里我已经习惯了引入这些东西,懒得删掉了,反正以后要用。
//网络
//noinspection GradleDependency
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.5.0'
//noinspection GradleDependency
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
//noinspection GradleDependency
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
//noinspection GradleDependency
implementation 'com.squareup.okhttp3:okhttp:3.12.8'
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.8'
//noinspection GradleDependency
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
//图片选择器
implementation group: 'com.github.LuckSiege.PictureSelector', name: 'picture_library', version: 'v2.6.0'
//gilde图片加载器
//noinspection GradleDependency
implementation 'com.github.bumptech.glide:glide:4.5.0'
//noinspection GradleDependency
annotationProcessor 'com.github.bumptech.glide:compiler:4.5.0'
记得加上仓库地址
image.png
maven { url "https://jitpack.io" }
maven { url 'http://central.maven.org/maven2/' }
2.修改gradle.properties文件
gradle.properties修改前修改为
gradle.properties修改后
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
3.项目目录结构
详细的项目接口示意图看起来好像好乱,收起再看就舒服多了
目录结构
二、代码实现
基类和接口
这里只剩下p层和v层的基类了,因为m层已经完全融入到rxjava中了。
IBasePresenter接口,在契约类中,所有的presenter都要继承于它。
public interface IBasePresenter<T> {
/**
* 设定类型
*
* @param i 默认为0
*/
void setType ( int i );
/**
* 添加请求列表
*
* @param disposable 实体
*/
void add ( Disposable disposable );
/**
* 解绑
*/
void detachView ( );
/**
* 视图回调
*
* @param data 数据
*/
void viewCallBack ( CallResult< T > data );
/**
* 视图回调
*
* @param e 异常
*/
void viewCallBack ( Throwable e );
}
同时,presenter需要处理View和model,所以他有一个实现类。你别看代码多,实际开发中,这类完全不需要动它,而且因为有了它,我们在新建presenter的时候,少很多功夫,而且新建的presenter只要继承了它,代码都能规范很多。这里需要提一下的方法是“add()”方法,它是将某次请求添加到请求队列中,当detachView()被调用,未完成的网络请求会被取消。
public abstract class BasePresenter< T, M, V extends IBaseView< T > >
implements IBasePresenter< T > {
public static final int UNAUTHORIZED = 401;
public static final int SUCCESS = 200;
public static final int FORBIDDEN = 403;
public static final int NOT_FOUND = 404;
public static final int REQUEST_TIMEOUT = 408;
public static final int INTERNAL_SERVER_ERROR = 500;
public static final int BAD_GATEWAY = 502;
public static final int SERVICE_UNAVAILABLE = 503;
public static final int GATEWAY_TIMEOUT = 504;
/**
* 默认页页面大小
*/
protected static final int PAGE_SIZE = 10;
/**
* 视图层
*/
private V view;
/**
* model层
*/
protected M module;
/**
* 视图引用
*/
private WeakReference< V > weakReference;
/**
* 主线程
*/
protected final Handler handler;
/**
* 请求列表
*/
protected final List< Disposable > disposables = new ArrayList<> ( );
/**
* 有时候,删除和修改共用一个presenter,需要用一个标记将它们却别
*/
private int type = 0;
/**
* 构造方法
*
* @param view 视图实现类
*/
public BasePresenter ( V view ) {
this.attachView ( view );
this.handler = new Handler ( Looper.getMainLooper ( ) );
}
/**
* 绑定
*
* @param view 视图
*/
public void attachView ( V view ) {
if ( view != null ) {
this.weakReference = new WeakReference<> ( view );
this.view = ( V ) Proxy.newProxyInstance ( view.getClass ( ).getClassLoader ( ),
view.getClass ( ).getInterfaces ( ),
new MvpViewHandler ( this.weakReference.get ( ) ) );
}
if ( this.module == null ) {
this.module = this.createModule ( );
}
}
/**
* 登录的token
*
* @return Strung
*/
protected String token ( ) {
return LoginUtils.getToken ( );
}
@Override
public void detachView ( ) {
this.module = null;
this.handler.removeCallbacksAndMessages ( null );
if ( this.isViewAttached ( ) && this.weakReference != null ) {
this.weakReference.clear ( );
this.weakReference = null;
}
this.view = null;
try {
for ( Disposable disposable : disposables ) {
if ( ! disposable.isDisposed ( ) ) {
disposable.dispose ( );
}
}
} catch ( Exception e ) {
Logger.e ( e );
}
}
protected boolean isViewAttached ( ) {
return this.view != null && this.weakReference != null && this.weakReference.get ( ) != null;
}
/**
* 创建module
*
* @return M
*/
protected abstract M createModule ( );
/**
* 请求是否成功
*
* @param callResult 响应体
* @return 成功true,失败false
*/
protected boolean isSuccess ( CallResult< T > callResult ) {
return callResult != null && callResult.code == SUCCESS;
}
/**
* 设定类型
*
* @param type 默认0
*/
@Override
public void setType ( int type ) {
this.type = type;
}
/**
* 添加到请求列表
*
* @param disposable 实体
*/
@Override
public void add ( Disposable disposable ) {
disposables.add ( disposable );
}
/**
* 视图回调---成功
* 如果回调的线程不是主线程,则在主线程回调
*
* @param data 数据
*/
@Override
public void viewCallBack ( CallResult< T > data ) {
if ( Looper.myLooper ( ) == Looper.getMainLooper ( ) ) {
viewCallBackSuccess ( data );
} else {
handler.post ( ( ) -> viewCallBackSuccess ( data ) );
}
}
/**
* 视图回调---失败
* 如果回调的线程不是主线程,则在主线程回调
*
* @param e 异常
*/
@Override
public void viewCallBack ( Throwable e ) {
if ( Looper.myLooper ( ) == Looper.getMainLooper ( ) ) {
viewCallBackError ( e );
} else {
handler.post ( ( ) -> viewCallBackError ( e ) );
}
}
/**
* 接口调用成功的后回调
*
* @param data data
*/
private void viewCallBackSuccess ( CallResult< T > data ) {
if ( isViewAttached ( ) ) {
view.onCallBack ( type, data.code == SUCCESS, data.message, data.data );
}
}
/**
* 接口调用失败的回调
*
* @param e 异常
*/
private void viewCallBackError ( Throwable e ) {
if ( isViewAttached ( ) ) {
try {
if ( e instanceof HttpException ) {
HttpException httpException = ( HttpException ) e;
switch ( httpException.code ( ) ) {
case UNAUTHORIZED:
view.onCallBack ( type, false, "登录验证已过期", null );
break;
case INTERNAL_SERVER_ERROR:
view.onCallBack ( type, false, "服务器错误", null );
break;
case FORBIDDEN:
case NOT_FOUND:
view.onCallBack ( type, false, "无效的请求", null );
break;
case REQUEST_TIMEOUT:
case GATEWAY_TIMEOUT:
case BAD_GATEWAY:
case SERVICE_UNAVAILABLE:
default:
view.onCallBack ( type, false, httpException.getMessage ( ), null );
break;
}
} else if ( e instanceof ConnectException ) {
view.onCallBack ( type, false, "网络连接异常,请检查您的网络状态", null );
} else if ( e instanceof SocketTimeoutException ) {
view.onCallBack ( type, false, "网络连接超时,请检查您的网络状态,稍后重试", null );
} else if ( e instanceof UnknownHostException ) {
view.onCallBack ( type, false, "网络异常,请检查您的网络状态", null );
} else if ( e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException ) {
view.onCallBack ( type, false, "数据解析错误", null );
} else if ( e instanceof SSLHandshakeException ) {
view.onCallBack ( type, false, "证书验证失败", null );
} else if ( e instanceof RuntimeException ) {
view.onCallBack ( type, false, "运行时异常", null );
} else {
view.onCallBack ( type, false, e.toString ( ), null );
}
} catch ( Exception e1 ) {
Logger.e ( e1 );
}
}
}
protected Map< String, Object > createMap ( int initSize ) {
return new HashMap<> ( initSize );
}
protected Map< String, Integer > createMapInt ( int initSize ) {
return new HashMap<> ( initSize );
}
protected Map< String, String > createMapStr ( int initSize ) {
return new HashMap<> ( initSize );
}
private class MvpViewHandler implements InvocationHandler {
private final IBaseView< ? > mvpView;
MvpViewHandler ( IBaseView< ? > mvpView ) {
this.mvpView = mvpView;
}
@Override
@SuppressWarnings ( "SuspiciousInvocationHandlerImplementation" )
public Object invoke ( Object proxy, Method method, Object[] args ) throws Throwable {
if ( isViewAttached ( ) ) {
return method.invoke ( this.mvpView, args );
}
return null;
}
}
}
IBaseView接口,在契约类中,所有的view都要继承于它。就是通过继承它,才把接口回调的形式全部都统一起来的。
public interface IBaseView<T> {
/**
* ui回调
*
* @param i 类型
* @param b 是否请求成功
* @param s 描述
* @param data 泛型
*/
void onCallBack ( int i, boolean b, String s, T data );
}
M层代码
model层,其实就是100%的rxjava层。这里的代码都是要手动编写的,与我们后端的接口基本上对应起来
public interface IBaseModel {
/**
* 登录
*
* @param data 登录参数
*/
@POST ( "/mcpdemo/api/user/login" )
Observable< CallResult< LoginResponse > > login ( @Body Map< String, String > data );
/**
* 注册
*
* @param data 注册参数
*/
@POST ( "/mcpdemo/api/user/register" )
Observable< CallResult< Void > > register ( @Body Map< String, Object > data );
/***
* 忘记密码
* @param data 忘记密码参数
*/
@POST ( "/mcpdemo/api/user/forget" )
Observable< CallResult< Void > > forgetPwd ( @Body Map< String, String > data );
/**
* 获取用户信息
*
* @param token 登录后的token
*/
@GET ( "/mcpdemo/api/user/detail" )
Observable< CallResult< UserInfo > > userInfo ( @Header ( "token" ) String token );
/**
* 更新用户信息,修改个人资料
*
* @param token token
* @param info info
*/
@POST ( "/mcpdemo/api/user/update" )
Observable< CallResult< Void > > userInfoUpdate ( @Header ( "token" ) String token,
@Body UserInfo info );
}
再看看他的实现类。在这里你可以统一定义观察者和被观察者的线程,无所谓的,反正到了p层会在ui线程回调。别看他代码多,编译器会帮你生成方法的,你只需要填写方法内容即可。
public class BaseModel implements IBaseModel {
private static IBaseModel api;
private static BaseModel instance;
public static BaseModel instance ( ) {
if ( instance == null ) {
instance = new BaseModel ( );
}
return instance;
}
private BaseModel ( ) {
api = Retrofit2Manager.with ( UrlUtils.URL ).retrofit ( ).create ( IBaseModel.class );
}
@Override
public Observable< CallResult< LoginResponse > > login ( Map< String, String > data ) {
return api.login ( data ).subscribeOn ( Schedulers.io ( ) )
.observeOn ( AndroidSchedulers.mainThread ( ) );
}
@Override
public Observable< CallResult< Void > > register ( Map< String, Object > data ) {
return api.register ( data ).subscribeOn ( Schedulers.io ( ) )
.observeOn ( AndroidSchedulers.mainThread ( ) );
}
@Override
public Observable< CallResult< Void > > forgetPwd ( Map< String, String > data ) {
return api.forgetPwd ( data ).subscribeOn ( Schedulers.io ( ) )
.observeOn ( AndroidSchedulers.mainThread ( ) );
}
@Override
public Observable< CallResult< UserInfo > > userInfo ( String token ) {
return api.userInfo ( token ).subscribeOn ( Schedulers.io ( ) )
.observeOn ( AndroidSchedulers.mainThread ( ) );
}
@Override
public Observable< CallResult< Void > > userInfoUpdate ( String token, UserInfo info ) {
return api.userInfoUpdate ( token, info ).subscribeOn ( Schedulers.io ( ) )
.observeOn ( AndroidSchedulers.mainThread ( ) );
}
}
C层代码(定义Presenter和View)
登录模块契约类。为什么注册和忘记密码不用分开声明?因为它们的回调结果是一样的(Void),我们只需要通过p层的type将它们区分开即可。你可以理解为,只要回调结果是一样的,都能合并在同一个presenter中,这样的话,presenter的实现类的数量会少很多。
public interface IContractLogin {
interface IViewLogin extends IBaseView< LoginResponse > {
}
interface IPresenterLogin extends IBasePresenter< LoginResponse > {
void login ( String phone, String passWord );
}
interface IViewLoginAction extends IBaseView< Void > {
}
interface IPresenterLoginAction extends IBasePresenter< Void > {
/**
* 注册-----(i为1)
*
* @param phone 手机号
* @param passWord 密码
* @param sex 性别1男2女3保密
*/
void register ( String phone, String passWord, int sex );
/**
* 忘记密码-----(i为2)
*
* @param phone 手机号
* @param newPassword 新密码
*/
void forgetPassword ( String phone, String newPassword );
}
}
用户模块契约类。token不是activity层(用户)关注的东西,因此获取个人信息这里,不需要传参token,在定义好的基类BasePresenter取就好了。
public interface IContractUser {
/**
* 获取个人信息
*/
interface IViewUserInfo extends IBaseView< UserInfo > {
}
interface IPresenterUserInfo extends IBasePresenter< UserInfo > {
void getInfo ( );
}
/**
* 更新用户信息
*/
interface IViewUserAction extends IBaseView< Void > {
}
interface IPresenterUserAction extends IBasePresenter< Void > {
void update ( UserInfo info );
}
}
P层代码
登录
public class PresenterLogin extends BasePresenter< LoginResponse, IBaseModel,
IContractLogin.IViewLogin >
implements IContractLogin.IPresenterLogin {
public PresenterLogin ( IContractLogin.IViewLogin view ) {
super ( view );
}
@Override
protected IBaseModel createModule ( ) {
return BaseModel.instance ( );
}
@Override
public void login ( String phone, String passWord ) {
Map< String, String > map = createMapStr ( 2 );
map.put ( "phone", phone );
map.put ( "passWord", passWord );
add ( module.login ( map ).subscribe ( result -> {
if ( isSuccess ( result ) ) {
//如果登录成功了,保存登录信息
LoginUtils.loginSuccess ( result.data );
}
//UI回调
viewCallBack ( result );
}, this :: viewCallBack ) );
}
}
注册和忘记密码
public class PresenterLoginAction extends BasePresenter< Void, IBaseModel,
IContractLogin.IViewLoginAction >
implements IContractLogin.IPresenterLoginAction {
public PresenterLoginAction ( IContractLogin.IViewLoginAction view ) {
super ( view );
}
@Override
protected IBaseModel createModule ( ) {
return BaseModel.instance ( );
}
@Override
public void register ( String phone, String passWord, int sex ) {
//设置类型为1,回调的类型则为1
setType ( 1 );
Map< String, Object > map = createMap ( 3 );
map.put ( "phone", phone );
map.put ( "passWord", passWord );
map.put ( "sex", sex );
add ( module.register ( map ).subscribe ( this :: viewCallBack, this :: viewCallBack ) );
}
@Override
public void forgetPassword ( String phone, String newPassword ) {
//设置类型为2,回调的类型则为2
setType ( 2 );
Map< String, String > map = createMapStr ( 2 );
map.put ( "phone", phone );
map.put ( "newPassword", newPassword );
add ( module.forgetPwd ( map ).subscribe ( this :: viewCallBack, this :: viewCallBack ) );
}
}
获取用户信息
public class PresenterUserInfo extends BasePresenter< UserInfo, IBaseModel,
IContractUser.IViewUserInfo >
implements IContractUser.IPresenterUserInfo {
public PresenterUserInfo ( IContractUser.IViewUserInfo view ) {
super ( view );
}
@Override
protected IBaseModel createModule ( ) {
return BaseModel.instance ( );
}
@Override
public void getInfo ( ) {
add ( module.userInfo ( token ( ) )
.subscribe ( this :: viewCallBack, this :: viewCallBack ) );
}
}
更新用户信息
public class PresenterUserAction extends BasePresenter< Void, IBaseModel,
IContractUser.IViewUserAction >
implements IContractUser.IPresenterUserAction {
public PresenterUserAction ( IContractUser.IViewUserAction view ) {
super ( view );
}
@Override
protected IBaseModel createModule ( ) {
return BaseModel.instance ( );
}
@Override
public void update ( UserInfo info ) {
add ( module.userInfoUpdate ( token ( ), info )
.subscribe ( this :: viewCallBack, this :: viewCallBack ) );
}
}
小结:从以上的presenter实现类可以看出来,大概有2类特点,一种是回调具体的数据,另一种是回调的Void;每种的写法都大同小异。实现类中,代码的样式基本上都是统一的,规则都是:构造方法-->获取model-->执行方法-->回调ui
三、activity如何使用mcp
1. 登录模块(包含登录、注册、忘记密码(暂不展示))
1.1 登录
public class LoginActivity extends BaseActivity< ActivityLoginBinding > {
@Override
public ActivityLoginBinding setBaseView ( ) {
return ActivityLoginBinding.inflate ( getLayoutInflater ( ) );
}
@Override
public void onBaseCreate ( ) {
//实例化登录操作
IContractLogin.IPresenterLogin presenterLogin = new PresenterLogin ( ( i, b, s, data ) -> {
//登录回调
if ( b ) {
openActivity ( MainActivity.class );
toastAndFinish ( "登录成功" );
} else {
toast ( s );
}
} );
//添加到presenter列表
addPresenter ( presenterLogin );
//登录按钮点击事件
view.btn.setOnClickListener ( v -> {
String phone = getPhone ( );
if ( TextUtils.isEmpty ( phone ) ) {
toast ( "请输入手机号" );
return;
}
String pwd = getPwd ( );
if ( TextUtils.isEmpty ( pwd ) ) {
toast ( "请输入密码" );
return;
}
//开始调用登录接口
presenterLogin.login ( phone, pwd );
} );
}
private String getPhone ( ) {
return view.edtPhone.getText ( ).toString ( );
}
private String getPwd ( ) {
return view.edtPwd.getText ( ).toString ( );
}
}
1.2 注册
public class RegisterActivity extends BaseActivity< ActivityRegisterBinding > {
@Override
public ActivityRegisterBinding setBaseView ( ) {
return ActivityRegisterBinding.inflate ( getLayoutInflater ( ) );
}
@Override
public void onBaseCreate ( ) {
IContractLogin.IPresenterLoginAction presenter =
new PresenterLoginAction ( ( i, b, s, data ) -> {
//i==1虽然有设置i的类型,但由于注册和忘记密码不可能在同一个时刻操作,在这里不用做判断
if ( b ) {
setResult ( RESULT_OK );
toastAndFinish ( "注册成功" );
} else {
toast ( s );
}
} );
//添加到presenter列表
addPresenter ( presenter );
//注册按钮点击事件
view.btn.setOnClickListener ( v -> {
String phone = getPhone ( );
if ( TextUtils.isEmpty ( phone ) ) {
toast ( "请输入手机号" );
return;
}
String pwd = getPwd ( );
if ( TextUtils.isEmpty ( pwd ) ) {
toast ( "请输入密码" );
return;
}
int sex;
if ( view.rb1.isChecked ( ) ) {
sex = 1;
} else if ( view.rb2.isChecked ( ) ) {
sex = 2;
} else if ( view.rb3.isChecked ( ) ) {
sex = 3;
} else {
sex = 0;
}
//调用注册接口
presenter.register ( phone, pwd, sex );
} );
}
private String getPhone ( ) {
return view.edtPhone.getText ( ).toString ( );
}
private String getPwd ( ) {
return view.edtPwd.getText ( ).toString ( );
}
}
2. 用户模块(包含获取用户信息,更新用户信息(不做展示))
2.1获取用户信息
public class UserInfoActivity extends BaseActivity< ActivityUserInfoBinding > {
@Override
public ActivityUserInfoBinding setBaseView ( ) {
return ActivityUserInfoBinding.inflate ( getLayoutInflater ( ) );
}
@Override
public void onBaseCreate ( ) {
IContractUser.IPresenterUserInfo presenterUserInfo=new PresenterUserInfo ( new IContractUser.IViewUserInfo ( ) {
@Override
public void onCallBack ( int i, boolean b, String s, UserInfo data ) {
if(b){
updateUi(data);
}else{
toast ( s );
}
}
} );
addPresenter ( presenterUserInfo );
presenterUserInfo.getInfo ();
}
private void updateUi(UserInfo info){
//忽略更新ui的代码...
}
}
重点:我该如何使用所谓的mcp?
我们假设,你已经配置好了项目,那你就一应该这样做
1.问后端的项目大佬拿到接口文档,去编写IBaseMode(rxjava)。
2.新建契约类,契约类中就是你项目的所有需求,一般来讲可以一个模块对应一个契约类,契约类中再声明某个具体的功能。
3.根据契约类新建各种Presenter。这里我建议这样,契约类的Presenter命名规范、明确一点,到时候新建实现类就复制粘贴就好。如契约类中声明IPresenterLogin,你就复制它,然后新建,直接粘贴“IPresenterLogin”,删掉“I”后,在PresenterLogin中直接implement IPresenterLogin,用快捷键就已经把契约类引入到你新建的类中了,然后那个Model不变,固定为“IBaseModel”,至于那个T(数据),连接到契约类就能拿到了。
补充
1.activity的基类请看这里:https://www.jianshu.com/p/4f28908ca603
2.rxjava的解析工具请看这里:https://www.jianshu.com/p/d882ffae1853
3.Retrofit2Managerd的代码,当你的项目有多个项目地址的时候,传入不同的url且用另一个IBaseModel接收就行。
public class Retrofit2Manager {
private long timeOut = 1000L * 20;
private final String baseUrl;
public static Retrofit2Manager with ( String baseUrl ) {
return new Retrofit2Manager ( baseUrl );
}
private Retrofit2Manager ( String baseUrl ) {
this.baseUrl = baseUrl;
}
public Retrofit2Manager setTimeOut ( long timeOut ) {
this.timeOut = timeOut;
return this;
}
public Retrofit retrofit ( ) {
OkHttpClient.Builder okBuilder = new OkHttpClient.Builder ( );
okBuilder.readTimeout ( this.timeOut, TimeUnit.MILLISECONDS );
okBuilder.writeTimeout ( this.timeOut, TimeUnit.MILLISECONDS );
okBuilder.connectTimeout ( this.timeOut, TimeUnit.MILLISECONDS );
okBuilder.addInterceptor ( new LogInterceptor ( ) );
return ( new retrofit2.Retrofit.Builder ( ) ).client ( okBuilder.build ( ) )
.baseUrl ( this.baseUrl )
.addConverterFactory ( MyConverterFactory.create ( ) )
.addCallAdapterFactory ( RxJava2CallAdapterFactory.create ( ) )
.build ( );
}
}
总结
1.总而言之,mcp是mvp在结合了rxjava之后,我自己总结出来的开发模式,在这种模式下,由接口到视图,逻辑严谨、清晰、合理而且相对简易。
2.mcp直接将model层的逻辑设定为rxjava的逻辑,因此在代码量上,会少很多代码。对于presenter层,有了契约类,使用会更加方便。
3.缺点:mcp模式下,依然撇不开mvp的毛病,就是presenter层的类太多太多,好在contract契约类可按模块管理presenter和view。
以上就是小生对mvp和rxjava结合的总结,诸位大神有什么见解,欢迎提出来,一起学习,一起进步。
网友评论