登录 立即注册
安币:

开始使用Retrofit2+RXjava+Gson [复制链接]

2017-7-10 20:04
BeatBeat 阅读:2647 评论:0 赞:1
Tag:  

配置依赖

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 使用补充

分享到:
我来说两句
facelist
您需要登录后才可以评论 登录 | 立即注册
所有评论(0)

站长推荐

通过邮件订阅最新安卓weekly信息
上一条 /4 下一条

下载安卓巴士客户端

全国最大的安卓开发者社区

广告投放| 下载客户端|申请友链|手机版|站点统计|安卓巴士 ( 粤ICP备15117877号 )

返回顶部