Retrofit使用总结

  • Post author:
  • Post category:其他

#.整体介绍

    
Retrofit也是一个网络请求库,它是对OkHttp的进一步封装。
    用OkHttp做请求时需要我们自己设置各种请求参数,并且对结果做解析。
   
Retrofit的
网络请求工作实际上还是由OkHttp来完成,

Retrofit针对OkHttp的输入和输出过程做处理
,简化了这两个过程的代码编写,提供了强大的功能支持。

1.Retrofit封装了上层的网络请求接口,帮助开发者简化OkHttp的Request封装。

    可以使用Retrofit提供的一堆注解,来编写网络请求接口类,然后Retrofit会解析这个接口类,调用相关接口时,
Retrofit内部会生成对应的Http请求数据体,并最终调用OkHttp来发起网络请求。      

2.Retrofit提供了数据转换器和适配器功能,当从OkHttp获取到网络请求结果Response后会做进一步地各种处理

    定义网络接口的返回类型时,需要与
数据转换器、适配器要相结合。

#.使用过程简介

1.使用注解编写接口类,示例:

public interface TestHttpService2 {
    @GET("weather/xxx/xxx")
    Call<String> testGet1(@Query("params1") int arg1, @Query("params2") String arg2);
}

2.创建Retrofit实例,解析接口类获取接口对象。示例:

 

​
    //创建OkHttpClient实例
    OkHttpClient.Builder builder = new OkHttpClient.Builder();
    builder.connectTimeout(5, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.SECONDS)
            
            .writeTimeout(10, TimeUnit.SECONDS);
    mOkHttpClient = builder.build();
    //创建Retrofit实例
    Retrofit.Builder builder = new Retrofit.Builder();
        //配置URL基地址,Retrofit2 的baseUrl必须以 /(斜线)结束,否则会抛出错误
        //这里的baseUrl加上接口类中每个方法注解后面的路径,才是一个接口的完整url地址
    builder.baseUrl(“Ten▪Api - Tenapi.cn”)
        .client(mOkHttpClient)//配置OkHttp实例
        //配置使用的适配器
        .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
        //配置使用的数据转化器
        .addConverterFactory(new HttpConverterFactory());
    mRetrofit = builder.build();
    //解析接口类,获取代理对象
    mHttpService = mRetrofit.create(TestHttpService.class);
}
​

3.调用代理对象的接口,获取到Call对象(或者Call的封装对象,要看选用的是什么适配器);利用Call(或其它对象)触发网络请求,获取结果数据并做需要的处理。

//调用方法获取Call对象或者Call对象的封装对象
Call<String> call = mHttpService.testGet1(3, "xxx");
//触发Retrofit发起网络请求,并获取Retrofit处理后的请求结果
call.enqueue(new Callback<String>() {
    @Override
    public void onResponse(Call<String> call, Response<String> response) {
        //....做相应处理....
    }

    @Override
    public void onFailure(Call<String> call, Throwable t) {
        //....做相应处理....
    }
});
(整体流程示意图,图片来自网络)

#.一、使用注解编写接口类

    使用Retrofit提供的注解来编写接口类,Retrofit能够解析使用注解编写的接口类,从而大大简化了相关功能开发。

##.各种注解

主要分为4类,分别用于标注请求方式、标记请求头、标记请求参数、标记请求和响应格式。

1. 请求方法注解

注解类型
说明
@GET
get请求
@POST
post请求
@PUT 
put请求
@DELETE
delete请求
@PATCH
用于更新局部资源,是PUT请求的补充
@HEAD 
head请求
@OPTIONS
options请求
@HTTP
该注解可以替换以上所有的注解,它拥有三个属性:method、path、hasBody

2. 请求头注解

请求头注解
说明
@Headers
用于添加固定请求头,可以同时添加多个,通过该注解的请求头不会相互覆盖,而是共同存在
@Header
作为方法的参数传入,用于添加不固定的header,它会更新已有请求头

3.请求参数注解

