关于Retrofit基础知识的学习,请参考这篇文章
代码的封装
-
APIService 使用Retrofit要求我们将项目中使用的接口请求定义写在一个接口中,咱们就写在APIService中。
-
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;
}
}
- 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());
}
}
- 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;
}
}
- 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);
}
}
- 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());
}
}
- 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;
}
- ParseInterface 该类就是被解析的实体类需要实现的接口
public interface ParseInterface<T>{
T parse(JSONObject jsonObject);
}
Retrofit工具类的使用
- 在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);
- 将要被解析的实体类实现ParseInterface接口,并实现解析接口,例如:
@Override
public User parse(JSONObject jsonObject) {
try {
return ServerHandler.getUser(jsonObject, null, null, null);
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
- 进行网络请求
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, "请求失败");
}
});
-
可以对请求进行取消,调用userCall.cancel()方法,比如在界面销毁的时候就可以取消当前正在请求的接口。
-
对同一个接口再次请求userCall.clone().enqueue(…);