Retrofit的使用与深入学习(上)

本文详细介绍了Retrofit网络请求库的使用方法及其实现原理,包括配置依赖、定义网络请求接口、创建Retrofit实例、执行网络请求等步骤,并深入探讨了Retrofit内部组件和服务方法的构建过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简单介绍

    Retrofit发送网络请求通过okHttp。相对于直接使用okhttp的好处在于,它支持对Response接收数据进行解析,支持RxJava。

    Retrofit和Volley一样,网络请求任务在背后线程中进行,返回结果的处理(或者说回调方法)在UI线程中执行。

    此外这里给出Retrofit2相对于Retrofit1的改进

  • 支持了在一个类型中的同步和异步,同时,一个请求也可以被真正地终止;Retrofit1之前是分开的,即需要定义两个方法
  • 每一个 call 对象实例只能被调用一次,request 和 response 都是一一对应的;若需多次重复请求,则建议每次请求前clone一个call对象
  • Response 对象增加了:响应码(the reponse code),响应消息(the response message),以及读取相应头(headers)
  • @Url ,允许你直接传入一个请求的 URL,定义如下方法
    • @GET
    • Call<List<Contributor>> repoContributorsPaginate( @Url String url);
  • 动态 URL Parameter
    • String links = response.headers().get("Link");
    • Call<List<Contributor>> nextCall =  gitHubService.repoContributorsPaginate(nextLink);

 

基本使用

一、引入依赖

compile 'com.google.code.gson:gson:2.6.2'  
compile 'com.squareup.retrofit2:retrofit:2.0.2'  
compile 'com.squareup.retrofit2:converter-gson:2.0.2'    
compile 'com.squareup.okhttp3:okhttp:3.2.0'  
一般使用Retrofit还会使用它和RxJava配套使用,因此还需要添加如下依赖  
compile 'io.reactivex:rxjava:1.1.3'  
compile 'io.reactivex:rxandroid:1.1.0'  
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'  
为了使用更多的java注解添加下面的依赖  
provided 'org.glassfish:javax.annotation:10.0-b28'  

二、定义网络业务接口

Retrofit的网络请求都是写在一个接口中,并使用一定的注释如下面一组请求接口:注意接口中的每个方法的参数都需要使用标注,不采用标注将会报错。

public interface MyApiEndpointInterface {  
    @GET("users/{username}") //note1  
    Call<User> getUser(@Path("username") String username,@Header("Cache-Control") int maxAge);  
  
    @Headers({"Cache-Control: max-age=640000", "User-Agent: My-App-Name"})//note2  
    @POST("users/new")  
    Call<User> createUser(@Body User user,@Query("sort") String sort);  //user支持被gson序列化的一个类,如JavaBean  
  
    @FormUrlEncoded  
    @POST("some/endpoint")  
    Call<SomeResponse> someEndpoint(@FieldMap Map<String, String> names);  
  
    @POST("https://blog.youkuaiyun.com/") //note3  
    Call<Response<User>> getUser(@Query("name") String name);  
  
    @Multipart  //note4  
    @POST("some/endpoint")  
    Call<Response> uploadImage(@Part("description") String description, @Part("image") RequestBody image)  
  
    @GET("users/{username}") //note5  
    void getUser(@Path("username") String username, Callback<User> cb);  
}  

1、Retrofit的网络请求返回的结果都是call<?>的形式,?代表希望对Response body解析得到的数据类型;如果想直接获得Responsebody中的内容,可以定义网络请求返回值为Call<ResponseBody>

2、Retrofit的注解有如下几种常见注解

    @GET 发送get方法的请求,@POST发送post方法的请求,@Header 网络请求的Header部分的键值对

    @Body Post请求报文的Body内容,内容往往是一个对Java Bean解析后得到的JSON String,@Path网络路径的缺省值,@Query网络请求地址后面的查询键值对,如www.baidu.com/s?wd=REST&rsv_spt=1.

    @FormUrlEncoded 和 @FieldMap配套使用,用于向Post表单传入键值对

3、一旦创建一个Retrofit实例就意味着网络请求根目录已经确定,但是Retrofit支持动态改变网络请求根目录。

4、使用@Multipart 标注的网络请求,用于上传文件;同时该请求必须在请求方法的参数中有一个使用@Part注解的参数。下面给出一个简单的使用例子

MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");  
file = new File("/storage/emulated/0/Pictures/MyApp/test.png");  
RequestBody requestBody = RequestBody.create(MEDIA_TYPE_PNG, file);  
Call<Response> call = apiService.uploadImage("test", requestBody);  

 5、用于发送一个异步请求,不过在Retrofit2中已经没有这个了,异步请求和同步请求已经被合并。即通过Call<?>的execute和queue方法分别发送同步和异步请求

 

三、得到网络业务接口的实体

public static final String BASE_URL = "http://api.myservice.com/";  
Retrofit retrofit = new Retrofit.Builder() //note1  
    .baseUrl(BASE_URL)  
    .addConverterFactory(GsonConverterFactory.create()) //gson负责对Response数据进行解析  
    .build();  
MyApiEndpointInterface apiService =  retrofit.create(MyApiEndpointInterface.class); //note2  

1、获得一个Retrofit对象

2、由retrofit得到一个网络请求接口的实现类

 

四、使用接口实体获取Response

Call<User> call = apiService.getUser("evan",5000);  
同步请求:  
Response<User> response = call.execute();  
异步请求:  
call.enqueue(new Callback<User>() {  
    @Override  
    public void onResponse(Call<User> call, Response<User> response) {  
       ....  
    }  
});  

call只能使用一次,调用第二次的时候,就会出现失败的错误。当你想要多次请求一个接口的时候,直接用 clone 的方法来生产一个新的,相同的可用对象,clone代价很低。

Retrofit2 中 Response 对象增加了曾经一直被我们忽略掉的重要元数据:响应码(the reponse code),响应消息(the response message),以及读取相应头(headers){cookies}。

五、关于授权

//note1  
Interceptor interceptor = new Interceptor() {  
  @Override  
  public okhttp3.Response intercept(Chain chain) throws IOException {  
    Request newRequest = chain.request().newBuilder().addHeader("User-Agent", "Retrofit-Sample-App").build();  
    return chain.proceed(newRequest);  
  }  
};  
//note2  
OkHttpClient.Builder builder = new OkHttpClient.Builder();  
builder.interceptors().add(interceptor);  
builder.cache(new Cache(new File("C:\\okhttp"),10*1024*1024)) ;  
OkHttpClient client = builder.build();  
//note3  
Retrofit retrofit = new Retrofit.Builder()  
  .baseUrl("https://api.github.com")  
  .addConverterFactory(GsonConverterFactory.create())  
  .client(client)  
  .build();  

1、创建拦截器,拦截器的工作就是在发送请求前在请求报文的header中添加一些键值对

2、添加前面的拦截器到client中,为了提高网络访问效率我们还为OkHttp设置了缓存,到此为止跟okhttp的操作都是一样的

3、为Retrofit设置个性化OkHttpClient对象

 

六、Retrofit和RxJava的配套使用

PartA 定义网络业务接口

public interface MyApiEndpointInterface {  
    @GET("/users/{username}")  
    Observable<User> getUser(@Path("username") String username);  
  
    @POST("/users/new")  
    Observable<User> createUser(@Body User user);  
}  

网络请求方法的返回值由之前的call<?> 转变成Observable<?>, ?类支持使用gson对其进行序列化

 

PartB 得到网络业务接口的实体

RxJavaCallAdapterFactory rxAdapter = RxJavaCallAdapterFactory.create(); //note1  
RxJavaCallAdapterFactory rxAdapter = RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()); //note2  
Retrofit retrofit = new Retrofit.Builder()  
                               .baseUrl("https://api.github.com")  
                               .addConverterFactory(GsonConverterFactory.create());  
                               .addCallAdapterFactory(rxAdapter)  
                               .build();  
MyApiEndpointInterface apiService =  retrofit.create(MyApiEndpointInterface.class); //note3  

1、默认发送同步的网络请求

2、默认发送异步的网络请求

3、获取网络请求实体

 

PartC 使用示例