基本上可分为两大类,用于GET请求的和用于POST请求的。
注解类型
说明
1.Get请求相关
@Query 
用于Get请求,对应于一个参数
示例:
@Query(“params1”) int arg1
以上”params1″、int分别指明了参数名(服务端约定的)、参数类型
@QueryMap
用于Get请求,对应一个参数Map,当传递的参数不确定时,很有用
示例:
@QueryMap Map<String, Object> paramMap
传入的map的key值将是Http请求报文中的参数名
2.Post请求相关
2.1数据字段
@Filed 
用于Post请求,对应单个参数
注意:需要配合@FromUrlEncoded使用,标明请求的数据格式是编码表单数据
示例:
@Field(“params1”) int arg1
@FiledMap
用于Post请求,需要配合
配合@FromUrlEncoded使用,效果与上面@QueryMap类似。
2.2复合类型数据上传,一般用于上传文件,当然也能用于上传其它数据
@Part
用于表单字段,Part和PartMap与@multipart注解结合使用,适合文件上传的情况
@PartMap 
用于表单字段,默认接受类型是Map<String,RequestBody>,可用于实现多文件上传
2.3自定义类型
@Body 
使用Post请求发送自定义的数据类型对象。会根据转换方式将该对象转化对应的数据,例如添加GsonConverterFactory可将body转化为json字符串进行传递
3.其它字段
@Path
用于Url中的占位符
@Url
表明请求Url通过参数形式传入,此时@Get/@Post后面不能指定Url地址。

4.请求报文和响应报文数据体类型注解

@FromUrlCoded、@Multipart最终会影响Http请求报文中的
Content-Type字段。
@Streaming不会影响Http报文字段,只是在Retrofit内部用于标注相应数据以字节流形式返回。
注解类型
说明
@FromUrlCoded
表明为编码表单数据,每个键值对需要使用@Filed注解,或者直接使用@FiledMap
@Multipart
表明为多种类型数据的混合表单,每部分数据都需要使用@Part,或者直接使用@PartMap
@Streaming
表示响应数据用字节流的形式返回。
注意:下载大文件时,应该使用该注解。
     因为未使用该注解时,Retrofit默认会把数据全部加载入内存中,再供后继处理,占用内存过多时会出现OOM问题。
     使用@Streaming注解时,响应数据会以字节流返回,不用全部同时加载入内存。可通过ResponseBody.byteStream()获取到数据的读取流,读取数据并进行处理。

##.示例

/**
* GET请求示例
*/
@GET("xxx/xxx/xxx")
Call<String> testGet1(@Query("params1") int arg1, @Query("params2") String arg2);

@GET("xxx/xxx/xxx")
Call<String> testGet2(@QueryMap Map<String, Object> paramMap);

@GET("xxx/xxx/xxx")
Flowable<String> testGet3(@Query("params1") int arg1, @Query("params2") String arg2);

/**
* POST请求示例 :@FormUrlEncoded
*/
@FormUrlEncoded
@POST("xxx/xxx/xxx")
Call<String> testPost1(@Field("params1") int arg1, @Field("params2") String arg2);

/**
* POST请求示例 :@Multipart
* 多用于文件上传,这里的RequestBody是OkHttp中的请求数据体封装对象
*/
@Multipart
@POST("xxx/xxx/xxx")
Call<String> testPost2(@Part("file1") RequestBody part1, @Part("file2") RequestBody part2);

/**
* .@Streaming示例,多用于下载比较大的文件
*/
@Streaming
@GET("xxx/xxx/xxx")
Call<ResponseBody> testMethod1(@Query("params1") int arg1, @Query("params2") String arg2);

/**
* .@Url示例
*/
@FormUrlEncoded
@POST
Call<String> testMethod2(@Url String url, @Field("params1") int arg1, @Field("params2") String arg2);

/**
* .@Path @HTTP示例
*/
@HTTP(method = "GET", path = "xxx/{userId}/xxx", hasBody = false)
Call<ResponseBody> testMethod3(@Path("userId") String user);

/**
* .@Body示例
*/
@POST("xxxx/xxxxx")
Call<ResponseBody> testMethod4(@Body TestBody body);

/**
* @Header @Headers示例
*/
@GET("user")
Call<ResponseBody> testMethod5(@Header("Authorization") String authorization);

@Headers({
        "Accept: xxxxxxxx",
        "User-Agent: xxxxx"
})
@GET("xxx/xxxxxxxx")
Call<ResponseBody> testMethod6(@Query("params1") int arg1, @Query("params2") String arg2);

#.二、流程分析

1.接口类解析与方法调用返回结果

接口类的解析是
通过动态代理模式(内部会使用Java反射)进行的,会生成对应的委托对象返回给上层。 
我们外部持有的对象,就是这个委托对象。
    //解析接口类,获取代理对象
    mHttpService = mRetrofit.create(TestHttpService.class);
对应的源代码:

