Retrofit封装和使用

关于Retrofit基础知识的学习,请参考这篇文章

Retrofit实战技巧

代码的封装

  1. APIService 使用Retrofit要求我们将项目中使用的接口请求定义写在一个接口中,咱们就写在APIService中。

  2. RequestManager类,该类就是retrofit请求管理类,该类使用单例模式,封装了项目使用的Intercepter、baseUrl、connectTimeOut、convertFactory等,

 public class RequestManager {
    public static final long DEFAULT_TIME_LIMIT = ColorVConst.tapi ? 3000 : 30;
    private static RequestManager requestManager;
    private APIService apiService;

    public static RequestManager getInstance() {
        if (requestManager == null) {
            synchronized (RequestManager.class) {
                if (requestManager == null) {
                    requestManager = new RequestManager();
                }
            }
        }
        return requestManager;
    }

    private RequestManager() {
        OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
        if (ColorVConst.DEBUG) {
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
                @Override
                public void log(String message) {
                    LogUtils.d(message);
                }
            });
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);
            okHttpClientBuilder.addInterceptor(logging);
        }
        okHttpClientBuilder.addInterceptor(new HeaderInterceptor())
                .addInterceptor(new ResponseIntercepter())
                .connectTimeout(DEFAULT_TIME_LIMIT, TimeUnit.SECONDS)
                .build();

        Retrofit retrofit = new Retrofit.Builder()
                .client(okHttpClientBuilder.build())
                .baseUrl(ServerInterfaceConst.BACK_SERVER)
                .addConverterFactory(JsonConverterFactory.create())
                .build();
        apiService = retrofit.create(APIService.class);
    }

    public APIService getApiService() {
        return apiService;
    }
}

  1. HeadIntercepter类,因为咱们每次请求都需要向服务器发送utk,所以统一发送Header比较方便,所以就在retrofit请求的时候进行拦截,将header加到请求中,自定义Intercepter需要实现Intercepter接口,并实现intercept方法。
public class HeaderInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request.Builder builder = chain.request()
                .newBuilder();
        try {
            Map<String, String> headers = ServerHandler.getCommonHeaders();
            for (String key : headers.keySet()) {
                builder.addHeader(key, headers.get(key));
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return chain.proceed(builder.build());
    }
}
  1. ResponseIntercepter 因为咱们部分json返回的时候Content-Encoding格式为gzip,而retrofit好像没处理这种情况,所以需要对返回后的数据进行拦截并处理。
public class ResponseIntercepter implements Interceptor {
    private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");

    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        Response.Builder builder = response.newBuilder();
        String encodeing = response.header("Content-Encoding");
        if (encodeing != null) {
            if ("gzip".equals(encodeing.toLowerCase(Locale.CHINA))) {
                GZIPInputStream gzin;
                InputStream is = response.body().byteStream();
                gzin = new GZIPInputStream(is);
                InputStreamReader isr = new InputStreamReader(gzin, "utf-8");
                BufferedReader br = new BufferedReader(isr);
                StringBuilder sb = new StringBuilder();
                String tempbf;
                while ((tempbf = br.readLine()) != null) {
                    sb.append(tempbf);
                    sb.append("\r\n");
                }
                isr.close();
                gzin.close();
                ResponseBody body = ResponseBody.create(MEDIA_TYPE, sb.toString());
                return builder.body(body).build();
            }
        }
        return response;
    }
}

  1. JsonConverterFactory 该类描述了在请求数据时和返回数据时序列化反序列化的规则,该类继承Converter.Factory,并需要实现两个方法
	public class JsonConverterFactory extends Converter.Factory {
    public static JsonConverterFactory create() {
        return new JsonConverterFactory(new Gson());
    }

    private Gson gson;

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

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
		//表示返回的数据反序列化为Java Bean的规则
        return new JsonResponseBodyConverter<>(type, gson, annotations);
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
		//表示请求的数据对象序列化为json的规则
        return new JsonRequestBodyConverter<>(gson, adapter);
    }
}
  1. JsonRequestBodyConverter 表示post请求的数据对象序列化为json的规则 里面实现使用gson序列化,如果实体类规范,可以直接使用实体类,如果不规范,还是直接使用JSONObject比较好。
/**
 * Created by star on 2017/2/16
 * 功能:
 */
final class JsonRequestBodyConverter<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;

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

    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());
//        return RequestBody.create(MEDIA_TYPE,value.toString());
    }
}
  1. JsonResponseBodyConverter 在这里面处理返回结果的解析,因为实体类的不规范,gson解析不行,所以需自行处理,在我们需要实体类被解析的时候,实体类必须实现ParseInterface接口并实现T parse(JSONObject jsonObject)方法,因为JsonResponseBodyConverter里面处理解析就是根据该接口的解析方法进行解析的,如果不实现该接口或者该接口的解析方法体为空,那实体类将不能被完整解析,另外需要注意的是,该解析只支持解析为T或List,不支持解析成Map什么的。
