Retrofit

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

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在项目中实践

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容