String username = "sarahjean";  
Observable<User> call = apiService.getUser(username); //note1  
Subscription subscription = call //note2  
.subscribeOn(Schedulers.io())   
.observeOn(AndroidSchedulers.mainThread())  
.subscribe(new Subscriber<User>() {  
  @Override  
  public void onCompleted() {  
  }  
  @Override  
  public void onError(Throwable e) {  
    if (e instanceof HttpException) {  
       HttpException response = (HttpException)e;  
       int code = response.code();  
    }  
  }  
  @Override  
  public void onNext(User user) {  
  }  
});  

1、获取到Observable<User>之后的使用与RxJava一样的使用流程

2、这里要十分注意的是在Android的Activity和Fragment中使用Rxjava需要在对应的onDestroy方法中调用subscription.unsubscribe()方法,防止OOM

 

源码学习

在正式学习源码之前首先看看我们想要了解的内容有哪些?

  • Retrofit对象的创建需要哪些元素;该对象内部保存有哪些元素
  • Retrofit对象的create方法返回的对象和传进去的接口有何种关联
  • 业务接口使用各种注解对方法会产生何种影响?常用注解@GET、@POST、@MultiPart、@FieldMap等作用效果
  • Retrofit的ConverterFactory; CallAdapterFactory方法设置的Converter.Factory和CallAdapter.Factory对象何时何地起作用
  • Call<?> 对象及其execute和queue方法的内部逻辑
  • Observable<?>相对于RxJava有什么特殊的地方
  • Response<?>类型内部结构,有哪些数据可以访问

 

Retrofit.class

Retrofit类中存储有如下的对象

Fields

private final HttpUrl baseUrl; //网络请求基地址  
private final List<Converter.Factory> converterFactories; //数据转换器工厂集合  
private final List<CallAdapter.Factory> adapterFactories; //网络请求适配器工厂集合  
private final okhttp3.Call.Factory callFactory; //底层进行网络请求工厂  
private final Executor callbackExecutor; //回调方法执行器  
  
private final boolean validateEagerly; //是否提前对业务接口中的注解进行验证转换的标志位  
private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();  
 //ServiceMethod是对业务接口中方法的注解进行解析之后得到的对象,该对象包含了访问网络的除了方法参数值之外的所有必要信息;如数据转换器、网络请求适配器、网络请求工厂、基地址、Http方法等等。  

Retrofit()@Retrofit.class

Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,  
      List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,  
      Executor callbackExecutor, boolean validateEagerly) {  
    this.callFactory = callFactory;  
    this.baseUrl = baseUrl;  
    this.converterFactories = unmodifiableList(converterFactories); //note1  
    this.adapterFactories = unmodifiableList(adapterFactories);   
    this.callbackExecutor = callbackExecutor;  
    this.validateEagerly = validateEagerly;  
}  

1、unmodifiableList(list)方法近似于UnmodifiableList<E>(list);这样做的好处在于创建的新对象能够对list数据进行访问,但是不可通过该对象对list集合中的元素进行任何修改

Builder.class@Retrofit.class

该类中的域基本与Retrofit一样,但是多了一个如下的域

private Platform platform;

 

Builder()@Builder.class

public Builder() {  this(Platform.get()); } //note1  
Builder(Platform platform) {  
      this.platform = platform;  
      converterFactories.add(new BuiltInConverters()); //note2  
}  

1、如果是安卓系统则Platform.get()获取到的是一个如下的对象;也可以获得IOS、Java对应的类,很好很强大。

static class Android extends Platform {  
    @Override public Executor defaultCallbackExecutor() {  
      return new MainThreadExecutor();  //note3  
    }  
    @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {  
      return new ExecutorCallAdapterFactory(callbackExecutor); //note4  
    }  
    static class MainThreadExecutor implements Executor {  
      private final Handler handler = new Handler(Looper.getMainLooper()); //获取一个和Android UI线程绑定的Handler  
      @Override public void execute(Runnable r) {  handler.post(r);  } //异步任务在UI线程中执行  
    }  
}  

2、添加一个内置的转换器工厂到converterFactories中

3、返回一个默认的回调方法执行器,该执行器负责在指定的线程中执行回调方法,Android就是将回调方法在UI线程中执行

4、如果我们不准备将RxJava和Retrofit一起使用,一般都是使用的这个默认CallAdapter.Factory,因此我们在后面对ExecutorCallAdapterFactory.class源码进行解析,先不急着看等遇到了再去看。