final class JsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
    private static final Charset UTF_8 = Charset.forName("UTF-8");
    private final Type type;
    private final Gson gson;
    private final Annotation[] annotations;

    JsonResponseBodyConverter(Type type, Gson gson, Annotation[] annotations) {
        this.type = type;
        this.gson = gson;
        this.annotations = annotations;
    }

    @Override
    public T convert(ResponseBody value) throws IOException {
        JSONObject repJson;
        try {
            String string = value.string();
            //value.string()如果调用两次就会抛出异常,因为调用完之后缓冲区就关掉了,再次调用就IOException
            if (ColorVConst.DEBUG) {
                LogUtils.d(string);
            }

            repJson = new JSONObject(string);
            ServerHandler.handleMessage(repJson);

            if (type instanceof ParameterizedType) {
                ParameterizedType cla = (ParameterizedType) type;
                if (cla.getRawType().equals(BaseResponse.class)) {
                    return (T) GsonUtil.fromJson(string, cla);
                }
            }

            Object data = repJson.opt("data");
            if (data == null) {
                return null;
            }

            if (data instanceof JSONObject) {
                JSONObject objectData = (JSONObject) data;
                if (!(type instanceof Class)) {
                    throw new ParseException("Call<T> 里面传入的泛型不是Class!");
                }

                Class claz = (Class) type;
                Class[] interfaces = claz.getInterfaces();
                for (Class cl : interfaces) {
                    if (cl.getSimpleName().equals(ParseInterface.class.getSimpleName())) {
                        Method parse = claz.getMethod("parse", JSONObject.class);
                        return (T) parse.invoke(claz.newInstance(), objectData);
                    }
                }

                return GsonUtil.parseJsonWithGson(objectData, (Class<T>) type);

            } else if (data instanceof JSONArray) {
                if (!(type instanceof ParameterizedType)) {
                    throw new ParseException("Call<T> 里面传入的类型不是List!");
                }

                JSONArray arrayData = (JSONArray) data;
                ParameterizedType list = (ParameterizedType) type;
                Type[] actualTypeArguments = list.getActualTypeArguments();
                Type rawType = list.getRawType();
                if (!rawType.equals(List.class)) {
                    throw new ParseException("Call<T> 里面传入的类型不是List!");
                }

                if (actualTypeArguments == null) {
                    throw new ParseException("List内部必须嵌套实体类 !");
                }

                Type actualTypeArgument = actualTypeArguments[0];
                if (!(actualTypeArgument instanceof Class)) {
                    throw new ParseException("List内部传入的泛型不是Class !");
                }

                List resultList = new ArrayList<>();
                for (int i = 0; i < arrayData.length(); i++) {
                    JSONObject jsonObject = arrayData.getJSONObject(i);
                    Class claz = (Class) actualTypeArgument;
                    Class[] interfaces = claz.getInterfaces();
                    if (interfaces != null) {
                        boolean hasFind = false;
                        for (Class cl : interfaces) {
                            if (cl.getSimpleName().equals(ParseInterface.class.getSimpleName())) {
                                Method parse = claz.getMethod("parse", JSONObject.class);
                                resultList.add(parse.invoke(claz.newInstance(), jsonObject));
                                hasFind = true;
                                break;
                            }
                        }
                        if (!hasFind) {
                            resultList.add(GsonUtil.parseJsonWithGson(jsonObject, claz));
                        }
                    } else {
                        resultList.add(GsonUtil.parseJsonWithGson(jsonObject, claz));
                    }

                }
                return (T) resultList;
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

public class BaseResponse<T> {
    public int state;
    public T data;
    public String msg;
}

  1. ParseInterface 该类就是被解析的实体类需要实现的接口
public interface ParseInterface<T>{
    T parse(JSONObject jsonObject);
}

Retrofit工具类的使用

  1. 在APIService中声明接口和参数

@Query @QueryMap : 用于Http Get请求传递参数
@Field : 用于Post方式传递参数,需要在接口方法上添加
@FormUrlEncoded,即以表单的方式传递参数
@Body : 用于Post,根据转换方式将实例对象转化为对应字符串传递参数,比如Retrofit添加GsonConverterFactory则是将body转化为gson字符串进行传递
@Path : 用于URL上占位符{}
@Part : 配合@Multipart使用,一般用于文件上传
@Header : 添加HttpHeader
@Headers : 跟@Header作用一样,只是使用方式不一样,@Header是作为请求方法的参数传入,@Headers是以固定方式直接添加到请求方法上

  String USER_INFO = "user/{user_id}/info";
  String USER_LIST = "user/{id}/{type}";

  @GET(USER_INFO)
  Call<User> userDetail(@Path("user_id") Integer userId, @QueryMap Map<String, String> map);

  @GET(USER_LIST)
  Call<List<User>> userList(@Path("id") Integer userId, @Path("type") String type, @Query("seq") String seq, @Query("length") Integer length, @Query("post_id") Integer postId);

  1. 将要被解析的实体类实现ParseInterface接口,并实现解析接口,例如:
	@Override
    public User parse(JSONObject jsonObject) {
        try {
            return ServerHandler.getUser(jsonObject, null, null, null);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return null;
    }
  1. 进行网络请求
Map<String, String> map = new HashMap<>();
        map.put("kind", "summary");
        Call<User> userCall = RetrofitClient.getInstance().getApiService().userDetail(ServerInterfaceHandler.getCurrentUserId(), map);
        userCall.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                LogUtils.d("thread_onResponse", Thread.currentThread().getName() + "-" + Thread.currentThread().getId());
                User user = response.body();
				//需要判断是否为空,因为不能正常解析的时候(只要不出异常也会回调onResponse,出异常则回调onFailure)这里会出现空指针的情况
                if(user != null) {
                    textView.setText(user.getName());
                }
            }

            @Override
            public void onFailure(Call<User> call, Throwable t) {
                ToastUtils.toastAbove(TestActivity.this, "请求失败");
            }
        });
  1. 可以对请求进行取消,调用userCall.cancel()方法,比如在界面销毁的时候就可以取消当前正在请求的接口。

  2. 对同一个接口再次请求userCall.clone().enqueue(…);

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值