Retrofit流程+源码分析

前言

前一篇分析了OkHttp的源码和流程,这一篇依然从Retrofit的使用角度并结合源码来分析,当然Retrofit是基于OkHttp的,所以OkHttp源码-流程-拦截器分析还是有必要了解的,而且Retrofit中使用了很多的设计模式,需要提前预览。

  • 在开始流程前需要先看一下整体的流程图以及设计模式,这里就用了之前的Stay的Retrofit源码流程图,虽然更新了很多版本,但主要部分还是没变化的,看完这图,结合代码,最后再看图,再自己看源码总结,会更好理解的。


    Retrofit流程图

基本使用流程

1.定义一个请求接口,包含请求方法

interface Api {
    @GET("users/{user}/repos")
    fun listRepos(@Path("user") user:String):Call<List<Repo>>
}

2.使用retrofit请求

   val retrofit = Retrofit.Builder()
          .baseUrl("https://api.github.com/")
          .addConverterFactory(GsonConverterFactory.create())
          .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
          .build()

   val api = retrofit.create(Api::class.java)
   val call = api.listRepos("")

   call.execute()

   call.enqueue(object :retrofit2.Callback<List<Repo>> {
      override fun onFailure(call: retrofit2.Call<List<Repo>>, t: Throwable) {}

      override fun onResponse(call: retrofit2.Call<List<Repo>>, response: retrofit2.Response<List<Repo>>) {}
   })

一、构建Retrofit实例

先来看看Retrofit中的成员变量

//解析接口注解后相关信息的存储
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
//请求工厂、默认为OkHttpClient
final okhttp3.Call.Factory callFactory;
final HttpUrl baseUrl;
//数据转换器工厂的集合,例如Gson转换
final List<Converter.Factory> converterFactories;
//网络请求适配器的集合
final List<CallAdapter.Factory> callAdapterFactories;
//回调执行器,在Android平台上就是通过Handler切换线程
final @Nullable Executor callbackExecutor;
//用来判断是否立即对我们的请求方法进行解析
final boolean validateEagerly;

1.Builder()方法

    Builder(Platform platform) {
      this.platform = platform;
    }
    
    public Builder() {
      this(Platform.get());
    }

1.1.Platform.get() 获取当前平台,一般是Android,当然也支持其他平台比如Java8等

class Platform {
  private static final Platform PLATFORM = findPlatform();
//通过findPlatfrom()获取平台信息
  static Platform get() {
    return PLATFORM;          
  }
  //比较SDK版本 不空就返回Android平台
  private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();                          
      }
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform(true);
  }
  ...
  static final class Android extends Platform {
    Android() {
      super(Build.VERSION.SDK_INT >= 24);
    }

    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }
    //通过Handler切换到主线程
    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

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

2.baseUrl() 将String类型的URL转换为请求用的URL

public Builder baseUrl(String baseUrl) {
      Objects.requireNonNull(baseUrl, "baseUrl == null");
      return baseUrl(HttpUrl.get(baseUrl));
}

public Builder baseUrl(HttpUrl baseUrl) {
      Objects.requireNonNull(baseUrl, "baseUrl == null");
      List<String> pathSegments = baseUrl.pathSegments();
      if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
        throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
      }
      this.baseUrl = baseUrl;
      return this;
}

3.addConverterFactory(GsonConverterFactory.create()) 添加转换器

//主要就是创建带Gson对象的转换工厂
GsonConverterFactory.create()  
//将带有Gson的转换器工厂添加到集合中
public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
      return this;
}

4.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
添加一个请求的适配器工厂最后添加到Builde中的变量中,通过设计模式,像现在支持Kotlin的协程,也只需要切换工厂即可

5.build() 完成创建Retrofit实例

 public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
      //一般不设置的话 就是默认OkHttpClient
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
      //这里其实就是MainThreadExecutor()
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

  ....
      //创建了Retrfit 为前面的变量赋值
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }

二、创建接口实例