所以到此为止Builder默认创建了Converter.Factory、CallAdapter.Factory,Executor三个系统默认的数据转换器工厂、网络请求适配器工厂、回调执行器。可能大伙儿有点陌生,这些个工厂究竟是干嘛的?特此先跟大伙儿说几句,本节的末尾还会详谈:

Converter.Factory

  • public Converter<ResponseBody, ?>  responseBodyConverter(Type type, Annotation[] annotations,  Retrofit retrofit) //对响应数据的解析
  • public Converter<?, RequestBody>    requestBodyConverter(Type type,  Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit)  //对请求数据的解析
  • public Converter<?, String>              stringConverter(Type type, Annotation[] annotations,  Retrofit retrofit) //普通对象转件String 如JSON

CallAdapter.Factory

  • public abstract CallAdapter<?>   get(Type returnType, Annotation[] annotations,  Retrofit retrofit) //获取网络请求适配器
  • protected static Type getParameterUpperBound(int index, ParameterizedType type) { return Utils.getParameterUpperBound(index, type); }
  • protected static Class<?> getRawType(Type type) { return Utils.getRawType(type); }

CallAdapter<?>

  • Type responseType();  //该请求适配器返回的数据类型
  • <R> T adapt(Call<R> call); //该请求适配器对原始Call的再次封装,如Call<R>到Observable<R>

 

Build()@Builder.class

public Retrofit build() {  
      if (baseUrl == null) {  throw new IllegalStateException("Base URL required.");} //note1  
        
      okhttp3.Call.Factory callFactory = this.callFactory; //note2  
      if (callFactory == null) { callFactory = new OkHttpClient(); }   
  
      Executor callbackExecutor = this.callbackExecutor; //note3  
      if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); }   
  
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); //note4  
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));  
  
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories); //note5  
      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly);   
    }  

1、基地址检测

2、网络请求执行器检测,若为空直接设置为OkHttpClient

3、回调方法执行器检测,若为空直接设置为Buidler构造器中创建的Executor

4、复制Builder中的List<CallAdapter.Factory>,并向该集合中添加Buidler构造器中创建的CallAdapter.Factory请求适配器,添加在集合器末尾

5、复制Builder中的List<Converter.Factory>,虽然该集合没有像前面第四步那样添加默认转换器,但是别忘了其实在Builder的构造器中已经向Builder中的List<Converter.Factory>集合的第一个位置插入了一个默认的转换器。

请求适配器工厂集合存储的是:自定义1适配器工厂、自定义2适配器工厂...默认适配器工厂

数据转换器工厂集合存储的是:默认数据转换器工厂、自定义1数据转换器工厂、自定义2数据转换器工厂....

create()@Retrofit.class

public <T> T create(final Class<T> service) {  
    Utils.validateServiceInterface(service); //note1  
    if (validateEagerly) {  
      eagerlyValidateMethods(service); //note2  
    }  
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, //note3  
        new InvocationHandler() {  
          private final Platform platform = Platform.get();  
          @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable {  
            if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); }  
            if (platform.isDefaultMethod(method)) {return platform.invokeDefaultMethod(method, service, proxy, args);}  
  
            ServiceMethod serviceMethod = loadServiceMethod(method); //note4  
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); //note5  
            return serviceMethod.callAdapter.adapt(okHttpCall); //note6  
          }  
        });  
}  

1、判断该参数Service是不是一个接口,接口中是否有定义方法;否则抛出异常

2、判断是否需要提前验证,方法内容就是给接口中的定义的每个方法的注解进行解析并得到一个ServiceMethod对象,并以Method为键将该对象存入LinkedHashMap集合中。如果不是提前验证则在第四步的时候会动态解析对应的方法,得到一个ServiceMethod对象,最后存入到LinkedHashMap集合中,有延迟加载的意思。默认都是延迟加载。eagerlyValidateMethods方法后面还有更为详细的介绍。

3、创建一个代理——java.lang.reflect.Proxy对象;Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler invocationHandler)方法近似于getProxyClass(loader, interfaces) .getConstructor(InvocationHandler.class).newInstance(invocationHandler);从字面就可以知道,通过这个代理类,调用interfaces接口的方法实际上是通过调用InvocationHandler类型对象的invoke方法来完成指定的功能。

