Retroft-简单封装

前言

Retrofit的使用已近有一段时间了,并做了一些简单的封装,但仅仅会使用是远远不够的,为此希望做一个总结,1是对Retroft的封装和使用、2是Retroft涉及到的Java技术、3是Retrofit相关的源码分析。这一篇主要是记录Retrofit的封装使用。

对于一个网络框架,在使用中我希望能做如下封装

  1. 统一无网络提示、统一网络超时判断
  2. 添加统一Heard头,添加统一请求参数
  3. 设置数据请求缓存
  4. 统一数据解析
  5. 统一处理Http请求错误如Code等于500,统一处理后台返回的errorCode,如token过期错误
  6. 统一请求数据加密

接下来开始实现上面的功能,首先对:统一无网络提示、统一网络超时判断
实现这个需要用到OkHttp的拦截器, 在下面HttpClient工具类中设置网络拦截器,拦截器中返回的是一个Chain接口,Chain接口是OkHttp中Interceptor的一个接口

public class NetWorkInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        if (!NetWorkUtils.isNetWorkAvailable(BaseApplication.getInstance())) {
            throw new SocketException("网络未连接!");
        } else {
            return chain.proceed(chain.request());
        }
    }
}

可以根据下面方法获取到RequestResponse, Request.Builder builder = originalRequest.newBuilder();就返回一个Request.Builder,Request.Builder中可以设置有关Header相关的参数。

  Request request = chain.request();
        Response response = chain.proceed(request);

看下面拦截器的相关方法可知道在其中可以设置相关超时时间,具体拦截中能做什么其实就看拦截器中能获取到那些参数,那么其相关的的属性就可以进行设置

public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    /**
     * Returns the connection the request will be executed on. This is only available in the chains
     * of network interceptors; for application interceptors this is always null.
     */
    @Nullable Connection connection();

    Call call();

    int connectTimeoutMillis();

    Chain withConnectTimeout(int timeout, TimeUnit unit);

    int readTimeoutMillis();

    Chain withReadTimeout(int timeout, TimeUnit unit);

    int writeTimeoutMillis();

    Chain withWriteTimeout(int timeout, TimeUnit unit);
  }
}

添加统一Heard头,添加统一请求参数:

public class QueryParameterInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {

        Request originalRequest = chain.request();
        
        originalRequest.newBuilder()
                .addHeader("version", "1")
                .addHeader("time", System.currentTimeMillis() + "");


        HttpUrl.Builder authorizedUrlBuilder = originalRequest.url()
                .newBuilder()
                .scheme(originalRequest.url().scheme())//设置Http或Https
                .host(originalRequest.url().host())//设置host
                .addQueryParameter("aaa", "111")
                .addQueryParameter("bbb", "222");


        Log.i("TAG", "=======" + originalRequest.body().toString());
        Request newRequest = originalRequest.newBuilder()
                .method(originalRequest.method(), originalRequest.body())
                .url(authorizedUrlBuilder.build())
                .build();
        return chain.proceed(newRequest);
    }
}

设置数据请求缓存


public class CacheInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        if (!NetWorkUtils.isNetWorkAvailable(BaseApplication.getInstance())) {//没网强制从缓存读取(必须得写,不然断网状态下,退出应用,或者等待一分钟后,就获取不到缓存)
            request = request.newBuilder()
                    .cacheControl(CacheControl.FORCE_CACHE)
                    .build();
        }
        Response response = chain.proceed(request);
        if (NetWorkUtils.isNetWorkAvailable(BaseApplication.getInstance())) {//有网情况下,从服务器获取
            int maxAge = BuildConfigs.DEFAULT_COOKIE_NETWORK_TIME;
// 有网络时, 缓存最大保存时长为60s
            response.newBuilder()
                    .header("Cache-Control", "public, max-age=" + maxAge)
                    .removeHeader("Pragma")
                    .build();
        } else {//没网情况下,一律从缓存获取
// 无网络时,设置超时为30天
            int maxStale = BuildConfigs.DEFAULT_COOKIE_NO_NETWORK_TIME;
            response.newBuilder()
                    .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                    .removeHeader("Pragma")
                    .build();
        }
        return response;
    }
}

统一数据解析,MyGsonConverterFactory的使用是在Retrofit创建的时候进行设置,Converter是Retrofit对数据解析的封装,

public final class MyGsonConverterFactory 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 MyGsonConverterFactory 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 MyGsonConverterFactory create(Gson gson) {
        return new MyGsonConverterFactory(gson);
    }

    private final Gson gson;

    private MyGsonConverterFactory(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        this.gson = gson;
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
                                                            Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new ResponseBodyConverter<>(gson, adapter);
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type,
                                                          Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new RequestBodyConverter<>(gson, adapter);
    }

}

ResponseBodyConverter的convert方法会得到ResponseBody,这个类是我们自己定义的,一般定义方法就是一个errorCode,一个errorMsg,以及一个泛型对象,泛型对象就是我们具体要得到的对象。在请求成功的情况下, 我们可以将所有非正常返回在这里进行一次过滤,但是一些请求失败的并不能在这里进行设置提示。需要我们自定义Callback。


public final class ResponseBodyConverter<T> implements Converter<ResponseBody, T> {

    private static final int SUCCESS = 0;    // 成功
    private static final int FAILURE = 1;        // 失败 提示失败msg
    private static final int TOKEN_EXPIRE = 2;  // token过期
    private static final int SERVER_EXCEPTION = -1;  // 服务器异常

    private final Gson gson;
    private final TypeAdapter<T> adapter;