val api = retrofit.create(Api::class.java)
这部分用了动态代理、外观模式创建了接口实例,以下是源码

  public <T> T create(final Class<T> service) {
    //1.主要做了一些验证比如是否接口以及是否需要提前缓存ServiceMethod对象
    validateServiceInterface(service);
    //2.代理接口中的方法
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();
          private final Object[] emptyArgs = new Object[0];

          @Override public @Nullable Object invoke(Object proxy, Method method,
              @Nullable Object[] args) throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //3.关键方法
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }

ServiceMethod<?> loadServiceMethod(Method method)
在这个方法中解析接口中的注解等信息 以及 信息的缓存

  ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        //解析方法的注解、返回类型、参数的注解以及参数类型
        result = ServiceMethod.parseAnnotations(this, method);
        //将解析出的信息添加到ConcurrentHashMap中供之后用
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    //解析接口中的方法
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
   // ...中间做了些验证 省略了
   //将接口方法的调用调整为HTTP请求
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract @Nullable T invoke(Object[] args);
}
  • 具体的解析 RequestFactory parseAnnotations(Retrofit retrofit, Method method)
    static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
        return new Builder(retrofit, method).build();
    }

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

    RequestFactory build() {
      for (Annotation annotation : methodAnnotations) {
        //循环解析注解
        parseMethodAnnotation(annotation);
      }
      ...
      return new RequestFactory(this);
    }
  • static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory)
  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
   ...
    //1.从Retrofit.java中的callAdapterFactories集合中获取对应的请求适配器
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    ...
    //2.从Retrofit.java中的converterFactories集合中获取对应的数据转换器
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    //检查是否kotlin协程中挂起的函数
    if (!isKotlinSuspendFunction) {
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } 
    ...
  }
  • 最终还是转接到OkHttp的Call
 // loadServiceMethod(method).invoke(args != null ? args : emptyArgs);

 @Override final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
  }

三、同/异步请求数据

同步请求call.execute()

  @Override public Response<T> execute() throws IOException {
    okhttp3.Call call;

    synchronized (this) {
      ...
      call = rawCall;
      if (call == null) {
        try {
          //1.这里创建了OkHttp的call
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException | Error e) {
          throwIfFatal(e); //  Do not assign a fatal error to creationFailure.
          creationFailure = e;
          throw e;
        }
      }
    }

    if (canceled) {
      call.cancel();
    }
    //2.解析OkHttp请求回来的数据
    return parseResponse(call.execute());
  }

  //callFactory就是OkHttpClient 
  private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

  //将OkHttp请求回的Response做解析
  Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();

    //判断一些状态码
    int code = rawResponse.code();
    if (code < 200 || code >= 300) {
      try {
        // Buffer the entire body to avoid future I/O.
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
      } finally {
        rawBody.close();
      }
    }

    if (code == 204 || code == 205) {
      rawBody.close();
      return Response.success(null, rawResponse);
    }

    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      //通过之前的赋值的转换器 将数据转为具体类型
      T body = responseConverter.convert(catchingBody);  
      //封装到Response中返回
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      catchingBody.throwIfCaught();
      throw e;
    }
  }

异步请求call.enqueue(Callback<T> callback)

  @Override public void enqueue(final Callback<T> callback) {
    Objects.requireNonNull(callback, "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 {
          //依旧是使用了OkHttp的Call
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(t);
          failure = creationFailure = t;
        }
      }
    }

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

    if (canceled) {
      call.cancel();
    }
    //依旧是使用了OkHttp的同步请求
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          throwIfFatal(e);
          callFailure(e);
          return;
        }

        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          throwIfFatal(t);
          t.printStackTrace(); // TODO this is not great
        }
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e);
      }

      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          throwIfFatal(t);
          t.printStackTrace(); // TODO this is not great
        }
      }
    });
  }

看到这里基本上的整体流程也就差不多了 再结合开头的图会比较清晰一点,如果自己能看一遍源码,那你的理解会更深一个层次。

总结

总的来说,Retrofit是基于OkHttp的二次封装,通过动态代理将数据请求转接于OkHttp请求并通过使用设计模式做到了低耦合,高扩展性,使用方便的网络请求框架,它其中很多的点值得我们去揣摩,学习,并且运用到我们自己的项目中。

最后

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

推荐阅读更多精彩内容