public <T> T create(final Class<T> service) {
  validateServiceInterface(service);
  return (T)
      Proxy.newProxyInstance(
          service.getClassLoader(),
          new Class<?>[] {service},
          new InvocationHandler() {
            private final Platform platform = Platform.get();
            private final Object[] emptyArgs = new Object[0];

            @Override
            public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                throws Throwable {
              // If the method is a method from Object then defer to normal invocation.
              if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
              }
              args = args != null ? args : emptyArgs;
              return platform.isDefaultMethod(method)
                  ? platform.invokeDefaultMethod(method, service, proxy, args)
                  : loadServiceMethod(method).invoke(args);
            }
          });
}
    当委托对象调用相应接口时(例如我们调用
mHttpService.testGet1(
3
,
“xxx”)),会执行动态代理中InvocationHandler()的invoke()回调方法,最终会对这个接口的相关注解和参数做解析。每个接口解析后的结果都是一个
HttpServiceMethod对象,调用委托对象的方法时,最终会执行到HttpServiceMethod的invoke(args)方法。
    以上每个接口的注解和参数解析结果解析成HttpServiceMethod对象的过程只会执行一次,因为解析完成后就会被Map缓存下来,下次直接通过调用的接口查Map就能查到对应的HttpServiceMethod对象。
@Override
final @Nullable ReturnT invoke(Object[] args) {
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  return adapt(call, args);
}
    方法调用最终返回的结果由上面adapt(call, args)来生成的,而这个方法的执行过程取决于Retrofit选用的适配器。但无论用哪种适配器,最终的返回结果都是对OkHttpCall进一步封装后的对象。例如默认情况下的适配器工厂类是DefaultCallAdapterFactory,最终返回的是ExecutorCallbackCall;当使用RxJava,选用RxJava3CallAdapterFactory时,会根据定义类型返回Observable/Flowable等。
(注:OkHttpCall、ExecutorCallbackCall都继承自Call接口)

2.触发网络请求时的整体过程总结

    OkHttpCall对象对应Retrofit一次网络请求任务,它可视为Retrofit对OkHttp架构中Call的进一步封装,在触发Retrofit网络请求时,通过分析源码,过程可总结如下:
1).使用适配器生成的封装对象(内部封装了OkHttpCall) :ExecutorCallbackCall/Observable/Flowable等,来触发整个过程
2).      ——> 触发Retrofit 中OkHttpCall的网络请求过程
3).              ——> 发起OkHttp库的网络请求(使用OkHttp库中的Call对象)
4).      ——>OkHttpCall对OkHttp库的网络请求结果,使用数据转换器(Converter)转换为定义的目标类型
5).——>适配器封装对象得到前面处理后的结果,根据适配器内部逻辑做后继处理:线程切换以及回调等。(例如ExecutorCallbackCall可用回调来返回结果,而Flowable等会按照RxJava中的流程将结果数据以事件流发送给观察者。)
  

#.三、适配器分析(Adapter)

适配器由适配器工厂生成,转换器由转换器工厂生成,Retrofit实例内部维护着一个适配器工厂和转换器工厂的列表:
private final List<Converter.Factory> converterFactories = new ArrayList<>();
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
    在用注解定义接口时,接口的返回类型与泛型内的数据类型,会影响最终选用的适配器、解析器。Retrofit会自动解析,根据
接口的返回类型、泛型类型来选用合适的工厂类生成
适配器、转换器,但如果找不到
对应的
适配器、数据转换器
,会出错。所以,定义接口时,需要注意对应的适配器工厂、转换器工厂是否已添加。    
​
    //创建Retrofit实例
    Retrofit.Builder builder = new Retrofit.Builder()
    builder.baseUrl(“Ten▪Api - Tenapi.cn”)
        .client(mOkHttpClient)
        //配置使用的适配器
        .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
        //配置使用的数据转化器
        .addConverterFactory(new HttpConverterFactory());
    mRetrofit = builder.build();
​
    适配器可以在获取网络请求结果后,进行线程切换等后继流程处理。
    适配器由适配器工厂类构造,而适配器工厂类在构建Retrofit实例时指定,可自定义(继承
CallAdapter.Factory
),也可选用已有的适配器工厂类。Android中常用的有两种,
默认的适配器工厂类:DefaultCallAdapterFactory
RxJava对应的适配器工厂类:RxJava3CallAdapterFactory
 (目前最新的rxjava是rxjava3,如果是rxjava2的话,对应的是RxJava2CallAdapterFactory)

1.Android平台默认的适配器工厂类DefaultCallAdapterFactory

例如定义以下接口,若使用默认适配器工厂,则此时返回的Call的具体实现是ExecutorCallbackCall。
    @GET("weather/xxx/xxx")
    Call<String> testGet1(@Query("params1") int arg1, @Query("params2") String arg2);

    ExecutorCallbackCall也可以发起同步请求和异步请求,也是只能执行一次,这些都类似于OkHttp中的Call的特性。

    ExecutorCallbackCall发起异步请求和同步请求的源代码如下,最终调用外部传入的回调接口,将结果传递出去:

@Override
public void enqueue(final Callback<T> callback) {
  Objects.requireNonNull(callback, "callback == null");

  delegate.enqueue(
      new Callback<T>() {
        @Override
        public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(
              () -> {
                if (delegate.isCanceled()) {
                  // Emulate OkHttp's behavior of throwing/delivering an IOException on
                  // cancellation.
                  callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                } else {
                  callback.onResponse(ExecutorCallbackCall.this, response);
                }
              });
        }

        @Override
        public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
        }
      });
}

@Override
public Response<T> execute() throws IOException {
  return delegate.execute();
}
注意:最终通过callbackExecutor来调用回调,而callbackExecutor默认为MainThreadExecutor类型(源代码如下),所以默认情况下,回调会执行在主线程中。
static final class MainThreadExecutor implements Executor {
  private final Handler handler = new Handler(Looper.getMainLooper());

  @Override
  public void execute(Runnable r) {
    handler.post(r);
  }
}

2.RxJava对应的适配器工厂类:RxJava3CallAdapterFactory

    这是Retrofit比较常见的使用形式,使用RxJava最大的好处就是简化了线程切换的过程,使复杂的逻辑编写起来比较简单,而且代码也相对简介。
封装接口示例:
@GET("xxx/xxx/xxx")
Flowable<String> testGet3(@Query("params1") int arg1, @Query("params2") String arg2);

@FormUrlEncoded
@POST("xxx/xxx/xxx")
Observable<String> testPost1(@Field("params1") int arg1, @Field("params2") String arg2);
此时返回的适配器类型是
RxJava3CallAdapter,关键的源代码:
@Override
public Object adapt(Call<R> call) {
  Observable<Response<R>> responseObservable =
      isAsync ? new CallEnqueueObservable<>(call) : new CallExecuteObservable<>(call);

  Observable<?> observable;
  if (isResult) {
    observable = new ResultObservable<>(responseObservable);
  } else if (isBody) {
    observable = new BodyObservable<>(responseObservable);
  } else {
    observable = responseObservable;
  }

  if (scheduler != null) {
    observable = observable.subscribeOn(scheduler);
  }

  if (isFlowable) {
    return observable.toFlowable(BackpressureStrategy.LATEST);
  }
  if (isSingle) {
    return observable.singleOrError();
  }
  if (isMaybe) {
    return observable.singleElement();
  }
  if (isCompletable) {
    return observable.ignoreElements();
  }
  return RxJavaPlugins.onAssembly(observable);
}
    它相关的整个逻辑过程比较复杂,
当返回泛型类型不是Response时,内部还会封装一层
Observable,会有两层被观察者-观察者,内部那层观察者先接收到请求结果,进一步处理后,才会交给外部观察者处理。
    简单概括,就是会根据很多情况来决定最终封装返回的对象类型,无论如何,都是对OkHttpCall的进一步封装。
    以下面返回
Flowable时为例,当为其指定观察者
observer.
时,最终会触发内部用OkHttpCall发起请求,获取请求结果后数据转化器将结果处理成接口定义的数据类型T,最终内部逻辑会会调用到
observer.onNext(T)、
observer.onComplete()等,将
结果T以事件形式发送给观察者。
Flowable<String> flowable = mHttpService.testGet3(2, "aa");
flowable.subscribeOn(Schedulers.io())//设置被观察者的执行线程
        .observeOn(AndroidSchedulers.mainThread())//设置观察者处理过程的执行线程
        .subscribe(new FlowableSubscriber<String>() {
            @Override
            public void onSubscribe(@NonNull Subscription s) {

            }

            //因为接口定义的返回数据泛型是String,
            //  所以在网络结果返回时,Retrofit会使用对应转化器把结果转化成String
            @Override
            public void onNext(String s) {
                //...对结果数据做处理...
            }

            @Override
            public void onError(Throwable t) {

            }

            @Override
            public void onComplete() {

            }
        });

#.四、数据转换器(Converter)

     Retrofit会根据接口返回值定义的泛型类型来选取对应的数据转换器,在OkHttpCall获取到结果数据后,会使用转化器对其做处理,处理成接口返回处定义的泛型类型。

     例如返回类型定义为Flowable<String>,然后存在支持返回泛型类型为String的数据转换器,则会选用该转换器将结果转化为String,最终提供给调用方使用。

1.默认的数据转换器工厂类BuiltInConverters

    数据转化器由数据转化器工厂创建,默认的数据转化器工厂为BuiltInConverters。
