配置依赖
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 使用补充