Retrofit

作者: WuXiao_ | 来源:发表于2016-07-06 23:29 被阅读1139次

    以下都是本人收集和总结的内容:

    1. 什么是Retrofit

    Retrofit是一个 RESTful 的 HTTP 网络请求框架的封装。注意这里并没有说它是网络请求框架,主要原因在于网络请求的工作并不是 Retrofit来完成的。Retrofit2.0 开始内置 OkHttp


    640.png

    我们的应用程序通过Retrofit请求网络,实际上是使用Retrofit接口层封装请求参数、Header、Url 等信息,之后由OkHttp完成后续的请求操作,在服务端返回数据之后,OkHttp将原始的结果交给Retrofit,后者根据用户的需求对结果进行解析的过程。讲到这里,你就会发现所谓Retrofit,其实就是 Retrofitting OkHttp 了。

    2. 如何实现Retrofit

    2.1.定义请求接口

    public interface GitHubService {
      @GET("/users/{user}/repos") 
      List<Repo> listRepos(@Path("user") String user);
    }
    

    接口当中的 listRepos 方法,就是我们想要访问的 api 了, 如下:

    https://api.github.com/users/{user}/repos

    2.2.通过retrofit生成一个刚才定义接口的实现类

    Retrofit retrofit = new Retrofit.Builder()    
    .baseUrl("https://api.github.com/")    
    .build();
    GitHubService service = retrofit.create(GitHubService.class);
    

    2.3.通过接口直接进行请求

    其中,在发起请求时, {user} 会被替换为方法的第一个参数 octocat.

    Call<List<Repo>> repos= service.listRepos("octocat");
    

    发请求的代码就像前面这一句,返回的 repos其实并不是真正的数据结果,它更像一条指令,你可以在合适的时机去执行它:

    public interface GitHubService {    
    @GET("users/{user}/repos")  
         Call<List<Repo>> listRepos(@Path("user") String user);
    }
    
    
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();
    GitHubService service = retrofit.create(GitHubService.class);
    
    //执行
    Call<List<Repo>> repos = service.listRepos("octocat");
    List<Repo> data = repos.execute();
    

    2.4.参数的配置

    发请求时,需要传入参数,Retrofit 通过注解的形式令 Http 请求的参数变得更加直接,而且类型安全。
    2.4.1 Query & QueryMap

    @GET("/list")
    Call<ResponseBody> list(@Query("page") int page);
    

    Query 其实就是 Url 中 ‘?’ 后面的 key-value,比如:

    http://www.baidu.com/?tn=56060048_4_pg

    这里的tn=56060048_4_pg 就是一个 Query,而我们在配置它的时候只需要在接口方法中增加一个参数

    这时候你肯定想,如果我有很多个 Query,多参数的版本的 QueryMap 横空出世了,使用方法如下

    @GET("/list")
    Call<ResponseBody> list(@QueryMap Map<String, String> options);
    

    2.4.2 Field & FieldMap
    其实我们用 POST 的场景相对较多,绝大多数的服务端接口都需要做加密、鉴权和校验,GET显然不能很好的满足这个需求。使用 POST 提交表单的场景就更是刚需了,怎么提呢?

      @FormUrlEncoded  
      @POST("/")     
      Call<ResponseBody> example( @Field("name") String name, 
                                  @Field("occupation") String occupation);
    

    其实也很简单,我们只需要定义上面的接口就可以了,我们用 Field声明了表单的项,这样提交表单就跟普通的函数调用一样简单直接了。
    同样多版本版本FieldMap,使用方式和QueryMap差不多,不讲了

    2.4.3 Part & PartMap
    这个是用来上传文件的

    public interface FileUploadService {     
     @Multipart    
     @POST("upload")   
     Call<ResponseBody> upload(@Part("description") RequestBody description,  
                               @Part MultipartBody.Part file);
    }
    

    如果你需要上传文件,和我们前面的做法类似,定义一个接口方法,需要注意的是,这个方法不再有 @FormUrlEncoded 这个注解,而换成了 @Multipart,后面只需要在参数中增加 Part
    就可以了。也许你会问,这里的 Part 和 Field 究竟有什么区别,其实从功能上讲,无非就是客户端向服务端发起请求携带参数的方式不同,并且前者可以携带的参数类型更加丰富,包括数据流。也正是因为这一点,我们可以通过这种方式来上传文件,下面我们就给出这个接口的使用方法:


    上传文件.png

    在实验时,我上传了一个只包含一行文字的文件:

    Visit me: http://www.println.net
    那么我们去服务端看下我们的请求是什么样的:

    HEADERS

    HEADERS.png

    FORM/POST PARAMETERS

    description: This is a description

    RAW BODY

    BODY.png

    我们看到,我们上传的文件的内容出现在请求当中了。如果你需要上传多个文件,就声明多个 Part参数,或者试试 PartMap

    2.5 Converter,让你的入参和返回类型丰富起来

    2.5.1 RequestBodyConverter
    2.4.3 当中,我为大家展示了如何用 Retrofit上传文件,这个上传的过程其实。。还是有那么点儿不够简练,我们只是要提供一个文件用于上传,可我们前后构造了三个对象:


    fdfdf.png

    天哪,肯定是哪里出了问题。实际上,Retrofit 允许我们自己定义入参和返回的类型,不过,如果这些类型比较特别,我们还需要准备相应的 Converter,也正是因为 Converter 的存在,Retrofit 在入参和返回类型上表现得非常灵活。

    下面我们把刚才的 Service 代码稍作修改:

    public interface FileUploadService {      
    @Multipart    
    @POST("upload")        
    Call<ResponseBody> upload(@Part("description") RequestBody description,                
    //注意这里的参数 "aFile" 之前是在创建 MultipartBody.Part 的时候传入的        
                              @Part("aFile") File file);
    }
    

    现在我们把入参类型改成了我们熟悉的 File,如果你就这么拿去发请求,服务端收到的结果会让你哭了的。。。
    RAW BODY

    BODY.png
    服务端收到了一个文件的路径,这明显是 Retrofit 在发现自己收到的实际入参是个 File时,不知道该怎么办,情急之下给 toString了,而且还是个 JsonString(后来查证原来是使用了 GsonRequestBodyConverter。。)。
    接下来我们就自己实现一个 FileRequestBodyConverter
     static class FileRequestBodyConverterFactory extends Converter.Factory {       
     @Override    
     public Converter<File, RequestBody> requestBodyConverter(Type type, Annotation[]           
                 parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {             
         return new FileRequestBodyConverter();   
     }  
    }          
    static class FileRequestBodyConverter implements Converter<File, RequestBody> {             
           @Override    
           public RequestBody convert(File file) throws IOException {            
             return RequestBody.create(MediaType.parse("application/otcet-stream"), file);  
      }  
    }
    

    在创建 Retrofit 的时候记得配置上它:

    addConverterFactory(new FileRequestBodyConverterFactory())
    

    这样,我们的文件内容就能上传了。来,看下结果吧:

    RAW BODY

    BODY.png

    文件内容成功上传了.

    2.5.2 ResponseBodyConverter

    前面我们为大家简单示例了如何自定义 RequestBodyConverter,对应的,Retrofit 也支持自定义 ResponseBodyConverter。

    我们再来看下我们定义的接口:

    public interface GitHubService {    
     @GET("users/{user}/repos")  
    Call<List<Repo>> listRepos(@Path("user") String user);
    }
    

    返回值的类型为 List<Repo>,而我们直接拿到的原始返回肯定就是字符串(或者字节流),那么这个返回值类型是怎么来的呢?首先说明的一点是,GitHub 的这个 api 返回的是 Json 字符串,也就是说,我们需要使用 Json 反序列化得到 List<Repo>,这其中用到的其实是 GsonResponseBodyConverter。

    问题来了,如果请求得到的 Json 字符串与返回值类型不对应,比如:

    接口返回的 Json 字符串:

    {"err":0, "content":"This is a content.", "message":"OK"}

    返回值类型

    class Result {
    int code;//等价于 err
    String body;//等价于 content
    String msg;//等价于 message
    }

    这种情况下, Gson 就是再牛逼,也只能默默无语俩眼泪了,它哪儿知道字段的映射关系怎么这么任性啊。好,现在让我们自定义一个 Converter 来解决这个问题吧!

    GsonConverter .png

    当然,别忘了在构造 Retrofit 的时候添加这个 Converter,这样我们就能够愉快的让接口返回 Result 对象了。

    注意!!Retrofit 在选择合适的 Converter 时,主要依赖于需要转换的对象类型,在添加 Converter 时,注意 Converter 支持的类型的包含关系以及其顺序。

    3.结合Retrofit源码深度学习

    retrofit的最大特点就是解耦,要解耦就需要大量的设计模式
    精简流程图


    Retrofit流程图.png

    举个列子,跟随他进入主题

    public interface GitHubService {    
    @GET("users/{user}/repos")  
         Call<List<Repo>> listRepos(@Path("user") String user);
    }
    
    
    Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();
    GitHubService service = retrofit.create(GitHubService.class);
    
    //执行
    Call<List<Repo>> repos = service.listRepos("octocat");
    List<Repo> data = repos.execute();
    

    3.1首先通过@GET来标识这个接口是一个GET请求。那么看一下这个GET注解的定义。

      @Documented
      //@Target表示Annotation可用在什么地方。
      //METHOD:方法声明 
      @Target(METHOD)
      //@Retention表示在什么级别保存该注解信息
      //RUNTIME:VM会在运行时保留注解,这时可以通过反射读取注解信息
      @Retention(RUNTIME)
      public @interface GET {
        String value() default "";
      }
    

    3.2 接下来看一下是如何创建Retrofit对象的。

    public final class Retrofit  {
       ......代码省略.......
      //对平台的支持
      private Platform platform;
      //发起请求OKHttp3的Client工厂
      private okhttp3.Call.Factory callFactory;
      //OKHttp2的HttpUrl对象,也就是将我们传入的baseUrl字符串包装成HttpUrl
      private HttpUrl baseUrl;
      //转换器工厂集合,retrofit可以插入多个转化器,例如:Gson,Jackson等等
      private List<Converter.Factory> converterFactories = new ArrayList<>();
      //用于发起请求和接收响应的Call适配器工厂集合,
      //Retrofit对RxJava的支持就是通过在该集合中添加RxJavaCallAdapterFactory,
      //而RxJavaCallAdapterFactory正是继承自CallAdapter.Factory
      private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
      //Executor并发框架,用于对请求后响应结果的回调执行
      private Executor callbackExecutor;
      //是否需要立即生效
      private boolean validateEagerly;
    
      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());
      }
    
      //对属性的配置
      public Retrofit build() {
        if (baseUrl == null) {
          throw new IllegalStateException("Base URL required.");
        }
    
        okhttp3.Call.Factory callFactory = this.callFactory;
        if (callFactory == null) {
          callFactory = new OkHttpClient();
        }
    
        Executor callbackExecutor = this.callbackExecutor;
        if (callbackExecutor == null) {
          callbackExecutor = platform.defaultCallbackExecutor();
        }
    
        // Make a defensive copy of the adapters and add the default Call adapter.
        List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      //默认添加ExecutorCallAdapterFactory ,先记住~~~~~~
    adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
    
        // Make a defensive copy of the converters.
        List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
    
        return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
            callbackExecutor, validateEagerly);
      }
    
     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;
      }
    
    
    ......代码省略.......
    }
    
    

    Builder中的Platform.get()做什么了?我们继续点击查看:

    class Platform {
      private static final Platform PLATFORM = findPlatform();
    
      static Platform get() {
        return PLATFORM;
      }
      //通过findPlatform方法我们可以看出Retrofit支持三个平台
      private static Platform findPlatform() {
        try {
          //Android
          Class.forName("android.os.Build");
          if (Build.VERSION.SDK_INT != 0) {
            return new Android();
          }
        } catch (ClassNotFoundException ignored) {
        }
        try {
         //java
          Class.forName("java.util.Optional");
          return new Java8();
        } catch (ClassNotFoundException ignored) {
        }
        try {
          //IOS,这里的IOS指的是RoboVM。
          //RoboVM它是一种可以在iOS设备上运行Java应用程序的技术,这种技术主要还是用于在游戏开发中。
          Class.forName("org.robovm.apple.foundation.NSObject");
          return new IOS();
        } catch (ClassNotFoundException ignored) {
        }
        return new Platform();
      }
    
      CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {   
        //默认为null,子类重写赋值
        if (callbackExecutor != null) {
          return new ExecutorCallAdapterFactory(callbackExecutor);
        }
        return DefaultCallAdapterFactory.INSTANCE;
      }  
    }
    
    

    当前平台为Android平台之后返回一个Android对象,这个Android类是Platform中的一个静态内部类。我们看看他做了什么

    static class Android extends Platform {
         @Override 
        public Executor defaultCallbackExecutor() {
             //重写父类,返回的Handler对象
            return new MainThreadExecutor();
        }
    
        @Override 
        CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
          //网络请求时分析ExecutorCallAdapterFactory做了什么
          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);
          }
        }
      }
    

    3.3 接下来直入重点,Retrofit对象的创建OK以后,便通过Reetrofit中的create方法创建一个我们请求接口,其实创建的一个代理对象了,这里涉及点儿 Java 的动态代理的知识,直接来看create代码:

    public final class Retrofit {
      ......代码省略.......
     public <T> T create(final Class<T> service) {
        Utils.validateServiceInterface(service);
        if (validateEagerly) {
          eagerlyValidateMethods(service);
        }
        //这里返回一个service的代理对象
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
            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.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                if (platform.isDefaultMethod(method)) {
                  return platform.invokeDefaultMethod(method, service, proxy, args);
                }
                //每一个接口最终初始化一个serviceMethod
                ServiceMethod serviceMethod = loadServiceMethod(method);
                //并且Retrofit 与 Okhttp 完全耦合
                OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
                //发送请求,并且解析服务端返回的结果
                return serviceMethod.callAdapter.adapt(okHttpCall);
              }
            });
      }
      //这个方法通过反射获取我们创建service接口中所有的接口方法,然后根据接口方法和当前的
      //retrofit对象来获得ServiceMethod并且以接口方法作为Key,
      //ServiceMethod作为值添加到serviceMethodCache缓存中
      //下次便可以通过接口方法直接获取ServiceMethod
      private void eagerlyValidateMethods(Class<?> service) {
        Platform platform = Platform.get();
        for (Method method : service.getDeclaredMethods()) {
          if (!platform.isDefaultMethod(method)) {
            loadServiceMethod(method);
          }
        }
      }
    
      ServiceMethod loadServiceMethod(Method method) {
        ServiceMethod result;
        synchronized (serviceMethodCache) {
          result = serviceMethodCache.get(method);
          if (result == null) {
            //初始化ServiceMethod,并且执行serviceMethod.callAdapter
            result = new ServiceMethod.Builder(this, method).build();
            serviceMethodCache.put(method, result);
          }
        }
        return result;
      }
      ......代码省略.......
    }
    

    总结下,当我们通过代理类(也就是我们调用create方法后返回的service)调用我们所创建的接口方法时。InvocationHandler中的invoke方法将会被调用。在invoke方法中由于method.getDeclaringClass()获取到的是一个接口,并不是Object类,所以第一个条件不成立。而在Android平台下platform.isDefaultMethod(method)返回的为false,所以这个条件也不成立。之后通过loadServiceMethod方法来获取ServiceMethod。最后调用ServiceMethod中callAdapter的adapt方法。而ServiceMethod中的callAdapter属性是通过ServiceMethod中createCallAdapter方法所创建。在createCallAdapter中实际上是调用了Retrofit中的callAdapter方法来对ServiceMethod中的callAdapter进行初始化。

    简单的说,在我们调用 GitHubService.listRepos 时,实际上调用的是这里的InvocationHandler.invoke 方法~~

    3.4 下面再看一下ServiceMethod中的callAdapter方法如何执行,

    final class ServiceMethod<T> {
     
      final Retrofit retrofit;
        final Method method;
        final Annotation[] methodAnnotations;
        final Annotation[][] parameterAnnotationsArray;
        final Type[] parameterTypes; 
    
    
    public Builder(Retrofit retrofit, Method method) {
          this.retrofit = retrofit;
          this.method = method;
          this.methodAnnotations = method.getAnnotations();
          this.parameterTypes = method.getGenericParameterTypes();
          this.parameterAnnotationsArray = method.getParameterAnnotations();
        }
    
        public ServiceMethod build() {
          callAdapter = createCallAdapter();
          ......省略代码......
          return new ServiceMethod<>(this);
        }   
    
    
        private CallAdapter<?> createCallAdapter() {
          Type returnType = method.getGenericReturnType();
          if (Utils.hasUnresolvableType(returnType)) {
            throw methodError(
                "Method return type must not include a type variable or wildcard: %s", returnType);
          }
          if (returnType == void.class) {
            throw methodError("Service methods cannot return void.");
          }
          Annotation[] annotations = method.getAnnotations();
          try {
           //注意这里调用Retrofit的callAdapter
            return retrofit.callAdapter(returnType, annotations);
          } catch (RuntimeException e) { // Wide exception range because factories are user code.
            throw methodError(e, "Unable to create call adapter for %s", returnType);
          }
        }
    
       
    }
    

    接下来查看Retrofit的callAdapter做了什么

    public final class Retrofit {
      .......省略代码.........
    private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
       return nextCallAdapter(null, returnType, annotations);
      }
    
      public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
          Annotation[] annotations) {
        checkNotNull(returnType, "returnType == null");
        checkNotNull(annotations, "annotations == null");
    
        int start = adapterFactories.indexOf(skipPast) + 1;
        //通过遍历adapterFactories并根据我们的接口方法所中返回值类型来获取相应的适配器callAdapter。
        for (int i = start, count = adapterFactories.size(); i < count; i++) {
          CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
          if (adapter != null) {
            return adapter;
          }
        }
    
        .......省略代码.........
      }
    
    

    总结:通过遍历adapterFactories并根据我们的接口方法所中返回值类型来获取相应的适配器callAdapter,如果我们想返回的是RxJava中Observable对象, 那我们添加了RxJavaCallAdapterFactory,那么返回的就是RxJavaCallAdapter。如果没有添加那么此处的adapter为null,便会抛出异常。在正是通过这种适配器模式完成了对RxJava的完美结合。

    Call<List<Repo>> repos = service.listRepos("octocat");
    List<Repo> data = repos.execute();
    

    service.listRepos("octocat");这个方法到底怎么执行的我们上面还遗留了一个ExecutorCallAdapterFactory方法接下来我们一步一步研究它

    
    final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
      final Executor callbackExecutor;
    
      ExecutorCallAdapterFactory(Executor callbackExecutor) {
        this.callbackExecutor = callbackExecutor;
      }
       //get方法返回一个CallAdapter对象,
      @Override
      public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        if (getRawType(returnType) != Call.class) {
          return null;
        }
        final Type responseType = Utils.getCallResponseType(returnType);
        return new CallAdapter<Call<?>>() {
          @Override public Type responseType() {
            return responseType;
          }
            //adapt方法返回ExecutorCallbackCall对象,它实现了Retrofit中的Call接口
           //其实就是InvocationHandler.invoke 那个callAdapter的实现类
          @Override public <R> Call<R> adapt(Call<R> call) {
            return new ExecutorCallbackCall<>(callbackExecutor, call);
          }
        };
      }
    
      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;
        }
    
        @Override public void enqueue(final Callback<T> callback) {
          if (callback == null) throw new NullPointerException("callback == null");
    
          delegate.enqueue(new Callback<T>() {
            @Override public void onResponse(final Call<T> call, final Response<T> response) {
              callbackExecutor.execute(new Runnable() {
                @Override public void run() {
                  if (delegate.isCanceled()) {
                    // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
                    callback.onFailure(call, new IOException("Canceled"));
                  } else {
                    callback.onResponse(call, response);
                  }
                }
              });
            }
    
            @Override public void onFailure(final Call<T> call, final Throwable t) {
              callbackExecutor.execute(new Runnable() {
                @Override public void run() {
                  callback.onFailure(call, t);
                }
              });
            }
          });
        }
        ......
    }
    

    从ExecutorCallAdapterFactory类中可看出通过get方法返回一个CallAdapter对象,从对CallAdapter的实现中可以看出在CallAdapter中的adapt方法返回的是ExecutorCallbackCall对象。它实现了Retrofit中的Call接口。到这里我们也就明白了,当通过代理调用我们创建的接口方法中所返回的Call对象就是这个ExecutorCallbackCall。当我们通过call.enqueue来完成网络请求操作实际上就是调用ExecutorCallbackCall中的enqueue方法。在ExecutorCallbackCall中enqueue又将网络请求委托给OkHttpCall去执行。而这个OkHttpCall正是我们在Retrofit的create方法中所创建的OkHttpCall。由于OKHttp的CallBack接口中的onResponse和onFailure是在子线程中执行的,所以在这时候又通过callbackExecutor将CallBack的onResponse和onFailure切换到主线程中执行。

    有了上面分析后我们知道repos.execute()其实就是一个 OkHttpCall 实例,execute 就是要发起网络请求

    那么点进去看看Call 的接口:

    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();    
      //返回原始请求  
      Request request();
    }
    

    然后在继续查看OkHttpCall

    final class OkHttpCall<T> implements Call<T> {
    
      @Override public Response<T> execute() throws IOException {
      //这个call 是 Okhttp的call,本质对okhttpcall做了一层封装
        okhttp3.Call call;
        
       synchronized (this) {
        //处理重复执行的逻辑
          if (executed) throw new IllegalStateException("Already executed.");
          executed = true;
    
          if (creationFailure != null) {
            if (creationFailure instanceof IOException) {
              throw (IOException) creationFailure;
            } else {
              throw (RuntimeException) creationFailure;
            }
          }
    
          call = rawCall;
          if (call == null) {
            try {
              call = rawCall = createRawCall();
            } catch (IOException | RuntimeException e) {
              creationFailure = e;
              throw e;
            }
          }
        }
    
        if (canceled) {
          call.cancel();
        }
        //发送请求,并解析结果
        return parseResponse(call.execute());
      }
      .......省略代码.........
    }
    

    我们看到 OkHttpCall 其实也是封装了 okhttp3.Call,在这个方法中,我们通过 okhttp3.Call 发起了进攻,额,发起了请求。有关 OkHttp 的内容,我在这里就不再展开了。

    parseResponse 主要完成了由 okhttp3.Response 向 retrofit.Response 的转换,同时也处理了对原始返回的解析:

    final class OkHttpCall<T> implements Call<T> {
      ......省略代码.........
    Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {   
    
     ResponseBody rawBody = rawResponse.body();          
    //略掉一些代码  
      try {          
    //在这里完成了原始 Response 的解析,T 就是我们想要的结果,
    //比如GitHubService.listRepos 的 List<Repo>     
          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; 
       }
      }
       .......省略代码.........
    }
    
    

    3.5 结果适配,你是不是想用 RxJava?
    前面我们已经提到过 CallAdapter的事儿,默认情况下,它并不会对 OkHttpCall 实例做任何处理:

    final class DefaultCallAdapterFactory extends CallAdapter.Factory {    
        static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();        
        @Override  
        public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {    
          ... 毫不留情的省略一些代码 ...       
         return new CallAdapter<Call<?>>() {    
          ... 省略一些代码 ...          
        @Override 
        public <R> Call<R> adapt(Call<R> call) {            
          //看这里,直接把传入的 call 返回了    
          return call;   
       }}; 
     }
    }
    

    现在的需求是,我想要接入 RxJava,让接口的返回结果改为 Observable:

     public interface GitHub {        
    @GET("/repos/{owner}/{repo}/contributors")    
    Observable<List<Contributor>> contributors(               
            @Path("owner") String owner,               
            @Path("repo") String repo); 
     }
    

    Retrofit 的开发者们早就想到了这个问题,并且为我们提供了相应的 Adapter,只需在构造 Retrofit时,添加它:

    addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    

    这样我们的接口就可以以 RxJava 的方式工作了。
    接着我们搞清楚 RxJavaCallAdapterFactory 是怎么工作的,首先让我们来看下 CallAdapter
    的接口:

    public interface CallAdapter<T> {
          //返回http解析后的类型,需注意这个类型并不是接口返回的类型,而是
          //接口返回类型中泛型参数的实参
          Type responseType();
           //T 是我们需要转换成接口返回的类型。参数call 其实最初就是okhttpcall的实例
    //在这里T 其实是rxjava 支持的类型  ,比如 observable
          <R> T adapt(Call<R> call);
           
    
         //我们需要将Factory的子类对应的实例在构造 retrofit 时添加到其中
          abstract class Factory {
          //根据接口的返回类型(observable<T>)。注释类型等等来判断是否当前adapter 支持的类型,不是则返回null
        public abstract CallAdapter<?> get(Type returnType, Annotation[] annotations,
            Retrofit retrofit);
      
        //获取指定index的泛型参数的上限,比如对于
      //map <string, ? extends number >, index 为1 时的参数上限是number
        protected static Type getParameterUpperBound(int index, ParameterizedType type) {
          return Utils.getParameterUpperBound(index, type);
        }
    
          //获取原始类型,比如List<String> 返回list.class 这里传入的type情况
         //可能比较复杂,不能直接当做class 去做判断,这个方法在判断类型是否
         //为支持的类型时经常用到
        protected static Class<?> getRawType(Type type) {
          return Utils.getRawType(type);
        }
      }
    }
    

    代码中做了较为详细的注释,简单来说,我们只需要实现 CallAdapter 类来提供具体的适配逻辑,并实现相应的 Factory,用来将当前的 CallAdapter注册到 Retrofit 当中,并在 Factory.get方法中根据类型来返回当前的 CallAdapter 即可(如果对这个方面或者逻辑混乱的话,可以把3.3和3.4的流程在走一遍你就会明白 了)。知道了这些,我们再来看 RxJavaCallAdapterFactory:

    public final class RxJavaCallAdapterFactory extends CallAdapter.Factory {
    .......省略代码.........
       @Override
      public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
         //下面代码主要判断returntype 是否为rxjava 支持的类型
        Class<?> rawType = getRawType(returnType);
        String canonicalName = rawType.getCanonicalName();
        boolean isSingle = "rx.Single".equals(canonicalName);
        boolean isCompletable = "rx.Completable".equals(canonicalName);
        if (rawType != Observable.class && !isSingle && !isCompletable) {
          return null;
        }
        if (!isCompletable && !(returnType instanceof ParameterizedType)) {
          String name = isSingle ? "Single" : "Observable";
          throw new IllegalStateException(name + " return type must be parameterized"
              + " as " + name + "<Foo> or " + name + "<? extends Foo>");
        }
    
        if (isCompletable) {
       
          return CompletableHelper.createCallAdapter(scheduler);
        }
    
        CallAdapter<Observable<?>> callAdapter = getCallAdapter(returnType, scheduler);
        if (isSingle) {
          return SingleHelper.makeSingle(callAdapter);
        }
        return callAdapter;
      } 
    
    static final class SimpleCallAdapter implements CallAdapter<Observable<?>> {
        private final Type responseType;
        private final Scheduler scheduler;
    
        SimpleCallAdapter(Type responseType, Scheduler scheduler) {
          this.responseType = responseType;
          this.scheduler = scheduler;
        }
    
        @Override public Type responseType() {
          return responseType;
        }
    
        @Override public <R> Observable<R> adapt(Call<R> call) {
            在这里创建需作为返回值的 observable 实例,并持有call 的实例
            当observable.subscribe 触发时,call.execute 将会调用
          Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)) 
              .lift(OperatorMapResponseToBodyOrError.<R>instance());
          if (scheduler != null) {
            return observable.subscribeOn(scheduler);
          }
          return observable;
        }
      }
      .......省略代码.........
    }
    
    

    至此,Retrofit 大部分结构源码有了新的认知。

    4.Retrofit结合Rxjava在项目中实践

    相关文章

      网友评论

      • sxt_0215:方便给下源码吗
        WuXiao_:https://github.com/quiet-wuxiao/RxHttp 源码
        b6c7a3ded5ff:@Me武晓 源码 :flushed:
        WuXiao_:@宿希婷 ok,没问题近期会的,最近发神经研究go后端了
      • 捡淑:mark

      本文标题:Retrofit

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