主要能处理泛型类型为ResponseBody、无返回结果(void)等情况,如果需要更复杂的情况处理,需要添加自定义数据转化器工厂,或者添加其它已有工厂类,如GsonConverterFactory等(需引入对应的库)。
    BuiltInConverters关键源代码:
@Override
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
    Type type, Annotation[] annotations, Retrofit retrofit) {
  if (type == ResponseBody.class) {
    return Utils.isAnnotationPresent(annotations, Streaming.class)
        ? StreamingResponseBodyConverter.INSTANCE
        : BufferingResponseBodyConverter.INSTANCE;
  }
  if (type == Void.class) {
    return VoidResponseBodyConverter.INSTANCE;
  }
  if (checkForKotlinUnit) {
    try {
      if (type == Unit.class) {
        return UnitResponseBodyConverter.INSTANCE;
      }
    } catch (NoClassDefFoundError ignored) {
      checkForKotlinUnit = false;
    }
  }
  return null;
}

2.参考默认转化器工厂类,根据需求自定义转化器工厂类

public class HttpConverterFactory extends Converter.Factory {

    public static HttpConverterFactory create() {
        return new HttpConverterFactory();
    }

    /**
     * 获取结果数据的转换器,
     * OkHttpCall在获取到结果数据后,会使用转化器对其做处理,处理成接口返回处定义的泛型类型
     * @return
     */
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
                                                            Retrofit retrofit) {
        //根据返回的泛型类型type,来判断选择用哪种转换器
        if (type == ResponseBody.class) {
            return isAnnotationPresent(annotations, Streaming.class)
                    ? StreamingResponseBodyConverter.INSTANCE
                    : BufferingResponseBodyConverter.INSTANCE;
        }
        if (type == Void.class) {
            return VoidResponseBodyConverter.INSTANCE;
        }
        if (type == String.class) {
            return StringResponseConverter.INSTANCE;
        }
        if (type == HttpResult.class) {
            return HttpResultConverter.INSTANCE;
        }
        return null;
    }

    /**
     * 获取请求数据体的转换器
     * 可以对请求体进行处理,在解析接口时,会调用到该方法
     * @return
     */
    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type,
                                                          Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        return RequestBodyConverter.INSTANCE;
    }

    @Override
    public Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        return ToStringConverter.INSTANCE;
    }

    private static final class VoidResponseBodyConverter implements Converter<ResponseBody, Void> {
        static final VoidResponseBodyConverter INSTANCE = new VoidResponseBodyConverter();

        @Override
        public Void convert(ResponseBody value) throws IOException {
            value.close();
            return null;
        }
    }

    private static final class RequestBodyConverter implements Converter<RequestBody, RequestBody> {
        static final RequestBodyConverter INSTANCE = new RequestBodyConverter();

        @Override
        public RequestBody convert(RequestBody value) throws IOException {
            //这里对请求数据没有做任何处理
            return value;
        }
    }

    private static final class StreamingResponseBodyConverter
            implements Converter<ResponseBody, ResponseBody> {
        static final StreamingResponseBodyConverter INSTANCE = new StreamingResponseBodyConverter();

        @Override
        public ResponseBody convert(ResponseBody value) throws IOException {
            return value;
        }
    }

    private static final class BufferingResponseBodyConverter
            implements Converter<ResponseBody, ResponseBody> {
        static final BufferingResponseBodyConverter INSTANCE = new BufferingResponseBodyConverter();

        @Override
        public ResponseBody convert(ResponseBody value) throws IOException {
            try {
                // Buffer the entire body to avoid future I/O.
                Buffer buffer = new Buffer();
                value.source().readAll(buffer);
                return ResponseBody.create(value.contentType(), value.contentLength(), buffer);
            } finally {
                value.close();
            }
        }
    }

    private static final class StringResponseConverter implements Converter<ResponseBody, String> {
        static final StringResponseConverter INSTANCE = new StringResponseConverter();

        @Override
        public String convert(ResponseBody value) {
            try {
                // Buffer the entire body to avoid future I/O.
                return value.string();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                value.close();
            }

            return null;
        }
    }

    private static final class ToStringConverter implements Converter<Object, String> {
        static final ToStringConverter INSTANCE = new ToStringConverter();

        @Override
        public String convert(Object value) {
            return value.toString();
        }
    }

    private boolean isAnnotationPresent(Annotation[] annotations,
                                Class<? extends Annotation> cls) {
        for (Annotation annotation : annotations) {
            if (cls.isInstance(annotation)) {
                return true;
            }
        }
        return false;
    }
}

(声明:部分图片来自网络,侵删!)


版权声明:本文为u013914309原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。