4、如果代理类传进来的方法是一般方法则不会执行到这里,执行到这里表明调用的方法是我们使用了符合Retrofit规则的标注的方法。该行语句是从serviceMethodCache集合中获取一个对应的ServiceMethod对象。如果没有会临时创建一个ServiceMethod对象,再返回。loadServiceMethod方法后面也有更为详细的介绍。

5、利用上面得到的ServiceMethod对象和方法参数创建一个OkHttpCall对象,该对象相对于okhttp3.Call会在网络请求前后对数据利用该方法对应的数据转换器进行一定的转换,之后内部再通过okhttp3.Call发送请求。OkHttpCall.class的相关方法在后面也会给出详细的介绍。

6、将上面得到的OkHttpCall对象传给ServiceMethod中对应的网络请求适配器工厂的adapt方法中,返回的对象类型就是ServiceMethod对应的方法的返回值类型,如Call<?>;

 

eagerlyValidateMethods()@@Retrofit.class

private void eagerlyValidateMethods(Class<?> service) {  
    Platform platform = Platform.get();  
    for (Method method : service.getDeclaredMethods()) {  
      if (!platform.isDefaultMethod(method)) {  loadServiceMethod(method); } //note1  
    }  
}  

1、非平台默认方法则加载解析该方法,并将得到的ServiceMethod对象加入到LinkedHashMap<Method, ServiceMethod>集合中。使用LinkedHashMap该集合的好处就是lruEntries.values().iterator().next()获取到的是集合最不经常用到的元素,提供了一种Lru算法的实现。

loadServiceMethod()@Retrofit.class

ServiceMethod loadServiceMethod(Method method) {  
    ServiceMethod result;  
    synchronized (serviceMethodCache) {  
      result = serviceMethodCache.get(method); //note1  
      if (result == null) {  
        result = new ServiceMethod.Builder(this, method).build(); //note2  
        serviceMethodCache.put(method, result);  
      }  
    }  
    return result;  
}  

1、尝试从集合中获取该方法

2、方法在集合中不存在,创建一个ServiceMethod对象,以method为键存入集合中。往下我们看看如何构建一个ServiceMethod对象

 

ServiceMethod.class

final okhttp3.Call.Factory callFactory;   //方法使用的网络请求工厂  
final CallAdapter<?> callAdapter;  //方法使用的网络请求适配器  
private final Converter<ResponseBody, T> responseConverter; //方法的Response内容转换器  
  
private final HttpUrl baseUrl; //方法的基地址  
private final String relativeUrl; //方法的相对地址  
private final String httpMethod;   //方法的Http方法  
private final Headers headers;  //方法的http请求头 键值对  
private final MediaType contentType; //方法的http报文body的类型  
private final boolean hasBody;  //方法的http是否有bod  
private final boolean isFormEncoded;  
private final boolean isMultipart;  
private final ParameterHandler<?>[] parameterHandlers;  //队方法参数的处理器,用于解析采用了标注的参数  

ServiceMethod()@ServiceMethod.class

ServiceMethod(Builder<T> builder) {  
    this.callFactory = builder.retrofit.callFactory();  
    this.callAdapter = builder.callAdapter;   
    this.responseConverter = builder.responseConverter;   
  
    this.baseUrl = builder.retrofit.baseUrl();   
    this.relativeUrl = builder.relativeUrl;   
    this.httpMethod = builder.httpMethod;  
    this.headers = builder.headers;  
    this.contentType = builder.contentType; .  
    this.hasBody = builder.hasBody; y  
    this.isFormEncoded = builder.isFormEncoded;   
    this.isMultipart = builder.isMultipart;  
    this.parameterHandlers = builder.parameterHandlers;  
}  

该对象包含了访问网络的所有基本的信息。

内部类Builder<T>.class@ServiceMethod<T>.class

Fields

final Retrofit retrofit; //当前方法所属的Retrofit  
final Method method; //当前方法  
final Annotation[] methodAnnotations; //当前方法的注解  
final Annotation[][] parameterAnnotationsArray; //当前方法参数的注解  
ParameterHandler<?>[] parameterHandlers; //当前方法参数的处理器  
final Type[] parameterTypes; //当前方法参数的数据类型  
  
