配置依赖
dependencies{ //添加retrofit2 的依赖 添加这个依赖就默认添加了okhttp依赖
compile 'com.squareup.retrofit2:retrofit:2.0.2'
//支持Gson 及 rxjava
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'
//okhttp log 工具
compile 'com.squareup.okhttp3:logging-interceptor:3.1.2'
//gson
compile 'com.google.code.gson:gson:2.5'
//rxjava
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.3'}开始使用
Retrofit2快速入门
如果使用Gson及Rxjava的话 生成Retrofit需要这样
new Retrofit.Builder() .baseUrl("your base url") .addConverterFactory(GsonConvertFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .client(client) .build();然后对应Api接口的样子类似下面这样:
@GET("user/token")
Observable<JSONObject> fetchToken(); @Headers("Cache-Control: public,max-age=30") @POST("user/login")
Observable<User> login(@Body LoginRequest loginRequest); @FormUrlEncoded
@POST("user/user-feed-back")
Observable<JSONObject> feedback(@Field("content") String content);这些是比较常用的三种方式
实现自定义的设置
通过上边的配置已经可以使用了,但是我们还会有一些常用需求需要加在里面,比如请求失败的统一处理等。
添加日志
添加日志是在OkHttp中使用日志拦截器
OkHttpClient.Builder builder = new OkHttpClient().newBuilder(); builder.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
重写请求头
这个操作也是对OkHttp中使用拦截器(比如每次请求需要携带的请求头和强制缓存)
builder.addNetworkInterceptor(new RewriteInterceptor())
final static class RewriteInterceptor implements Interceptor {
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request originalRequest = chain.request();
Request reWriteRequest = originalRequest.newBuilder()//添加默认的头
.header("Os", "android")
.build();
Response response = chain.proceed(reWriteRequest);
String headValue = originalRequest.cacheControl().toString(); if (!TextUtils.isEmpty(headValue)) {
response = response.newBuilder().header("Cache-Control", headValue).removeHeader("Pragma").build();
} return response;
}对于在请求中设置类似@Headers("Cache-Control: public,max-age=30")进行强制缓存。
OkHttp其他设置
再对OkHttp进行设置 比如 超时时间 和 缓存路径等,到此OkHttp的配置结束,一般来说就配个日志就好了。
报错信息的统一处理
看上面快速入门中有配置这个.addConverterFactory(GsonConvertFactory.create())是为了支持使用Gson解析数据,我们的错误的统一处理可以放到这里。因为每次的请求 和 响应 都会通过这个使用Gson转换为我们需要的内容
自定义ResponseConverterFactory
public class ResponseConverterFactory extends Converter.Factory { /**
* Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static ResponseConverterFactory create() { return create(new Gson());
} /**
* Create an instance using {@code gson} for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static ResponseConverterFactory create(Gson gson) { return new ResponseConverterFactory(gson);
} private final Gson gson; private ResponseConverterFactory(Gson gson) { if (gson == null) throw new NullPointerException("gson == null"); this.gson = gson;
} @Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) { return new GsonResponseBodyConverter<>(gson, type);
} @Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type)); return new GsonRequestBodyConverter<>(gson, adapter);
}
}GsonRequestBodyConverter(不做修改直接copy)
final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> { private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8"); private static final Charset UTF_8 = Charset.forName("UTF-8"); private final Gson gson; private final TypeAdapter<T> adapter;
GsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) { this.gson = gson; this.adapter = adapter;
} @Override public RequestBody convert(T value) throws IOException {
Buffer buffer = new Buffer();
Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
JsonWriter jsonWriter = gson.newJsonWriter(writer);
adapter.write(jsonWriter, value);
jsonWriter.close(); return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
}
}GsonResponseBodyConverter(这里做统一错误处理)
对于服务器返回的失败 抛出对应异常,并且添加对JSONObject的类型装换支持。
@Override
public T convert(ResponseBody value) throws IOException { String response = value.string(); try { //ResultResponse 只解析result字段
ResultResponse resultResponse = gson.fromJson(response, ResultResponse.class); T responseBody = getResponseBody(response); if (resultResponse.isSuccess()){ return responseBody;
} else { //ErrResponse 将msg解析为异常消息文本
throw new ApiException(resultResponse.getErrorCode(), resultResponse.getErrorMsg(),responseBody);
}
} finally {
}
} private T getResponseBody(String response){ if(type instanceof Class){ Class clazz = (Class) type; if(clazz.equals(JSONObject.class)){//如果返回类型 为JsonObject
try { return (T) new JSONObject(response);
} catch (JSONException e) {
}
} if(clazz.equals(JSONArray.class)){ try{ return (T) new JSONArray(response);
}catch (JSONException e){
}
}
} return gson.fromJson(response, type);然后ResponseConverterFactory 替代原来的GsonConvertFactory现在对于服务器返回的错误会抛出异常。
使用自定义的ApiSubscriber对所有异常进行处理
public abstract class ApiSubscriber<T> extends Subscriber<T> {/**
* 对 onError进行处理
* @param e
*/
@Override
public void onError(Throwable e) {
Throwable throwable = e; /**
* 获取根源 异常
*/
while (throwable.getCause() != null){
e = throwable;
throwable = throwable.getCause();
} if(e instanceof HttpException){//对网络异常 弹出相应的toast
HttpException httpException = (HttpException) e; if(TextUtils.isEmpty(httpException.getMessage())){
ToastUtils.toast(R.string.error_net_fail);
}else {
String errorMsg = httpException.getMessage(); if(TextUtils.isEmpty(errorMsg)){
ToastUtils.toast(R.string.error_net_fail);
}else {
ToastUtils.toast(errorMsg);
}
}
}else if(e instanceof ApiException){//服务器返回的错误
onResultError((ApiException) e);
}else if(e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException){//解析异常
ToastUtils.toast(R.string.error_net_parse);
}else if(e instanceof UnknownHostException){
ToastUtils.toast(R.string.error_network_is_not_available);
}else if(e instanceof SocketTimeoutException) {
ToastUtils.toast(R.string.error_net_timeout);
}else {//未知错误
if(DeviceUtils.isNetworkAvailable(GlobalData.getContext())){
ToastUtils.toast(R.string.error_inner_error);
e.printStackTrace();//对于未知错误进行打印,大部分为程序逻辑错误
}else {
ToastUtils.toast(R.string.error_network_is_not_available);
}
}
} /**
* 服务器返回的错误
* @param ex
*/
protected void onResultError(ApiException ex){ //default 打印 服务器返回
String msg = ex.getMessage(); if(TextUtils.isEmpty(msg)){
ToastUtils.toast("服务器未返回具体错误信息");
}else {
ToastUtils.toast(ex.getMessage());
} if(ApiException.TOKEN_INVAILD == ex.getErrCode()){
RxBus.getDefault().post(new ReLoginEvent());
}
}
}这里对常见类型异常使用Toast输出提示信息,对于服务器返回错误打印服务器的错误信息,对于token失效的错误,使用RxBus(类似EventBus)发出事件 ,之后会进行重写登陆的操作。
尝试使用
网络请求不会使用主线程,所以修改一下.addCallAdapterFactory(RxJavaCallAdapterFactory.create())改成默认在io()线程.addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
下面是一个拉取token的简单使用示例:
RetrofitManager.getApiService().fetchToken() .observeOn(AndroidSchedulers.mainThread()) .subscribe(new ApiSubscriber<JSONObject>(){ public void onNext(JSONObject jsonObject){
}
})结语
因为要配合使用Rxjava 最好先学习一遍Rxjava的使用
这样基本功能就齐全了,可以开始正式的开发工作了。关于Api接口定义可以看下Retrofit2+RxJava+Gson 使用补充