四.mvp模式(使用MVPPluge插件,自动生成MVP的类文件以及该插件的改装)
MVP模式理解起来很容易,结合安卓,简单概括:无非就是在activty中将网络请求等与数据相关的操作封装到一个P类中,activty中持有P的引用,同时P中又包含activty中调度View的一些方法,这样activity中只写业务逻辑,请求获取数据全靠P去实现,好处是在请求很多,业务逻辑复杂的情况下整个activity的代码依然清晰,同时activity的职责也更单一。
曾经看到过一句话,大致的意思是:
"代码的规范,业务逻辑的清晰,类之间的解耦等等一定是通过增加而不是减少代码来实现的。”
同样,mvp的问题也就暴露出来了,要实现activity中模块的分离,需要增加P类,需要实现各种接口,因此,之前的项目中一直未使用mvp,都是在activity中写到底,最多就是抽取一些方法到util,helper等类中。
这回虽然引入了mvp,但是实际方法中同样也是分情况来看,如果要实现的功能很简单,或者功能很边缘化,甚至小迭代时间紧,任务重的时候还是直接activity中操作数据,只有在一些比较重要,核心的模块里 通过mvp来做更优雅的实现。
题外话到此,下面重点来说说这次项目搭建中mvp的使用过程。
使用过程:
1.android studio引用 MVPPlugin插件
2.MVPPlugin插件的使用
3.MVPPlugin插件的改造
可以看到这里mvp模式都是围绕一个“ MVPPlugin插件”的东西,这个插件是干嘛的? 其实就是一个帮助开发者一键生成mvp模式所需要的一些接口类,P类等。具体的使用方法可以看看这个
如果你之前也只是听说并未真正使用过mvp模式开发,那么这个插件将极大的节约你的mvp上手的时间成本,基本属于拿来就用。 不过用归用,插件帮你生成的那些类的源码还是要好好看看,特别是基类,做到消化吸收!可能自己暂时写不出来,但起码要理解原理。
五.网络框架的接入(RXJava2+Retrofit2.0+OKHTTP)
这三个玩意儿无疑是目前灰常火的项目,但凡做android开发,可以说不一定用过,但绝对都听过,了解过。
Rxjava:
是个很神奇的东西。网上的描述一大堆:什么 事件流推动,响应式编程,观察订阅等等 我刚接触的时候百度了半天,也没弄清楚到底是个啥,使用有啥好处,直到看到这篇文章,
我将作者称为“管神”,水管的管。
http://www.jianshu.com/u/c50b715ccaeb([给初学者的RxJava2.0教程)
哈哈 有一个系列,帮助上手Rxjava,但实际上你可能会跟我一样。 系列文章看了一遍,感觉懂了,但是想在项目里面用,发现用不上。
然后结合Retrofit2.0+OKHTTP直接先在项目里面用起来,用熟练了再把文章看一遍,哦,恍然大悟-事件推动。
说得好像有点云里雾里,先别喷我,咋先用上再说。
Retrofit2.0:
是个很方便的东西,说它方便,是因为它像一个接口,本身没有实现,没有功能,但是你把Gson框架放进去,它就能json解析,你把OKHTTP放进去它就能发送网络请求,你把Rxjava放进去它就能进行发送事件流。
那么整个的连接起来是神马?
RXJava2+Retrofit2.0+OKHTTP+GSON=“发送 网络请求 并自动解析json数据的事件流”
可能你会说了,作者你又装X,神马框架不能发送网络请求,然后解析json数据?
但我会告诉你,这个X我装定了!重点是“事件流”三个字。现在你给我0分,等你把上面推荐的rxjava文章看完理解之后或许你会重新打出97分。
为了增加说服力,这里我给出一个复杂的场景:
同时压缩两张图片,都压缩成功后调用上传接口,并将上传接口返回的部分数据作为参数,请求另外一个接口。
这个需求涉及的东西还不少,图片压缩,线程变换,网络请求嵌套。如果不用rxjava,可能最常规的方法应该是这样:
开两个子线程压缩图片,handler发送成功信号到主线程,while循环等待,两个标识都成功然后发送网络请求,请求成功回调再嵌套一个请求。
上面的实现基本上能满足这个需求,但是无论从代码量,代码可读性等等而言都不是那么优雅,但是如果采用rxjava,利用它自带的线程切换,操作符zip,事件流转换flatMap的话,你可以写出一段逻辑清晰,代码量少的链式代码,,,,,
好吧,可能这样描述无法达到那种 “听到后连我自己都害怕”的效果,,但是,请相信,,效果真的很牛X,谁用谁知道。
基本使用:
1.初始化一个OkHttpClient
OkHttpClient mOkHttpClient = new OkHttpClient.Builder()
.readTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.SECONDS)
//设置连接超时时间
.connectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.SECONDS)
//设置缓存
.cache(getDefaultCache())
//日志拦截器
.addInterceptor(loggingInterceptor)
//头部拦截器
.addInterceptor(new HeadsInterceptor())
//设置SSH 兼容https
.sslSocketFactory(sslSocketFactory.sSLSocketFactory, sslSocketFactory.trustManager)
//默认全部信任
.hostnameVerifier(SSHUtil.getHostnameVerifier())
.build();
2.通过Retrofit.Builder初始化一个RetrofitApi对象,过程中设置请求代理(就是前面mOkHttpClient ),设置数据解析,设置Rxjaxa
Retrofit.Builder builder = new Retrofit.Builder()
//设置网络请求实现
.client(mOkHttpClient)
//设置json数据解析实现
.addConverterFactory(GsonConverterFactory.create())
//RXJAVA适配器
.addCallAdapterFactory(RxJava2CallAdapterFactory.create());
//这里主域名的设置单独来弄,万一后台奇葩,一个app里两个主域名的话可以再建一个API_X的接口类,这里类似的单独再配置他的baseurl
RetrofitApi httpApi = builder.baseUrl(Constants.APP_HOST)
.build()
.create(RetrofitApi.class);
//配置了RxJava2CallAdapterFactory即集合RXjava的话httpApi调用xxx方法可以直接返回Observable,然后拿来subscribe
//不结合RXjava的话返回httpApi调用xxx方法返回Call对象,然后拿来enqueue发请求
//结合RXjava的优点:更灵活,链式调用,线程切换方便,各种操作符效果吊炸天。 说白了就是可以在处理很复杂嵌套循环请求的时候利用RXJAVA,简化开发
//不过RXJAVA自身的学习成本的确是个问题,我到现在也是云里雾里,不过既然是框架,,,先用再说啦!
3.调用RetrofitApi实例里的业务接口并处理回调
httpApi.getIndexData().subscribe(new Observer<TDataBean<IndexMultBean>>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(TDataBean<IndexMultBean> value) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
})
全局封装说明:
真正使用过程中肯定不会每次请求都重复上面过程,都会做封装,这里是给出的封装实例,可能并不完美,欢迎批评指出哈!
三个类,分别是:
HttpUtil:初始化retrofit,并根据服务器主域名生成一个全局的 RetrofitApi对象
RetrofitApi: 注解的方式定义出具体的业务接口
ApiManager:网络请求发起类,通过获取HttpUtil中的retrofit实例,调用retrofit中定义的业务接口并传入参数。
在MVP模式的Presenter 里调用方式如下:
public class LoginPresenter extends BasePresenterImpl<LoginContract.View> implements LoginContract.Presenter {
private void textHttp() {
ApiManager.login("acount","pwdString").subscribe(new HttpObserver<TDataBean<UserInfo>>(mView) {
@Override
public void onSucceed(TDataBean<UserInfo> value) {
}
@Override
public void onDefeat(TDataBean<UserInfo> value) {
super.onDefeat(value);
}
});
}
}
HttpObserver继承自Observer<T>,对请求结果做了进一步的封装,具体见源码
部分源码展示:
1.添加引用依赖
//OKhttp
compile 'com.squareup.okhttp3:okhttp:3.9.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.7.0'
//retrofit
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
compile 'com.squareup.retrofit2:converter-gson:2.2.0'
//rxjava
compile 'io.reactivex.rxjava2:rxjava:2.0.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
2.请求模块初始化
public class HttpUtil {
//超时时间
private static final long DEFAULT_READ_TIMEOUT_MILLIS = 20;//提交数据超时时间
private static final long DEFAULT_WRITE_TIMEOUT_MILLIS = 20;//提交数据超时时间
private static final long DEFAULT_CONNECT_TIMEOUT_MILLIS = 10;//请求超时时间
//缓存大小
private static final long HTTP_RESPONSE_DISK_CACHE_MAX_SIZE = 10 * 1024 * 1024;
//OkHttpClient
private OkHttpClient mOkHttpClient;
//API接口
private RetrofitApi httpApi;
private HttpUtil() {
initOKHttp();
initRetrofit();
}
public static HttpUtil getInstance() {
return ClassHolder.instace;
}
private static class ClassHolder {
static final HttpUtil instace = new HttpUtil();
}
private void initOKHttp() {
//日志拦截器,专业处理请求过程中的日志 ,header、body数据
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
//根据包的类型控制日志输出与否
loggingInterceptor.setLevel(BasePackageUtil.isApkDebugable() ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.NONE);
//SSH相关设置,处理https请求的,默认全部信任,不添加的话遇到https请求可能会出异常,WebView,Picasso,Gilde等框架都会有此问题
SSLParams sslSocketFactory = SSHUtil.getSslSocketFactory();
mOkHttpClient = new OkHttpClient.Builder()
.readTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.SECONDS)
//设置连接超时时间
.connectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.SECONDS)
//设置缓存
.cache(getDefaultCache())
//日志拦截器
.addInterceptor(loggingInterceptor)
//头部拦截器
.addInterceptor(new HeadsInterceptor())
//设置SSH 兼容https
.sslSocketFactory(sslSocketFactory.sSLSocketFactory, sslSocketFactory.trustManager)
//默认全部信任
.hostnameVerifier(SSHUtil.getHostnameVerifier())
.build();
}
private void initRetrofit() {
Retrofit.Builder builder = new Retrofit.Builder()
//设置网络请求实现
.client(mOkHttpClient)
//设置json数据解析实现
.addConverterFactory(GsonConverterFactory.create())
//RXJAVA适配器
.addCallAdapterFactory(RxJava2CallAdapterFactory.create());
//这里主域名的设置单独来弄,万一后台奇葩,一个app里两个主域名的话可以再建一个API_X的接口类,这里类似的单独再配置他的baseurl
httpApi = builder.baseUrl(Constants.APP_HOST)
.build()
.create(RetrofitApi.class);
//配置了RxJava2CallAdapterFactory即集合RXjava的话httpApi调用xxx方法可以直接返回Observable,然后拿来subscribe
//不结合RXjava的话返回httpApi调用xxx方法返回Call对象,然后拿来enqueue发请求
//结合RXjava的优点:更灵活,链式调用,线程切换方便,各种操作符效果吊炸天。 说白了就是可以在处理很复杂嵌套循环请求的时候利用RXJAVA,简化开发
//不过RXJAVA自身的学习成本的确是个问题,我到现在也是云里雾里,不过既然是框架,,,先用再说啦!
}
public OkHttpClient getmOkHttpClient() {
return mOkHttpClient;
}
public RetrofitApi getHttpApi() {
return httpApi;
}
public Cache getDefaultCache() {
String dir = BaseFileUtil.getHttpCacheDir();
File file = new File(dir);
Cache cache = new Cache(file, HTTP_RESPONSE_DISK_CACHE_MAX_SIZE);
return cache;
}
}
3.API封装与调用
public interface RetrofitApi {
/*获取首页数据*/
@POST(Constants.FUNCTION_INDEX)
Observable<TDataBean<IndexMultBean>> getIndexData();
/*登录*/
@POST(Constants.FUNCTION_LOGIN)
Observable<TDataBean<UserInfo>> login(@Body JsonObject object);
}
public class ApiManager {
//统一设置线程
private static <T> Observable<T> subscribeOn(Observable<T> observable) {
return observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
//获取RetrofitApi实例
private static RetrofitApi getJavaApi() {
return HttpUtil.getInstance().getHttpApi();
}
/***************************************业务逻辑接口**************************************/
/*获取首页数据*/
public static Observable<TDataBean<IndexMultBean>> getIndexData() {
Observable<TDataBean<IndexMultBean>> observable = getJavaApi().getIndexData();
return subscribeOn(observable);
}
/*获取首页数据*/
public static Observable<TDataBean<UserInfo>> login(String username, String pwd) {
JsonObject data = new JsonObject();
data.addProperty("mobile", username);
data.addProperty("password", pwd);
data.addProperty("device_token", DeviceUtils.getDeviceId());
Observable<TDataBean<UserInfo>> observable = getJavaApi().login(data);
return subscribeOn(observable);
}
}
完整的demo地址:
https://gitee.com/lunguoguo/revised_development_framework.git (已更新)