CallAdapter<?> callAdapter; //请求适配器  
Type responseType; //请求适配器的返回值类型  
Converter<ResponseBody, T> responseConverter; //网络请求内容转换器  
  
String httpMethod; //当前方法对应的http请求方法  
boolean hasBody; //当前请求是否有body  
String relativeUrl;  //当前方法的相对URL地址  
Set<String> relativeUrlParamNames; //当前方法的URL参数  
Headers headers; //当前请求的header键值对  
boolean isMultipart; //当前请求是否是Multipart  
boolean isFormEncoded; // 一般设置了Multipart标签不能使用FormEncoded标签,该标签的使用参考前面的使用说明  

Builder()@Builder.class

public Builder(Retrofit retrofit, Method method) {  
      this.retrofit = retrofit;  
      this.method = method;  
      this.methodAnnotations = method.getAnnotations(); //获取方法的注解  
      this.parameterTypes = method.getGenericParameterTypes(); //获取方法的参数类型  
      this.parameterAnnotationsArray = method.getParameterAnnotations(); //获取方法参数的注解  
}  

build()@Builder.class

public ServiceMethod build() {  
      callAdapter = createCallAdapter(); //note1  
      responseType = callAdapter.responseType(); //note2  
      if (responseType == Response.class || responseType == okhttp3.Response.class) { throw ...} //note3  
      responseConverter = createResponseConverter(); //note4  
  
      for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation);  }//note5  
      if (httpMethod == null) {  throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.)."); }  
        
·····if (!hasBody) {//note6  
        if (isMultipart) {throw methodError( "Multipart can only be specified on HTTP methods with request body (e.g., @POST)."); }   
        if (isFormEncoded) {throw methodError("FormUrlEncoded can only be specified on HTTP methods with "+ "request body (e.g., @POST)."); }   
      }  
  
      int parameterCount = parameterAnnotationsArray.length; //note7  
      parameterHandlers = new ParameterHandler<?>[parameterCount];  
      for (int p = 0; p < parameterCount; p++) {  
        Type parameterType = parameterTypes[p];  
        if (Utils.hasUnresolvableType(parameterType)) {throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",parameterType); }   
        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];  
        if (parameterAnnotations == null) {throw parameterError(p, "No Retrofit annotation found.");}  
        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); //note8  
      }  
  
      if (relativeUrl == null && !gotUrl) {throw methodError("Missing either @%s URL or @Url parameter.", httpMethod); } //note9  
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {throw methodError("Non-body HTTP method cannot contain @Body."); }  
      if (isFormEncoded && !gotField) { throw methodError("Form-encoded method must contain at least one @Field.");}  
      if (isMultipart && !gotPart) { throw methodError("Multipart method must contain at least one @Part.");}  
      return new ServiceMethod<>(this);  
}  

1、近似于 callAdapter = retrofit.callAdapter(returnType, annotations);即根据方法返回值类型和注释从retrofit中获取对应的网络请求适配器。callAdapter方法是从Retrofit的adapterFactories集合中从0开始遍历,找到第一个满足要求的网络请求适配器。

2、从前面的网络适配器(callAdapter)中获取该网络适配器返回的数据类型

3、返回数据类型不可以为retrofit2.Response.class和okhttp3.Response.class类型,Response<T>和Response是有区别的。 

4、近似于 responseConverter = retrofit.responseBodyConverter(responseType, annotations);即根据方法返回值类型和注释从retrofit中获取对应的转换器。responseBodyConverter方法是从Retrofit的converterFactories集合中从0开始遍历,找到第一个满足要求的内容转换器。

5、对方法中的DELETE、GET、POST、HEAD、PATCH、PUT、OPTIONS、HTTP、retrofit2.http.Headers、Multipart、FormUrlEncoded几个标注进行处理,大多数标签都会调用方法parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody),该方法对ServiceMethod中的httpMethod、hasBody、relativeUrl、relativeUrlParamNames域进行赋值。

6、请求报文中body中没有内容,但是使用了Multipart或者FormUrlEncode标注则抛出异常

7、当前方法中参数个数,为每个参数创建一个arameterHandler

8、为方法中的每个参数创建一个ParameterHandler<?>对象,该对象的创建过程就对方法参数中的Body、PartMap、Part、FieldMap、Field、Header、QueryMap、Query、Path、Url标注进行解析

