在使用Retrofit/Okhttp时会发现有一部分代码是重复出现的,所以对重复的这部分代码进行简单封装,提高开发效率也让网络请求调用更加的方便(无需再写重复的部分代码)。
1、封装分析
Retrofit网络框架中的网络的请求部分交给了Okhttp来处理,Retrofit通过接口方式封装了请求的参数、通过注解方式告知Okhttp需要调用的是GET\POST还是其他的请求网络方法;所以一个网络请求过程中,我需要同时创建Retrofit和OkhttpClient两个对象;首先需创建OkhttpClient、因为OkhttpClient是Retrofit的一个成员(它们之间就是简单的依赖关系)。
OkhttpClient的创建(建造者模式)
client = new OkHttpClient.Builder()
.sslSocketFactory(SSLHelper.getSSLCertifcation(this))
.hostnameVerifier(new UnSafeHostnameVerifier())
.addInterceptor(loggingInterceptor)
.connectTimeout(mTimeOut, TimeUnit.SECONDS)
.readTimeout(mTimeOut, TimeUnit.SECONDS)
.writeTimeout(mTimeOut, TimeUnit.SECONDS)
.build();
以上我们为通过构造者模式创建了OkhttpClient对象client,配置https所需要的证书环境、添加了日志拦截器(当然还可以添加通用的请求头信息)、配置网的请求超时时长和读写超时时长。这些参数对于一个客户端网络请求而言都是通用的,所以OkhttpClient对象的创建过程完全可以进行内部封装了。
Retrofit的创建(建造者模式)
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
Retrofit对象的创建也是通过构造者模式,以上配置的服务器地址baeUrl、gosn转换和之前创建的OkhttpClient对象,正常情况下一个客户端可能对应多个服务器进行数据交互,所以baseUrl可能是多个的,这里我们暂且认为一个客户端对应着一个服务器,因为一个客户端对应多个服务器后台,一般会通过一个中间服务器(中台系统)我目前接触的项目也是这样的。
所以Retrofit 的创建也可以完全进行封装(偷个笑),那么单例模式就可以搞定我们的需求了,应为retrofit是通用的(唯一的),又得是全局性的(一个项目中很多地方都得用到网络请求),所以采用单例模式在适合不过了。
2、封装代码
/**
* Created by zhouds
* Created time 2018/10/4.
* Description:
* Version: V 1.0
*/
public class RetrofitManager {
/**
* baseUrl 在application中赋值
*/
public static String baseUrl;
/**
* token
*/
public static String userToken = "4234345dfgd754df";
/**
* 超时时间
*/
private static final int DEFAULT_TIME_OUT = 15;
private static RetrofitManager mRetrofitManager;
private static HttpLoggingInterceptor loggingInterceptor;
private static OkHttpClient okHttpClient;
private Retrofit mRetrofit;
//静态块,类加载时初始化OkHttpClient对象
static {
initOkHttpClient();
}
private RetrofitManager() {
if (TextUtils.isEmpty(baseUrl)){
new Throwable("baseUrl is Empty").printStackTrace();
}
mRetrofit = new Retrofit.Builder()
.baseUrl(baseUrl+"ForAnd/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build();
}
public static synchronized RetrofitManager getInstance() {
if (mRetrofitManager == null) {
synchronized (RetrofitManager.class) {
if (mRetrofitManager == null) {
mRetrofitManager = new RetrofitManager();
}
}
}
return mRetrofitManager;
}
/**
* 获取单例OkHttpClient
*
* @return
*/
@SuppressWarnings("deprecated")
private static OkHttpClient initOkHttpClient() {
//添加公共参数拦截器
HttpCommonInterceptor interceptor = new HttpCommonInterceptor.Builder()
.addHeaderParams("paltform", "android")
.addHeaderParams("userToken", userToken)
.build();
//日志
loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
//打印retrofit日志
Log.i("RetrofitLog", "retrofitBack = " + message);
}
});
//日志等级
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
if (okHttpClient == null) {
synchronized (OkHttpClient.class) {
if (okHttpClient == null) {
okHttpClient = new OkHttpClient.Builder()
//http + ssl
.sslSocketFactory(SSLHelper.getSSLCertifcation(MyApplicaiton.getContext()))
.hostnameVerifier(new UnSafeHostnameVerifier())
//打印拦截器日志
.addNetworkInterceptor(loggingInterceptor)
//设置连接超时时间
.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS)
//设置读取超时时间
.readTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS)
// 设置写入超时时间
.writeTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS)
.build();
}
}
}
return okHttpClient;
}
/**
* 创建相应的服务接口
*/
public <T> T create(Class<T> reqServer) {
return mRetrofit.create(reqServer);
}
}
1)可以把baseUrl放置到AppAplication的中
@Override
public void onCreate() {
super.onCreate();
context = this;
LitePal.initialize(this);
//网络baseUrl初始化
RetrofitManager.baseUrl = IPutils.getIP2();
//热更新初始化
initHotFix();
//其他初始化...
}
2)HttpCommonInterceptor 是定义的拦截器
/**
* Created by zhouds
* Created time 2018/11/5.
* Description: 自定义拦截器、添加请求头
* Version: V 1.0
*/
public class HttpCommonInterceptor implements Interceptor {
private Map<String, String> mHeaderParamsMap = new HashMap<>();
public HttpCommonInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Request oldRequest = chain.request();
// 新的请求
Request.Builder requestBuilder = oldRequest.newBuilder();
requestBuilder.method(oldRequest.method(), oldRequest.body());
// 添加公共参数,添加到header中
if (mHeaderParamsMap.size() > 0) {
for (Map.Entry<String, String> params : mHeaderParamsMap.entrySet()) {
requestBuilder.header(params.getKey(), params.getValue());
}
}
Request newRequest = requestBuilder.build();
return chain.proceed(newRequest);
}
public static class Builder {
HttpCommonInterceptor mHttpCommonInterceptor;
public Builder() {
mHttpCommonInterceptor = new HttpCommonInterceptor();
}
public Builder addHeaderParams(String key, String value) {
mHttpCommonInterceptor.mHeaderParamsMap.put(key, value);
return this;
}
public Builder addHeaderParams(String key, int value) {
return addHeaderParams(key, String.valueOf(value));
}
public Builder addHeaderParams(String key, float value) {
return addHeaderParams(key, String.valueOf(value));
}
public Builder addHeaderParams(String key, long value) {
return addHeaderParams(key, String.valueOf(value));
}
public Builder addHeaderParams(String key, double value) {
return addHeaderParams(key, String.valueOf(value));
}
public HttpCommonInterceptor build() {
return mHttpCommonInterceptor;
}
}
}
3)对外提供create方法用于创建接口类型的代理对象
public <T> T create(Class<T> reqServer) {
return mRetrofit.create(reqServer);
}
4)为了支持Rxjava的使用添加配置
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
//同时需要在gradel中添加配置
//retrofit2 配合Rxjava 使用
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
3、使用
普通方式
1)定义接口
public interface ItemsBiz {
@GET("items.action")
Call<Items>getItems();
@GET("items.action")
Observable<Items>getItemsRx();
}
2)通过RetrofitManager来生成ItemsBiz 对象
private void doGet() {
//ItemsBiz itemsBiz = retrofit.create(ItemsBiz.class);
ItemsBiz itemsBiz = RetrofitManager.getInstance().create(ItemsBiz.class);
Call<Items> call = itemsBiz.getItems();
call.enqueue(new Callback<Items>() {
@Override
public void onResponse(Call<Items> call, Response<Items> response) {
textShow.setText(gson.toJson(response.body()));
}
@Override
public void onFailure(Call<Items> call, Throwable t) {
t.printStackTrace();
}
});
}
结合Rxjava方式
RetrofitManager.getInstance().create(ItemsBiz.class).getItemsRx().subscribe(new Consumer<Items>() {
@Override
public void accept(Items items) throws Exception {
}
});
至此Retrofit的封装和基本使用就介绍完毕了。网上也看到把一个接口定义到一个业务Loader中,并在Loader中进行代理对象的生成和具体方法调用,向外部提供相关网请求方法。
业务Loader
1)定义一个顶层Loader
/**
* Created by zhouds
* Created time 2018/10/6.
* Description:
* Version: V 1.0
*/
public class ObjectLoader {
/**
* * @param observable * @param <T> * @return
*/
protected <T> Observable<T> observe(Observable<T> observable) {
return observable
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
}
这里封装了一个通用方法,用于设置Rxjava中子线程与主线程之间切换。
2)定义具体的业务Loader
/**
* Created by zhouds
* Created time 2018/10/6.
* Description:
* Version: V 1.0
*/
public class ItemLoader extends ObjectLoader{
private ItemService itemService;
/**
* 2、初始化:调用RetrofitManager获得retrofit单例,并creat接口
*/
public ItemLoader(){
itemService = RetrofitManager.getInstance().create(ItemService.class);
}
/**
* 1、定义retrofig 接口
*/
public interface ItemService{
/**
* @param count
* @return
*/
@GET("itemList.action")
Observable<List<Items>> getItemsQurey(@Query("count")Integer count);
/**
* @param count
* @return
*/
@GET("query.action")
Observable<List<Customer>> getQueryCustomer(@Query("count")Integer count);
}
/*----3、供外部调用的方法(结合Rxjava)----*/
/**
* 查询
*
* @param count
* @return
*/
public Observable<List<Items>> getItems( Integer count ){
return observe(itemService.getItemsQurey(count));
}
/**
* 查询
*
* @param count
* @return
*/
public Observable<List<Customer>> getCustomers(Integer count){
return observe(itemService.getQueryCustomer(count));
}
}
3)初始化Loader
itemLoader = new ItemLoader();
4)使用
private void rxRetrofit2() {
itemLoader.getCustomers(3).flatMap(new Function<List<Customer>, ObservableSource<Customer>>() {
@Override
public ObservableSource<Customer> apply(List<Customer> customers) throws Exception {
return Observable.fromIterable(customers);
}
}).filter(new Predicate<Customer>() {
@Override
public boolean test(Customer customer) throws Exception {
return customer.getId() == 1;
}
}).subscribe(new Consumer<Customer>() {
@Override
public void accept(Customer customer) throws Exception {
textShow.setText(gson.toJson(customer));
}
});
}
itemLoader.getCustomers(3)执行了网络查询并返回被观察者,此时我们不需要关系接口对象的创建和内部具体调用了什么方法,这些操作全部封装在了Loader业务中;getCustomers方法之后调用的都是Rxjava的相关操作符,对数据进行处理,flatMap对List集合数据进行转换成一个个对象,filter对数据进行过滤获取我们想要的数据,最终通过subscribe订阅被观察者,观察接收数据并展示出来。
下一篇《网络(五)》将整理一下 Retrofit2请求webservice接口(即xml的组装和xml的解析)