Retrofit源码解析

作者: 一只小鸡仔 | 来源:发表于2016-10-10 15:58 被阅读0次

    如果你还没了解Retrofit如何使用,可以先查看这篇文章:Retrofit使用指南

    一般分析源码都习惯从使用开始分析,那么接下来先从实例化Retrofit开始分析。

    new Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    

    此处使用了builder设计模式实例化Retrofit,不管build()方法里面执行了什么,但最终还是需要调用Retrofit的构造函数的,那么我们看它的构造函数:

     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); // Defensive copy at call site.
       this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
       this.callbackExecutor = callbackExecutor;
       this.validateEagerly = validateEagerly;
     }
    

    先不关心这些参数到底有什么作用,再回顾一下我们是如何使用Retrofit请求接口的:

    创建一个接口,通过申明方法来请求接口,

    public interface TestService {    
        //定义一条接口请求
        @GET("users/yuhengye")
        Call<GitHubUser> getUserInfo();
    }
    

    调用请求接口:

    TestService testService = getRetrofit().create(TestService.class);
    Call<GitHubUser> call = testService.getUserInfo();
    call.enqueue(new Callback<String>() {
        @Override
        public void onResponse(Call<GitHubUser> call, Response<GitHubUser> response) {}
    
        @Override
        public void onFailure(Call<GitHubUser> call, Throwable t) {}
    });
    

    上面代码里是直接调用我们申明的接口方法testService.getUserInfo(),但是接口申明的方法肯定都是未实现的,可以直接调用的话证明我们一定是实现了这个接口,然而我们根本没有实现过TestService这个接口,我们是通过Retrofitcreate(TestService.class)方法得到实现列TestService接口的实例,那么看一下create()方法:

    public <T> T create(final Class<T> service) {
      //验证service是否是接口类型,并且没有继承任何接口
      Utils.validateServiceInterface(service);
      if (validateEagerly) {
        //提前加载TestService申明的方法
        eagerlyValidateMethods(service);
      }
      //通过动态代理创建TestService接口的实例
      return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
          new InvocationHandler() {
          //具体代码实现下文再分析
          });
    }
    

    如果想了解更多Java的动态代理知识的话可以去参考其他资料了解下,这里简单说明下调用过程,上面代码最后实例化了一个TestService的动态代理,可以理解为这个动态代理实现了TestService接口,每次调用接口里的方法并且返回值时,都会通过调用创建的InvocationHandlerinvoke()方法返回值,也就是说当我们调用TestServicegetUserInfo()得到的值是上面代码invoke()方法里return的值,那分析一下invoke()里发生了什么:

     new InvocationHandler() {
       private final Platform platform = Platform.get();
    
       @Override public Object invoke(Object proxy, Method method, Object... args)
           throws Throwable {
         // If the method is a method from Object then defer to normal invocation.
         // 如果申明该方法的类是Object, 则直接调用该方法
         if (method.getDeclaringClass() == Object.class) {
           return method.invoke(this, args);
         }
         // 如果方法是平台默认的方法,转移给具体平台实现类调用,Android的平台实现类没有默认的方法;
         if (platform.isDefaultMethod(method)) {
           return platform.invokeDefaultMethod(method, service, proxy, args);
         }
         // 加载接口的方法
         ServiceMethod serviceMethod = loadServiceMethod(method);
         OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
         // 通过CallAdapter的adapt()方法转换得到接口申明方法的值
         return serviceMethod.callAdapter.adapt(okHttpCall);
       }
     }
    

    首先开始调用了Platform.get()得到了当前使用的平台,实际是调用了这个方法:

     private static Platform findPlatform() {
       try {
         Class.forName("android.os.Build");
         if (Build.VERSION.SDK_INT != 0) {
           return new Android();
         }
       } catch (ClassNotFoundException ignored) {
       }
       try {
         Class.forName("java.util.Optional");
         return new Java8();
       } catch (ClassNotFoundException ignored) {
       }
       try {
         Class.forName("org.robovm.apple.foundation.NSObject");
         return new IOS();
       } catch (ClassNotFoundException ignored) {
       }
       return new Platform();
     }
    

    这段代码不难理解,通过Class.forName()判断出当前所使用的平台,一共有AndroidJava8IOS这3个平台,再看看Android平台继承类的具体实现:

     static class Android extends Platform {
       @Override public Executor defaultCallbackExecutor() {
         return new MainThreadExecutor();
       }
        
       @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
         return new ExecutorCallAdapterFactory(callbackExecutor);
       }
        
       static class MainThreadExecutor implements Executor {
         private final Handler handler = new Handler(Looper.getMainLooper());
        
         @Override public void execute(Runnable r) {
           handler.post(r);
         }
       }
     }
    

    只是重载类Platform的两个方法,不过可以看到里面申明了一个MainThreadExecutor类,里面有用到Android SDK的Handler实现在主线程执行Runnable,那么我们在什么时候需要用到这个东西呢,回想一下我们在使用Retrofit异步请求网络并且在回调里得到成功或是失败的结果,在这里肯定是需要在主线程执行回调的,不然使用起来还要自己切换主线程更新UI,那么到底是不是,我们下文通过源码验证。

    回到上文提到的InvocationHandler,当执行到if(platform.isDefaultMethod(method))时,因为Platform默认都返回false,只有Java8才会走这一步,此处略过,接下来到ServiceMethod serviceMethod = loadServiceMethod(method);

     ServiceMethod loadServiceMethod(Method method) {
       ServiceMethod result;
       synchronized (serviceMethodCache) {
       // 从Map缓存中获取方法对应的ServiceMethod,没有则实例化,并且放进缓存中
         result = serviceMethodCache.get(method);
         if (result == null) {
           result = new ServiceMethod.Builder(this, method).build();
           serviceMethodCache.put(method, result);
         }
       }
       return result;
     }
    

    此方法重点是实例化ServiceMethod,主要用到的参数是Retrofit和接口方法Method,Map缓存中把Method作为Key,证明一个接口方法对应着一个ServiceMethod,并且也是用到了builder去实例化,我们看一下build()发生了什么:

    public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
      }
      responseConverter = createResponseConverter();
    
      // 解析接口方法申明的注解
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
    
     /...此处是一些参数验证等,具体实现请查看源码.../
    
      return new ServiceMethod<>(this);
    }
    

    最终调用ServiceMethod的构造函数:

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

    以上变量大多从变量名字就可以猜测出其含义,当InvocationHandlerinvoke()方法通过loadServiceMethod(method)解析接口申明的方法后,最后会执行以下代码:

    OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
    return serviceMethod.callAdapter.adapt(okHttpCall);
    

    OkHttpCall的构造函数是这样的:

    final class OkHttpCall<T> implements Call<T> {
            
        private final ServiceMethod<T> serviceMethod;
        private final Object[] args;            
            
        OkHttpCall(ServiceMethod<T> serviceMethod, Object[] args) {
            this.serviceMethod = serviceMethod;
            this.args = args;
        }
        /...其余代码略过.../
    }
    

    先实例化一个OkHttpCall,最后通过调用ServiceMethod的成员变量callAdapteradapt方法得到我们在接口申明方法里定义的返回值,而OkHttpCall继承了RetrofitCall接口:

    public interface Call<T> extends Cloneable {
        //同步执行网络请求
        Response<T> execute() throws IOException;
        //异步执行网络请求
        void enqueue(Callback<T> callback);
        //是否已经执行过请求    
        boolean isExecuted();
        //取消请求    
        void cancel();
        //请求是否已经取消 
        boolean isCanceled();
        //复制一个请求 
        Call<T> clone();
        //返回okhttp3.Request 
        Request request();
    }
    

    因为Retrofit没有实现具体的网络请求,默认是使用OkHttp请求网络的,OkHttpCall内部是调用OkHttpCall去请求网络得到请求结果okhttp3.Response,再进一步包装成RetrofitResponse,如果是异步请求的话,再把okhttp3.Callback回调包装成RetrofitCallback

    OkHttpCall<T>的构造函数可以看出它的泛型是由它构造函数里的参数ServiceMethod<T>决定的,而后者的泛型又和它的成员变量private final Converter<ResponseBody, T> responseConverter;所对应的,而这个成员变量的赋值是发生在上文所提到的ServiceMethod.Builderbuild()方法里responseConverter = createResponseConverter();
    以下是createResponseConverter()函数的源码:

      private Converter<ResponseBody, T> createResponseConverter() {
         Annotation[] annotations = method.getAnnotations();
         try {
           return retrofit.responseBodyConverter(responseType, annotations);
         } catch (RuntimeException e) { // Wide exception range because factories are user code.
           throw methodError(e, "Unable to create converter for %s", responseType);
         }
    }
    

    retrofit.responseBodyConverter()最终又调用了RetrofitnextResponseBodyConverter

      public <T> Converter<ResponseBody, T> nextResponseBodyConverter(Converter.Factory skipPast,
          Type type, Annotation[] annotations) {
        checkNotNull(type, "type == null");
        checkNotNull(annotations, "annotations == null");
    
        int start = converterFactories.indexOf(skipPast) + 1;
        for (int i = start, count = converterFactories.size(); i < count; i++) {
          Converter<ResponseBody, ?> converter =
              converterFactories.get(i).responseBodyConverter(type, annotations, this);
          if (converter != null) {
            //noinspection unchecked
            return (Converter<ResponseBody, T>) converter;
          }
        }
    
        /...部分代码略过.../
      }
    

    以上代码就是遍历了一个converterFactories集合,如果符合条件就返回该Converter.Factory工厂生成的Converter<ResponseBody, T>,最后遍历完也找不到合适的转换器Converter就抛出异常,再看一下Converter是怎么定义的:

    public interface Converter<F, T> {
      /**
        * 把对象F转换成T对象返回;
        */
      T convert(F value) throws IOException;
    
      /** Creates {@link Converter} instances based on a type and target usage. */
      abstract class Factory {
      
        /**
         * 返回一个retrofit2.Converter对象, 这个对象可以把HTTP的ResponseBody转换成type参数所对应的类型,
         * 如果该工厂不能处理则返回空。这个方法用于创建响应类型的转换器, 类似Call<SimpleResponse>这样的申明,
         * 负责把ResponseBody类型转换成SimpleResponse类型;
         */
        public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
            Retrofit retrofit) {
          return null;
        }
    
        /**
         * 返回一个retrofit2.Converter对象, 这个对象可以把type参数所对应的类型转换成HTTP的RequestBody,
         * 如果该工厂不能处理则返回空。这个方法用于创建可以把Body、Part、PartMap等注解对应的值转换成RequestBody类型的转换器
         */
        public Converter<?, RequestBody> requestBodyConverter(Type type,
            Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
          return null;
        }
    
        /**
         * 返回一个retrofit2.Converter对象, 这个对象可以把type参数所对应的类型转换成String,
         * 如果该工厂不能处理则返回空。这个方法用于创建可以把Field、FieldMap、Header、HeaderMap、Path、
         * Query、QueryMap等注解对应的值转换成String类型的转换器
         */
        public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
            Retrofit retrofit) {
          return null;
        }
      }
    }
    

    那么这个ServiceMethod在得到匹配的responseConverter后肯定是在OkHttp请求成功后把它的结果ResponseBody交给转换器转换成相应的类型,怎么请求网络呢,看下OkHttpCallenqueue()execute()方法:

    final class OkHttpCall<T> implements Call<T> {
            
        private okhttp3.Call rawCall;
    
          @Override public void enqueue(final Callback<T> callback) {
            if (callback == null) throw new NullPointerException("callback == null");
        
            okhttp3.Call call;
            
            /...略过.../
            call = rawCall = createRawCall();
            /...略过.../
            
            call.enqueue(new okhttp3.Callback() {/...略过.../});
          }            
            
        @Override public Response<T> execute() throws IOException {
            okhttp3.Call call;
        
            /...略过.../
            call = rawCall = createRawCall();
            /...略过.../
            
            //把Http的请求的结果转成对应类型
            return parseResponse(call.execute());
        }
        
        private okhttp3.Call createRawCall() throws IOException {
            //通过serviceMethod配合接口方法的参数args得到OkHttp的Request;
            Request request = serviceMethod.toRequest(args);
            //serviceMethod的callFactory是由retrofit提供的,默认是OkHttpClient
            //这个call工厂负责把Request生成一个OkHttp的Call,用于操控网络请求
            okhttp3.Call call = serviceMethod.callFactory.newCall(request);
            if (call == null) {
             throw new NullPointerException("Call.Factory returned null.");
            }
            return call;
        }
        
        Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
            ResponseBody rawBody = rawResponse.body();
            
            /...略过.../
            
            ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
            try {
             T body = serviceMethod.toResponse(catchingBody);
             return Response.success(body, rawResponse);
            } catch (RuntimeException e) {
             // If the underlying source threw an exception, propagate that rather than indicating it was
             // a runtime exception.
             catchingBody.throwIfCaught();
             throw e;
            }
        }  
              
        /...其余代码略过.../
    }
    

    先是执行createRawCall()生成OkhttpCall,在OkHttp的网络请求执行完后,通过parseResponse()方法转换成对应类型,里面实际是调用了serviceMethod.toResponse()方法进行转换的:

      /** Builds a method return value from an HTTP response body. */
      T toResponse(ResponseBody body) throws IOException {
        return responseConverter.convert(body);
      }
    

    正是在实例化ServiceMethod的时候得到合适的转换器,每次请求成功后调用转换器的convert()方法把ResponseBody转换成对应的类型;

    既然我们可以正常使用接口,那么肯定是找到了合适的转换器Converter,而转换器又是由它的工厂集合遍历得到的,这个工厂集合converterFactoriesRetrofit的成员变量,在Retrofit.Builder类的构造函数初始化的:

    public static final class Builder {
    
        private List<Converter.Factory> converterFactories = new ArrayList<>();
    
        Builder(Platform platform) {
            this.platform = platform;
            // Add the built-in converter factory first. This prevents overriding its behavior but also
            // ensures correct behavior when using converters that consume all types.
            converterFactories.add(new BuiltInConverters());
        }
    
        public Builder() {
            this(Platform.get());
        }
        
        /...其余代码略过.../
    }
    

    可以看到已经提供了一个默认的转换器工厂实现类BuiltInConverters:

    final class BuiltInConverters extends Converter.Factory {
      @Override
      public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
          Retrofit retrofit) {
        if (type == ResponseBody.class) {
          if (Utils.isAnnotationPresent(annotations, Streaming.class)) {
            return StreamingResponseBodyConverter.INSTANCE;
          }
          return BufferingResponseBodyConverter.INSTANCE;
        }
        if (type == Void.class) {
          return VoidResponseBodyConverter.INSTANCE;
        }
        return null;
      }
    
      @Override
      public Converter<?, RequestBody> requestBodyConverter(Type type,
          Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        if (RequestBody.class.isAssignableFrom(Utils.getRawType(type))) {
          return RequestBodyConverter.INSTANCE;
        }
        return null;
      }
    
      @Override public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
          Retrofit retrofit) {
        if (type == String.class) {
          return StringConverter.INSTANCE;
        }
        return null;
      }
    }
    

    BuiltInConvertersresponseBodyConverter方法只支持RequestBodyVoid的类型转换,也就是说,我们在申明接口方法时,默认是只支持Call<RequestBody>Call<Void>这样的返回值;可是我们上文申明的却是Call<GitHubUser>,这是因为上文我们在实例化Retrofit的时候通过addConverterFactory(GsonConverterFactory.create())方法给Retrofit的转换器工厂集合增加了一个Gson转换器工厂,这个工厂会根据接口申明方法里返回值的泛型对应的Type生成一个GsonResponseBodyConverter转换器,把RequestBody类型转换成了GitHubUser类型:

    final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
      private final Gson gson;
      private final TypeAdapter<T> adapter;
    
      GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
      }
    
      @Override public T convert(ResponseBody value) throws IOException {
        JsonReader jsonReader = gson.newJsonReader(value.charStream());
        try {
          return adapter.read(jsonReader);
        } finally {
          value.close();
        }
      }
    }
    

    那么在上文创建的InvocationHandler类里的invoke()方法里所得到的OkHttpCall<T>实际上已经符合我们在接口方法里申明的返回值,像OkHttpCall<GitHubUser>这样,为什么在invoke()最后不直接返回OkHttpCall<GitHubUser>,而是调用return serviceMethod.callAdapter.adapt(okHttpCall);呢?先看一下adapt()方法的申明:

    public interface CallAdapter<T> {
    
        //将一个Call<R>实例转换成T;
        <R> T adapt(Call<R> call);
        
        /...其余代码略过.../
    }
    

    这个方法跟上文里的提到的转换器Converter很相似,不过这里是限定了必须把Call<R>转换成其他类型返回;

    在Android里必须在主线程更新UI,而OkHttpCall执行回调的时候没有进行线程切换,不同平台的默认实现也不一样,在Android平台里默认的CallAdapterFactory生成的CallAdapteradapt(okHttpCall)方法返回了一个ExecutorCallbackCall,也是继承了RetrofitCall接口:

    static final class ExecutorCallbackCall<T> implements Call<T> {
        final Executor callbackExecutor;
        final Call<T> delegate;
    
        ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
          this.callbackExecutor = callbackExecutor;
          this.delegate = delegate;
        }
        
        /...其余代码略过.../
    }
    

    实际上在实现Call接口的方法时,都是用成员变量delegate,也就是okHttpCall去实现,唯一不同的是在执行异步请求enqueue()方法时,在执行回调是是通过callbackExecutor去执行,而在Android平台里的默认的callbackExecutor对应是上文提到的MainThreadExecutor,通过Handler在主线程执行回调,也可以在实例化Retrofit的时候使用它的BuildercallbackExecutor(executor)方法使用自定义的Executor;然而callAdapter.adapt(okHttpCall)的作用还不仅仅是为了方便在主线程执行异步请求的回调,因为我们可以在实例化Retrofit的时候调用addCallAdapterFactory()方法增加自己的CallAdapter工厂,把Call<R>类型转换成你想申明的任何类型,比较典型的应用场景是结合RxJava,这样我们就可以在接口申明方法时这样申明:

    public interface TestService {
        @GET("users/yuhengye")
        Observable<GitHubUser> getUserInfo();
    }
    

    然后这样请求网络:

    RetrofitManager
                .getTestService()
                .getUserInfo()//返回一个Observable<GitHubUser>对象
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<GitHubUser>() {
                    @Override
                    public void onCompleted() {
                    }
                    @Override
                    public void onError(Throwable e) {
                    }
                    @Override
                    public void onNext(GitHubUser result) {
                    }
                });
    

    通过返回Observable<GitHubUser>对象请求网络后需要自己操纵线程的切换执行回调,对比一下文章开始用Call<GitHubUser>返回值去请求网络并没有什么优势,但是用RxJava可以很优雅、方便地实现合并多个网络请求最后统一执行回调、一条网络请求完成后紧接着执行下一条网络请求等需求,如果用最原始的Call<GitHubUser>去实现这些需求则要自己实现,并且可能会引起多重嵌套,有关Retrofit配合RxJava的使用后面会另外再写一篇文章;

    上文已经分析了Retrofit的实现原理,现在我们再总结一下Retrofit的构造函数参数所代表的含义:

    callFactory: 该工厂负责生成一个OkHttpCall,用于请求网络,一般是使用OkHttpClient;

    baseUrl: 接口前缀地址

    converterFactories: 转换工厂集合,负责把HTTP请求的参数(传给接口方法的参数)转换成RequestBody
    或者String,以及把HTTP请求成功后的结果ResponseBody
    转换成接口方法申明的对应泛型类型;

    adapterFactories:适配器工厂集合,负责把HTTP请求成功后的Call(实际上就是OkHttpCall)转换成自定义的类型;

    callbackExecutor:当接口方法申明的返回值类型是Call<T>这种类型时,由默认提供的平台适配器转换,并且在执行异步请求回调时使用callbackExecutor去执行回调,主要是为了方便线程切换;

    validateEagerly:如果该值为true,在使用Retrofitcreate()方法创建接口的实例时,会生成所有的ServiceMethod,如果为false,则在该接口方法第一次请求时才去创建;

    以上就是Retrofit的主要实现源码分析;
    最后总结一下在Retrofit里调用我们申明接口方法的主要流程:

    检查是否有该接口方法对应的ServiceMethod缓存,没有则生成一个加入缓存,并且解析接口方法的注解,以及方法参数的注解,生成对应的参数处理器用于处理我们传给方法的参数;以及创建合适的responseConverter,它可以把网络请求结果ResponseBody转换成该接口方法返回值所需要的类型,这个接口方法返回值所需要的类型又是由CallAdapter提供的;默认的CallAdapter是这样的处理的:如果接口方法返回类型是Call<String>,则这里所需要的类型是String;这样在调用接口方法的时候,实际是先生成一个OkHttpCall<String>,这个对象再经由合适的CallAdapteradapt()方法转换成在接口方法里所申明的返回值类型;

    相关文章

      网友评论

        本文标题:Retrofit源码解析

        本文链接:https://www.haomeiwen.com/subject/kinmyttx.html