9、后面就是一些判断了,最后创建ServiceMethod对象

该方法内容比较多我们再次梳理一下:

    首先根据返回值类型和方法标注从Retrofit的网络请求适配器工厂集合和内容转换器工厂集合中分别获取到该方法对应的网络请求适配器和Response内容转换器;根据方法的标注对ServiceMethod的域进行赋值;最后为每个方法的参数的标注进行解析,获得一个ParameterHandler<?>对象,该对象保存有一个Request内容转换器——根据参数的类型从Retrofit的内容转换器工厂集合中获取一个Request内容转换器或者一个String内容转换器。

 

往下我们接着对create()@Retrofit.class中OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);利用ServiceMethod和方法参数args创建的OkHttpCall类进行分析。

Call.class

首先简单看一下retrofit.Call接口的定义

Response<T> execute() throws IOException; //执行同步请求  
void enqueue(Callback<T> callback); //执行异步请求  
boolean isExecuted(); //当前请求是否执行完毕  
void cancel(); //取消当前请求  
boolean isCanceled(); //当前请求是否被取消  
Call<T> clone(); //clone一个当前线程  
Request request(); //返回当前请求的请求对象  

OkHttpCall.class

如果我们没有特别给Retrofit设置一个请求适配器工厂,则Retrofit使用默认的请求适配器工厂——ExecutorCallAdapterFactory.class,默认请求适配器工厂是存放在Retrofit的请求适配器工厂集合的末尾。通过ExecutorCallAdapterFactory得到Call<?> 对接收的网络请求没有经过任何特殊的处理直接交给OkHttpCall.class进行处理。证明分析见后面的ExecutorCallAdapterFactory.class源码部分。对于OkHttpCall类我们就分析下它的execute方法和enqueue方法。

final class OkHttpCall<T> implements Call<T> 

OkHttpCall()@OkHttpCall.class

Fields

private final ServiceMethod<T> serviceMethod; //构造器接收的参数  
private final Object[] args; //构造器接收的参数  
private okhttp3.Call rawCall; //实际进行网络访问的类  
private Throwable creationFailure; //几个状态标志位  
private boolean executed;  
private volatile boolean canceled;  
  
OkHttpCall(ServiceMethod<T> serviceMethod, Object[] args) {  
    this.serviceMethod = serviceMethod;  
    this.args = args;  
}  

execute()@OkHttpCall.class

@Override public Response<T> execute() throws IOException {  
    okhttp3.Call call;  
    synchronized (this) {  
      if (executed) throw new IllegalStateException("Already executed.");  
      executed = true;  
      if (creationFailure != null) {  throw...}   
      call = rawCall;  
      if (call == null) {  
        try { call = rawCall = createRawCall();  } //note1  
        catch (IOException | RuntimeException e) {  creationFailure = e; throw e; }   
      }  
    }  
    if (canceled) {  call.cancel(); }   
    return parseResponse(call.execute()); //note2  
  }  

1、创建一个okhttp3.Call请求

 Request request = serviceMethod.toRequest(args); 该方法会对使用对应的ParameterHandler进行解析,并利用ServiceMethod中存储的headers等数据构造一个okhttp请求

 return serviceMethod.callFactory.newCall(request);近似于OkHttpClient.newCall(request)

2、对请求结果解析,方法内部逻辑如下:

Response<T> parseResponse(okhttp3.Response rawResponse) {  
    ResponseBody rawBody = rawResponse.body();  
    int code = rawResponse.code();  
    if (code < 200 || code >= 300) { //响应执行失败  
    ResponseBody bufferedBody = Utils.buffer(rawBody);  
     return Response.error(bufferedBody, rawResponse);  
    }  
    if (code == 204 || code == 205) { //响应执行成功 但是没有返回数据body为空  
      return Response.success(null, rawResponse);  
    }  
    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);  
    T body = serviceMethod.toResponse(catchingBody); //近似于serviceMethod.responseConverter.convert(body);//即使用ServiceMethod对应的Response内容转换器对接收到的数据进行转换。  
    return Response.success(body, rawResponse); //近似于return new Response<>(rawResponse, body, null);  
}  

