该封装中除了原有的功能,额外包含什么功能先说一下,另外先展示基本使用方式。看是不是你想要的
1、公共参数的添加
2、token的自动刷新
3、错误请求的自动处理
4、单例模式以及工程模式的retrofit,以适应出现不同域名请求的情况
使用方式如下
1、接口方法定义
/**
* 示例
* 上传名字
* > BaseResponse为封装的公共请求结果实体,Object可以改成对应自己想要的实体
*/
@POST("*********")
Observable> uploadName(@Query("name") String name);
2、调用方法
RxHelper.deploy(RetrofitUtil.getRetrofit().uploadName("小明"), new NetCallback() {
@Override
public void onCompleted(StudentInfo studentInfo) {
//StudentInfo studentInfo 这个要和 方法定义中的BaseResponse的泛型保持一致
}
});
以上就是使用的方法,写这个的初衷是因为我懒,抱着磨刀不误砍柴工的想法,所以封装的尽可能简单。之后我会对该封装做非常细致的说明,每个类名称,和其中所负责的功能都会以文字描述,之后配上该类的整体代码。尽可能做到复制之后可以直接使用,也希望让你看了之后有所借鉴。(需要看的文字简述不多,代码复制可直接使用)
一、首先需要导入的库如下
implementation 'com.squareup.retrofit2:retrofit:2.1.0'
implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'
implementation 'io.reactivex:rxjava:1.0.14'
implementation 'io.reactivex:rxandroid:1.0.1'
二、类名“RetrofitUtil” , retrofit初始化,生产实例的工具类,一般初始化的过程类似,配置参数不同,代码中对配置的参数做详细说明
public class RetrofitUtil {
private static ApiManage api;
/**
* 一般调用方式,用于服务器域名相同的情况
*/
public static ApiManage getRetrofit() {
if(api==null){
Gson gson=new GsonBuilder()
.serializeNulls()
.setLenient()
.create();
api = new Retrofit.Builder()
.baseUrl(Api.BASE_URL) //服务器域名 Api,就是存放通用域名的接口,也可以是静态类,自己写一个,或者直接把地址些里面也可
.client(OkHttpClientHelper.getInstance().getOkHttpClient()) //请求预处理
.addConverterFactory(GsonConverterFactory.create(gson)) //gson自动解析配置
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) //使用Rxjava自定义adapter,通常用法
.build().create(ApiManage.class); //发起请求的接口
}
return api;
}
/**
* 用于服务器域名不同的情况,传入服务器域名
*/
public static ApiManage createRetrofitFrom(String baseUrl) {
if (baseUrl == null) {
baseUrl = Api.BASE_URL;
}
Gson gson=new GsonBuilder()
.serializeNulls()
.setLenient()
.create();
ApiManage api = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(OkHttpClientHelper.getInstance().getOkHttpClient())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build().create(ApiManage.class);
return api;
}
}
三、类名“OkHttpClientHelper” ,请求预处理,主要增加了公共参数的添加,以及请求链接的打印。这里代码做了判断分别进行参数添加,是因为请求以请求体方式和拼接方式请求时,添加参数方式有所不同。使用时记得将公共参数名和值改成自己需要的。
public class OkHttpClientHelper {
private OkHttpClient mOkHttpClient;
private static final int TIME_OUT_LIMIT = 60 * 1000;//超时时间
/**
* 获取OkHttpClient
* @return
*/
public OkHttpClient getOkHttpClient() {
if (mOkHttpClient == null) {
OkHttpClient.Builder builder = new OkHttpClient.Builder()
//设置超时时间
.connectTimeout(TIME_OUT_LIMIT, TimeUnit.MILLISECONDS)
.readTimeout(TIME_OUT_LIMIT, TimeUnit.MILLISECONDS)
.writeTimeout(TIME_OUT_LIMIT, TimeUnit.MILLISECONDS)
.retryOnConnectionFailure(true);//错误重连开启
//添加公共参数
builder.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if ("POST".equals(request.method())||"GET".equals(request.method())) {
if (request.body() instanceof FormBody) {
FormBody.Builder bodyBuilder = new FormBody.Builder();
FormBody formBody = (FormBody) request.body();
// 先复制原来的参数
for (int i = 0; i < formBody.size(); i++) {
bodyBuilder.addEncoded(formBody.encodedName(i), formBody.encodedValue(i));
}
formBody = bodyBuilder
.addEncoded("公共参数名", "公共参数值")
.addEncoded("公共参数名", "公共参数值")
.build();
request = request.newBuilder().post(formBody).build();
}else {
HttpUrl httpUrl = request.url()
.newBuilder()
.addQueryParameter("公共参数名","公共参数值")
.build();
request = request.newBuilder().url(httpUrl).build();
}
}
return chain.proceed(request);
}
});
//添加拦截打印请求日志
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.i("http-request", message);
}
});
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(interceptor);
mOkHttpClient = builder.build();
}
return mOkHttpClient;
}
private OkHttpClientHelper() {
}
public static OkHttpClientHelper getInstance() {
return LazyHolder.INSTANCE;
}
private static final class LazyHolder {
private static final OkHttpClientHelper INSTANCE = new OkHttpClientHelper();
}
}
四、类名“RxHelper” , rxjava的帮助类,针对返回值做了统一处理,也对使用rxjava请求做了一些简化处理。token的刷新被注释掉了,如果需要token的自动刷新并从新请求原接口,注释的部分就很有必要看一下,里面注释很清楚。返回值的code判断请根据自己服务器返回的内容进行修改
public class RxHelper {
private static int refreshTokenNumber = 0;
private static Observable threadControl(Observable observable) {
return observable //线程转换的常用方式,封装节约调用步骤
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io());
}
private static Subscription subscribe(final Observable observable, final NetCallback callback) {
return observable
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
callback.onFailed("系统错误,请联系管理员");
ErrorRequestProcess.throwableErrorRequest(e);
}
@Override
public void onNext(BaseResponse baseResponse) {
if (null != baseResponse) {
if (baseResponse.getCode() == 503) { //判断token过期,重新获取token,并发起原请求
refreshToken(observable, callback); //自定义刷新token方法
return;
}
if (baseResponse.getCode() == 200) { //请求成功返回请求结果
callback.onCompleted(baseResponse.getData());
} else {
callback.onFailed(baseResponse.getMsg());
ErrorRequestProcess.codeErrorRequest(baseResponse.getCode());
}
} else {
callback.onFailed("返回格式错误");
ErrorRequestProcess.codeErrorRequest(0);
}
}
});
}
/**
* token刷新,该方法根据需要自行配置,如果请求中不需要刷新直接打印token过期就好
* @param observable
* @param callback
*/
private static void refreshToken(final Observable observable, final NetCallback callback) {
ToastUtil.showshortToastInButtom(MyApplication.getContext(),"token过期");
// if (refreshTokenNumber > 100) {//针对token的非过期错误的极端措施/因为过期没有单独的返回值
// Toast.makeText(MyApplication.getContext(), "token出现未知错误", Toast.LENGTH_LONG).show();
// refreshTokenNumber = 80;
// return;
// }
// refreshTokenNumber++;
//
// Log.e("<请求错误log>", "CodeError=====: token验证失效,重新获取");
// //刷新token请求的参数预处理,这里根据你的刷新token接口替换
// String dateTime = String.valueOf(System.currentTimeMillis());
// StringBuilder stringBuilder = new StringBuilder();
// stringBuilder.append(Constants.appKey).append(dateTime).append(Constants.appSecret);
// String sign = MD5.md5(stringBuilder.toString());
// //刷新token请求,换成你直接的请求,请求写在ApiManage中
// deploy(RetrofitUtil.getRetrofit().auth(Constants.appKey, dateTime, sign), new NetCallback() {
// @Override
// public void onCompleted(String s) {
// new SharedPreferencesHelper().put("token", s); //这里存储token并全局化,一般是在公共参数出添加
// deploy(observable, callback); //这里固定的调用旧的接口就好,记得本地token的更新处理
// }
// });
}
/**
* 接口请求并返回结果
* @param observable 接口方法
* @param callback 请求结果的接口回调
* @return
*/
public static Subscription deploy(Observable observable, final NetCallback callback) {
Subscription mSubscription = subscribe(threadControl(observable), callback);
return mSubscription;
}
}
五、接口名“ApiManage” ,请求接口,返回类型增加rxjava后改为Observable,@Query注解可以用于一般情况,请求参数直接拼接到请求地址上。
public interface ApiManage {
/**
* 这只是一个实例
*/
@POST("这里是你自己请求的地址") //post是请求方式
Observable> uploadName(@Query("你自己的参数名") String 参数名,@Query("你自己的参数名") String 参数名);
}
六、类名“NetCallback” ,统一的请求回调,这里用了抽象类作为回调接口,方便不进行实现失败的回调,因为失败的回调已经做了统一处理。使用时,在结果回调失败时如果有特殊逻辑,可以直接实现进行。
public abstract class NetCallback {
/**
* 完成
* @param t
*/
public abstract void onCompleted(T t);
/**
* 失败
* @param errMsg
*/
public void onFailed(String errMsg) {
}
}
七、类名“BaseResponse”,返回结果的基础映射类,需要和你服务器返回的通常参数做对应,data的实例根据参数单独创建。该类的使用方式可参考顶部的接口方法定义。
public class BaseResponse {
private int code;
private String msg;
private T data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
八、类名“ErrorRequestProcess”,错误的统一处理类,根据服务器返回code进行打印的地方自根据服务器错误对应的内容改一下
public class ErrorRequestProcess {
public static void throwableErrorRequest(Throwable e) {
String errorMsg = "未知错误";
if (e instanceof UnknownHostException) {
errorMsg = "网络不可用";
} else if (e instanceof SocketTimeoutException) {
errorMsg = "请求网络超时";
} else if (e instanceof HttpException) {
HttpException httpException = (HttpException) e;
errorMsg = convertStatusCode(httpException);
} else if (e instanceof ParseException || e instanceof JSONException
|| e instanceof JSONException) {
errorMsg = "数据解析错误";
}
logThrowableError(errorMsg);
}
private static String convertStatusCode(HttpException httpException) {
String msg;
if (httpException.code() >= 500 && httpException.code() < 600) {
msg = "服务器处理请求出错";
} else if (httpException.code() >= 400 && httpException.code() < 500) {
msg = "服务器无法处理请求";
} else if (httpException.code() >= 300 && httpException.code() < 400) {
msg = "请求被重定向到其他页面";
} else {
msg = "↑↑↑错误信息↑↑↑";
logThrowableError("未知错误",httpException.message());
}
return msg;
}
/**
* 这里是服务器返回的code,根据服务器的规定自行打印错误日志
* @param errorCode
*/
public static void codeErrorRequest(int errorCode) {
switch (errorCode) {
case -1:
logCodeError("未知错误");
return;
case 400:
logCodeError("参数错误");
return;
}
logCodeError("未知错误"+errorCode);
}
public static void logCodeError(String error) {
Log.e("<请求错误log>", "CodeError=====: " + error);
Toast.makeText(MyApplication.getContext(),error,Toast.LENGTH_SHORT).show();
}
public static void logThrowableError(String error) {
Log.e("<请求错误log>", "ThrowableError=====: " + error);
Toast.makeText(MyApplication.getContext(),error,Toast.LENGTH_SHORT).show();
}
public static void logThrowableError(String error,String errorLog) {
Log.e("<请求错误log>", "ThrowableError=====: " + errorLog);
Toast.makeText(MyApplication.getContext(),error,Toast.LENGTH_SHORT).show();
}
}
到此就结束了,这是第二次心血来潮写了篇博客,nnd还挺累。