    public ResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
    }

    @Override
    public T convert(ResponseBody value) throws IOException {
        String json = value.string();
        verify(json);
        JSONObject jsonObject = null;
        try {
            jsonObject = new JSONObject(json).getJSONObject("data");
        } catch (JSONException e) {
            e.printStackTrace();
        }

        try {
            return adapter.read(gson.newJsonReader(new StringReader(jsonObject.toString())));
        } finally {
            value.close();
        }

    }

    private void verify(String json) {
        ResponseData result = gson.fromJson(json, ResponseData.class);
        if (result.getErrorCode() != SUCCESS) {
            switch (result.getErrorCode()) {
                case FAILURE:
                case SERVER_EXCEPTION:
                    throw new MyException(result.getErrorMsg());
                case TOKEN_EXPIRE:
                    throw new MyException(result.getErrorMsg());
                default:
                    throw new MyException(result.getErrorMsg());
            }
        }
    }


}

对于统一的加密,这里首先要获取到设置的所有参数,参数都存在于RequestBody中,我们可以在RequestBodyConverter的convert中获取到这个参数,但是,RequestBodyConverter的convert方法,并不是所有的请求都会被调用,目前我知道的是当设置参数为@Body的时候时是可以调用到的,具体的什么时候被调用到得看RequestFactory类中哪里调用了retrofit.requestBodyConverter方法

public final class RequestBodyConverter<T> implements Converter<T, RequestBody> {

    private static final MediaType MEDIA_TYPE = MediaType
            .parse("application/json; charset=UTF-8");
    static final Charset UTF_8 = Charset.forName("UTF-8");

    private final Gson gson;
    private final TypeAdapter<T> adapter;


    RequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
        System.out.println("#IRequestBodyConverter初始化#");

    }

    @Override
    public RequestBody convert(T value) throws IOException {
        String json = value.toString();
        Log.e("LOG", "#加密前#" + json);
        //  json = AesEncryptionUtil.encrypt(json);
        Log.e("LOG", "#加加密后#" + json);
        return RequestBody.create(MEDIA_TYPE, json);

    }


}

在这里我们创建一个自定义Callback,在此处可以处理请求过程中response返回的400,500等提示,并且也可以在这里处理ResponseData中不同Code的结果

public abstract class OkCallback<T> implements Callback<T> {
    @Override
    public void onResponse(Call<T> call, Response<T> response) {
        //这里可以对其他Code 吗码进行监听,做特殊处理
        if (200 == response.code()) {
            onSuccessful(call, response);
        } else {
            Throwable throwable = new Throwable(response.message());
            onFail(call, throwable, response);
        }
    }

    public abstract void onSuccessful(Call<T> call, Response<T> response);

    @Override
    public void onFailure(Call<T> call, Throwable t) {
        onFail(call, t, null);
    }

    protected void onFail(Call<T> call, Throwable t, Response<T> response) {
        Toast.makeText(BaseApplication.getInstance(), t.getMessage(), Toast.LENGTH_SHORT).show();
    }
}

public class ResponseData<T> implements Serializable {

    private static final long serialVersionUID = 5213230387175987834L;

    private int errorCode;
    private String errorMsg;
    private T data;


    public int getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(int errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

这里是对前面添加的设置统一处理,请求前的封装

public class HttpClient {

    private Api api;


    private HttpClient() {
        Cache cache = new Cache(new File(BuildConfigs.PATH_CACHE), BuildConfigs.DEFAULT_CACHE_SIZE);

        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .addInterceptor(new NetWorkInterceptor())
                .addInterceptor(new HeaderInterceptor())//设置Header
                .addInterceptor(new QueryParameterInterceptor())//设置请求参数
                .addNetworkInterceptor(new CacheNetWorkInterceptor())//设置缓存
                .addInterceptor(new CacheInterceptor())
                .cache(cache)
                .connectTimeout(BuildConfigs.DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)//设置连接超时时间
                .readTimeout(BuildConfigs.DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)//设置读取超时时间
                .writeTimeout(BuildConfigs.DEFAULT_WRITE_TIMEOUT, TimeUnit.SECONDS)//设置写的超时时间
                .retryOnConnectionFailure(true);//错误重连
      //  okhttp3.internal.Util.checkDuration();

//调试模式打印Log日志
        if (BuildConfig.DEBUG) {
            //    builder.addInterceptor(new LoggingInterceptor());
        }

        OkHttpClient client = builder.build();

        Gson gson = new GsonBuilder()
                //配置你的Gson
                .setDateFormat("yyyy-MM-dd hh:mm:ss")
                .create();


        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(AppConst.BASE_URL)
                .client(client)
                //  .addConverterFactory(GsonConverterFactory.create(gson))
                //  .addConverterFactory(GsonConverterFactory.create())
                .addConverterFactory(MyGsonConverterFactory.create())
                //   .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                //   .callbackExecutor()
                .build()
        api = retrofit.create(Api.class);
    }
    private static class HttpClientHolder {
        private static final HttpClient INSTANCE = new HttpClient();
    }
    public static HttpClient getInstance() {
        return HttpClientHolder.INSTANCE;
    }

    public Api getApiService() {
        return api;
    }
}
private void onLogin() {

        HttpClient.getInstance().getApiService()
                .onLogin("18201883983", "1111111", "1111111")
                .enqueue(new OkCallback<News>() {
                    @Override
                    public void onSuccessful(Call<News> call, Response<News> response) {
                        Toast.makeText(AModuleActivity.this, "登录成功:" + response.body().getUsername(), Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    protected void onFail(Call<News> call, Throwable t, Response<News> response) {
                        super.onFail(call, t, response);
                    }
                });
    }

demo下载地址
Retrofit封装

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值