以上过程就是整个网络请求的过程,首先对方法中每个参数利用对应ParameterHandler进行解析,再加上ServiceMethod中存储的Headers等数据创建一个okhttp的Request;使用okhttp发送这个请求;再对接收到的请求利用ServiceMethod存储的内容转换器对响应内容进行转换,最终得到一个Response<T>对象。

 

enqueue()@OkHttpCall.class

@Override public void enqueue(final Callback<T> callback) {  
    if (callback == null) throw new NullPointerException("callback == null");  
    okhttp3.Call call;  
    Throwable failure;  
    synchronized (this) {  
      if (executed) throw new IllegalStateException("Already executed.");  
      executed = true;  
      call = rawCall;  
      failure = creationFailure;  
      if (call == null && failure == null) {  
        try { call = rawCall = createRawCall();}   
        catch (Throwable t) {failure = creationFailure = t;}  
      }  
    }  
    if (failure != null) { callback.onFailure(this, failure); return;}  
    if (canceled) {call.cancel();}  
    call.enqueue(new okhttp3.Callback() {  
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) throws IOException {  
        Response<T> response;  
        try {  
          response = parseResponse(rawResponse); //对响应数据进行解析  
        } catch (Throwable e) {  
          callFailure(e);  
          return;  
        }  
        callSuccess(response);  
      }//end of onResponse  
      @Override public void onFailure(okhttp3.Call call, IOException e) {  
        try { callback.onFailure(OkHttpCall.this, e);}   
        catch (Throwable t) {t.printStackTrace();}  
      }//end of onFailure  
      private void callFailure(Throwable e) {  
        try {callback.onFailure(OkHttpCall.this, e); }   
        catch (Throwable t) { t.printStackTrace();}  
      }  
      private void callSuccess(Response<T> response) {  
        try {callback.onResponse(OkHttpCall.this, response);}   
        catch (Throwable t) {t.printStackTrace();}  
      }  
    });  
}  

异步请求跟同步请求类似,也有创建okhttp请求,并对okhttp请求响应结果进行转换的过程;不同之处在于异步请求会将回调方法交给回调执行器在指定的线程中执行。对于okhttp的Call对象的enqueue、execute方法这里不再往下分析了,感兴趣的同学参考博客

 

到此为止,我们学习到的有:

一、通过Retrofit的Builder内部类如何创建一个Retrofit对象,通过buidler设置baseUrl和可选的Converter.Factory、CallAdapter.Factory。

二、调用Retrofit的create方法,根据create方法参数(接口类对象)构造一个代理,代理的执行实体是一个InnovationHandler对象。

三、调用接口的所有方法最终都是通过调用InnovationHandler的invoke方法,invoke方法接收的参数主要有Method和args,invoke方法内部逻辑为:

  1. 将Method对象转为一个ServiceMethod对象,构造过程会根据方法的标注从Retrofit的ArrayList<Converter.Factory> converterFactories和ArrayList<CallAdapter.Factory> adapterFactories集合中获取该方法对应的Converter<ResponBody,T> responseConverters和CallAdapter<?> callAdapter,同时根据方法参数的标注为每个参数创建一个ParameterHandler对象,该对象包含有一个Converter<?,RequestBody> requestConverter对象或者一个Converter<?,String> stringConverter对象;用于对每个参数进行转换处理。
  2. 利用前面的ServiceMethod对象和args创建一个OkHttpCall对象,该对象内部会包含一个okhttp3.Call对象,OkHttpCall会在okhttp3.Call对象进行网络访问前后分别利用对应的requestConverter和responseConverter对数据进行相应的转换。
  3. 通过ServiceMethod的callAdapter对象的adapter方法(参数OkHttpCall)创建一个本Method预期的返回值对象;
    • 如果在构造Retrofit的时候没有设置CallAdapter.Factory则都是返回的Call<?>对象,该对象的execute方法和enqueue方法实际上都是直接调用OkHttpCall的同名方法,返回一个Response<T>对象
    • 如果在构造Retrofit的时候设置了RxJavaCallAdapterFactory 那么可能返回对象可以定义为Observable<?> 类型,对于该类型我们的使用可以跟RxJava的使用一样,但是内部具体如何实现,我们还未曾介绍过。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值