【这一次保证学会读源码】Retrofit篇

369 阅读20分钟

相信很多小伙伴都尝试阅读过各种优秀的开源框架的源码。即使没认真读过,在面试的时候也会被迫去搜几篇相关源码解读的博客去阅读和背诵。

很多小伙伴会说阅读源码会很痛苦,甚至很助眠。

我突然困了晚安(熊猫头)_晚安_困了_熊猫_突然表情

其实这是很多小伙伴阅读源码的方式不对。举个例子,很多小伙伴阅读源码时,会一行一行去阅读源码,尝试看懂每一行的源码,而我们的大脑好比内存,当我们尝试理解每一行源码的时候,这时候我们的内存就会溢出,大脑直接进入睡眠模式。

你大脑3MB够用吗?_3MB_够用_大脑表情

而当我们不再尝试一上来就精读每一行的代码,而是有选择性的阅读框架,理解框架的整体大致架构,抓住主要的核心模块的代码进行精读,这样就能够让帮助我们站在宏观的角度去理解一个框架整体的架构,然后再一点点去慢慢精度其中的每个模块的源码,理解每个模块的作用和实现方式,这时候我们就能够比较轻松的学习整个源码的框架。

今天我们就拿一个优秀的网络上层应用框架开刀,来学习一下阅读优秀框架的源码方法。

Retrofit使用流程介绍

对于了解和使用过Retrofit的小伙伴可以大胆、放心的直接跳过这个章节。

Retrofit是Square公司开发的一款针对Android和Java网络请求的框架,遵循Restful设计风格,底层基于OkHttp.

Retrofit是一个优秀的网络应用层框架,它的底层封装了OkHttp网络请求框架,能够帮助开发者快速的完成网络请求的业务需求。

我们来简单看一下Retrofit的使用。这不是本文的重点,Retrofit有很多强大的功能,支持Http的许多请求方式,对于不了解但感兴趣的小伙伴可以看完本文后,自行阅读一下Retrofit的官方使用教程

//定义网络请求接口
public interface GitHubService {
	//官方例子:访问Github的用户仓库数据
	//以Get请求的方式,访问users/{user}/repos路径下的接口
	//其中的{user}由参数@Path("user") String user代替
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

接着我们实例化Retrofit实例:

Retrofit retrofit = new Retrofit.Builder()
		//我们接口的host地址为:https://api.github.com/
    .baseUrl("https://api.github.com/")
    //通过添加ConverterFactory,直接帮助我们将网络请求返回的json字符串转变为对应的JavaBean对象
    //这里直接帮我们转换成了List<Repo>对象
    .addConverterFactory(GsonConverterFactory.create())
    .build();
//因此我们请求完整的地址是:https://api.github.com/users/{user}/repos
GitHubService service = retrofit.create(GitHubService.class);

接着我们通过我们实例化的service实例来创建一个Call对象。相信对于使用过Okhttp的小伙伴对Call对象并不陌生,但这个Call对象并不是Okhttp中的Call对象,而是Retrofit的一个包装对象。对于这个,我们后面学习源码的时候会了解到。

//获得Call实例,这时候我们请求的Url地址就完整了
//我们请求的地址是:https://api.github.com/users/octocat/repos
Call<List<Repo>> repos = service.listRepos("octocat");

有了Call对象后我们就可以真正发起网络请求了,Retrofit和Okhttp一样(毕竟就是封装了Okhttp),也是支持异步请求和同步请求两种方式,异步请求调用Callenqueue方法;同步请求调用Callexecute方法。这里我们只展示异步的方式。

repos.enqueue(new Callback<List<Repo>>() {
            @Override
            public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
                Log.e("TAG","请求成功");
            }

            @Override
            public void onFailure(Call<List<Repo>> call, Throwable t) {
                Log.e("TAG","请求失败");
            }
});

这样我们就发起了一次完整的网络请求。Retrofit能够帮助我们简单的实现各种各样的Http请求方式。这里就不一一赘述,这并不是本文的重点,小伙伴们可以直接阅读官网的例子,快速的了解Retrofit的使用方式。

学习阅读源码方式

首先我们应该确立一下真正阅读源码的方式,我们不应该过分拘泥于每一行代码的实现,我们应该在最初阅读源码 时,只要大致了解源码的整体架构即可,这样可以帮助我们避免我们的大脑内存溢出。

当我们对框架大致由一个框架以后,我们可以再尝试一点点精度每一个模块代码的具体实现。因此阅读源码的大致流程是:

  • 粗略的阅读源码 ,尝试理解框架的整体架构
  • 在理解了整体架构的基础上,确立自己需要精读的模块,再尝试精读某一模块的具体实现

在开始我们学习如何读源码发车前,我还需要向各位车友说明两点

  1. 本篇文章重点不是理解整个Retrofit的每个实现技术细节,而是帮助各位车友完成阅读源码的第一个步骤,初略的阅读源码,尝试理解框架的整体发起网络请求的架构。
  2. 本篇文章站在假装个人第一次阅读源码的基础上,所以不会以一个阅读了N次源码的老司机的上帝视角去解说源码。
这不是去幼儿园的车我要下车_幼儿园_下车_不是表情

而我们这篇文章主要目标是:

  • 了解retrofit的整体发起网络请求的架构流程。

各位车友可以在这篇文章的基础上(即实现步骤一:理解整体架构的基础上)再去精读自己感兴趣的模块的实现原理,例如:Retrofit是如何一步步解析注释,生成对应的请求方法的;Retrofit的各类Converter是如何做到类型转换的。诸如此类的问题,我相信车友在掌握今天阅读源码的方法基础上,不敢保证快读,但能够更轻松的阅读源码,解开心中的谜底。

正式发车

Retrofit如何发送网络请求

我们要了解Retrofit的网络请求框架,我们自然要从网络请求的发送作为入口。我们通过Call对象的enqueue方法作为我们今天发车的入口。

  /**
   * Asynchronously send the request and notify {@code callback} of its response or if an error
   * occurred talking to the server, creating the request, or processing the response.
   */
  void enqueue(Callback<T> callback);

我们可以看到enqueue是一个抽象方法,没关系我们可以跟进它实现的地方看看。我们可以看到一个Retrofit叫OKHttpCall的类实现了这个方法:

@Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("callback == null");
		
    okhttp3.Call call;
    Throwable failure;
		
    synchronized (this) {
    	// 关注点1:加了同步锁,保证一个Call只被执行一次
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
        	// 关注点2:rawCall为Okhttp的Call对象,真正用来发起网络请求的Call
        	//createRawCall()方法创建了一个Okhttp的Call对象
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

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

    if (canceled) {
      call.cancel();
    }
		// 关注点3:通过Okhttp的Call对象发起异步请求
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
        	//关注点4:通过解析Okhttp的Response成为Retrofit的response
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
        	//关注点5:解析失败,调用callFailure方法,回调出去
          callFailure(e);
          return;
        }
        //关注点6:解析成功,调用callSuccess方法,回调出去
        callSuccess(response);
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
        try {
        	//失败回调
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      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();
        }
      }
    });
  }

我们可以大致阅读一下上面的源码片段,我们就了解了。哦~Retrofit其实就是包装了Okhttp的框架,通过OkHttp的Call对象去发送网络请求,不过如此而已。

不过如此(酸了 柠檬精表情包)_柠檬精_不过如此表情
这就是我们阅读源码时,所需要理解的第一步骤,略读源码,了解大致的框架架构。

在这里我标出了6个关注点,我们再更精细的阅读和理解一下6个关注点的代码。

关注点1:

	synchronized (this) {
    	// 关注点1:加了同步锁,保证一个Call只被执行一次
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
  }

在这里我们可以看到我会通过synchronized关键词,锁住了Retrofit的OkHttpCall的实例对象,确保每一个Retrofit的OkHttpCall对象实例只会被执行一次。当我们还要发起这个请求的时候,我们需要重新创建一个Retrofit的OkHttpCall实例。

关注点2:

okhttp3.Call call;
// 关注点2:rawCall为Okhttp的Call对象,真正用来发起网络请求的Call
//createRawCall()方法创建了一个Okhttp的Call对象
call = rawCall = createRawCall();

在这里我们可以大致猜测通过createRawCall方法创建了OkHttp的Call实例。因为这个callrawCall的实例都是okhttp3.Call类型的。

我们这时候就会忍不住点进createRawCall方法来证实我们的猜测:

  private okhttp3.Call createRawCall() throws IOException {
  	//通过serviceMethod实例的toRequest方法创建OkHttp的Requset对象
    Request request = serviceMethod.toRequest(args);
    //通过serviceMethod的callFactory属性,创建一个OkHttp的Call对象
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

果然我们通过serviceMethod实例创建了Okhttp的Request对象和Call对象。并将最后的Okhttp的Call对象返回回去。

那么这个toRequest方法是如何构建一个Okhttp的Request对象的呢,我们点进去看一下:

/** Builds an HTTP request from method arguments. */
  Request toRequest(Object... args) throws IOException {
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
        contentType, hasBody, isFormEncoded, isMultipart);

    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

    int argumentCount = args != null ? args.length : 0;
    if (argumentCount != handlers.length) {
      throw new IllegalArgumentException("Argument count (" + argumentCount
          + ") doesn't match expected count (" + handlers.length + ")");
    }

    for (int p = 0; p < argumentCount; p++) {
      handlers[p].apply(requestBuilder, args[p]);
    }

    return requestBuilder.build();
  }

这个ServiceMethod类的toRequest方法,我们发现看的有点点懵逼了。大致好像就是通过一堆参数,构建了OkHttp的Request对象,那么这些对象又是在何时,在哪里赋值的呢?

这里我们就先不跟了,当我们开始有点懵逼的时候,我们要心中默念我们此次读源码的目的:

  • 初略的阅读源码 ,尝试理解框架的整体架构。而这个整体架构就是发起网络请求的整体架构。

这里看不懂,我们就先暂时放一下。到这一步,我们大致了解就是通过ServiceMethod这个对象的属性,创建了一个Okhttp的Call对象,有了这个Call对象,我们就可以愉快的进行网络请求了。

别慌稳住问题不大_别慌_稳住_不大_问题表情

关注点3:

通过我们Okhttp的Call实例,我们就可以调用它的enqueue方法,发起网络请求了。

这一步没有是那么难度,就不赘述了。我们主要关注它的Callback回调。

关注点4、5和6:

@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
        	//关注点4:通过解析Okhttp的Response成为Retrofit的response
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
        	//关注点5:解析失败,调用callFailure方法,回调出去
          callFailure(e);
          return;
        }
        //关注点6:解析成功,调用callSuccess方法,回调出去
        callSuccess(response);
}

我们在Okhttp的onResponse回调方法中,尝试解析Okhttp的Response对象实例,并将OkHttp的对象实例,转换为Retrofit的Response对象实例。

如果转换失败,则被catch逻辑捕获,调用失败的方法callFailure,把错误信息回调出去。如果成功则调用成功的方法callSuccess把转换后的response实例回调出去。

至此,我们就已经大致了解了Retrofit是如何基于Okhttp发送一次异步的网络请求的。

复盘一下Retrofit的发起异步请求的整体架构:

  1. 通过同步锁,锁住对象实例,保证一个Call只会被调用一次
  2. 通过ServiceMethod创建一个OkHttp的Call对象实例
  3. 通过Okhttp的对象实例,发起异步网络请求
  4. 并在回调过程中,对Response进行处理并回调

如何转换Response

上面一章节最后一部分,我们知道Retrofit通过parseResponse来帮助我们将Okhttp的Response转换成Retrofit的Response对象实例。在这一章节我们可以具体看一下parseResponse是如何帮我们做到转换的。

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
		//记录responseBody的内容
    ResponseBody rawBody = rawResponse.body();

    // Remove the body's source (the only stateful object) so we can pass the response along.
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();
		
    int code = rawResponse.code();
    //关注点1:通过Response的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);
    }

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
    	//关注点2:通过serviceMethod转换Response Body
      T body = serviceMethod.toResponse(catchingBody);
      //关注点3:返回转换后的Response
      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;
    }
  }

关注点1:

首先我们根据Response的响应码(即code)来分别进行处理。对于Http协议了解的车友,应该知道Http除了2xx开头的响应码代表请求成功,其他的响应码都代表着此次请求没有返回成功的响应。响应码分为下面五种类型:

分类分类描述
1xx信息,服务器收到请求,需要请求者继续执行操作
2xx成功,操作被成功接收并处理
3xx重定向,需要进一步的操作以完成请求
4xx客户端错误,请求包含语法错误或无法完成请求
5xx服务器错误,服务器在处理请求的过程中发生了错误
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();
      }
}

因此我们可以看到code小于200或者code大于等于300的code码,Response直接通过error方法创建了一个只包含errorBody的对象。

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

而204和205的响应码,虽然服务端已经成功处理了请求,但返回的响应中没有包含任何响应体的内容,所以直接通过success方法返回body和errorBody为空的Response对象。

关注点2:

T body = serviceMethod.toResponse(catchingBody);

我们又看到了这个ServiceMethod对象(我们后面会有机会具体介绍这个对象),通过它的toResponse方法,将我们的Response的body部分转换成一个泛型类型。

这里我们再跟进去看一下toResponse方法的具体实现:

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

我们可以看到通过responseConverterconvert方法进行了转换。而这个responseConverter对象的的来源就是我们当初创建Retrofit实例时,通过addConverterFactory传入的转换工厂。

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                //传入Gson的转换工厂
                .addConverterFactory(GsonConverterFactory.create())
                .build();

当我们ServiceMethod初始化的时候,会通过Retrofit实例寻找到一个能够适用它返回值的转换工厂,通过调用转换器的convert方法将返回体转换为我们想要的对象。

例如我们添加了GsonConverterFactory,它的convert方法会将json响应体转换为一个Java对象。

@Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      return adapter.read(jsonReader);
    } finally {
      value.close();
    }
}

关注点3:

return Response.success(body, rawResponse);

最后我们通过Response的success方法创建一个bodyError为null,body属性是已经被我们转换后的Response对象。

到这里我们的Retrofit转换就完成了,Retrofit支持多种转换器,甚至我们可以自己去定义自己的转换器,每个转换器的源码我们可以自己去看一下。

复盘一下,在这一部分我们知道了Retrofit在处理解析响应体的时候做了以下操作:

  1. 根据Http的响应码,返回对应的Response对象
  2. 如果响应码为2xx,且有响应体,则通过合适的转换器,将响应体做类型转换,并返回转换后的Response对象。

Retrofit Call如何创建的

我们通过前面的章节知道Retrofit能够通过Call对象实例去发起网络请求。但Retrofit的Call是如何被创建的呢?

//GitHubService实例由retrofit实例的create方法返回
GitHubService service = retrofit.create(GitHubService.class);
//Call 通过我们自己定义的GitHubService接口产生
Call<List<Repo>> repos = service.listRepos("octocat");

我们可以看到Call对象由我们自己定义的GitHubService接口的listRepos抽象方法返回。

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

等等!我们都知道我们要实例化一个接口,是必须实现它所有抽象方法的。但是!我好像没有实现过GitHubServicelistRepos抽象方法啊!这个接口是如何被实例化的?

递归懵逼 - “数学体系”懵逼脸表情包_懵逼_斗图表情
//GitHubService实例由retrofit实例的create方法返回
GitHubService service = retrofit.create(GitHubService.class);

问题好像在这个create方法里。接下来我们就要看到Retrofit最最最核心的代码了。

别说了,上车与我一撸_上车_别说表情
public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    //关注点1:通过动态代理,代理我们传入的接口 
    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.
            //如果为Object类或者平台原有的方法,例如equals、hashCode等方法,直接调用
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //关注点2:获取ServiceMethod对象
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
        		//关注点3:创建Retrofit Call的实现类OkHttpCall
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            //关注点4:转换为对应的返回值类型,在这里被转换为ExecutorCallbackCall
            //如果我们添加了RxJavaCallAdapterFactory的话,会被转化成CallEnqueueOnSubscribe或CallExecuteOnSubscribe类
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

关注点1:

return (T) roxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler invocationHandler)

这行代码已经回答了我们为什么没有实现接口的抽象方法,但它却能够神奇的进行实例化。凶手就是:动态代理

动态代理

何为动态代理,这里可以简单解释下。动态代理简单理解就是我们传入一个接口,然后JDK在运行的过程中动态帮我们创建class字节码并加载的过程。而接口的调用最终都会调用InvocationHandlerinvoke方法中。这样说可能有点抽象,我们可以看一个例子。

public interface GitHubService {
		String sayHello(String name);
}

可能我们现在GitHubService有一个getName的抽象方法。我们如果要实例化GitHubService接口,我们必须实例化接口。

GitHubService gitHubService = new GitHubService() {
            @Override
            public String sayHello(String name) {
                return "Hello,"+name;
            }
}

但是我们通过动态代理来帮助我们完成实例化的流程,甚至可以在方法的前后做一些其他的操作,这就是所谓的代理。

GitHubService service = (GitHubService) Proxy.newProxyInstance(GitHubService.class.getClassLoader(), new Class[]{GitHubService.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return "Hello,"+args[0];
            }
        });
String hello = service.sayHello("DaMai");
Log.e("TEST",hello);

![image-20200908152311447](/Users/damai/Library/Application Support/typora-user-images/image-20200908152311447.png)

我们上面的这行代码其实和我们自己实例化接口的作用是一致的,也是Retrofit能够帮我们实例化的原理。

我们通过Java的Proxy类来实现动态代理的功能,它接受三个参数:

  1. ClassLoader,用来加载接口的类加载器,通常我们直接需要被代理Class的ClassLoader即可
  2. Class<?>[],被代理Class实现的接口
  3. InvocationHandler接口,InvocationHandler接口的invoke方法是我们实现抽象方法被代理的实现逻辑。当被代理的接口的抽象方法被调用的时候,会调用invoke方法。因此我们在这里可以写抽象方法的具体实现。

回到上面的例子,我们可以看到我们代理了GitHubService接口,并将代理对象转换为GitHubService接口类型,这样就完成了GitHubService的实例化。

GitHubService service = (GitHubService) Proxy.newProxyInstance(GitHubService.class.getClassLoader()
, new Class[]{GitHubService.class}
, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return "Hello,"+args[0];
            }
        });

当我们调用service实例的sayHello方法的时候,它其实调用的就是代理的InvocationHandler接口的invoke方法。

String hello = service.sayHello("DaMai");

//抽出InvocationHandler接口的invoke方法来帮助我们直观的理解
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    return "Hello,"+args[0];
}

因此最终打印出了Hello,DaMai的字符串。理解了这个动态代理实例化接口的流程,我们也就了解了Retrofit如何帮助我们实例化我们接口中抽象的方法的。

关注点2:

ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method);

终于见到了我们陌生又熟悉的老朋友ServiceMethod,原来ServiceMethod对象是在这里被实例化的。我们来仔细看一下loadServiceMethod方法,看一下它是如何被实例化的。

ServiceMethod<?, ?> loadServiceMethod(Method method) {
		// 首先看看是否有缓存,有的话直接返回缓存
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;
		
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
      	//如果没有缓存,则初始化一个ServiceMethod并返回
        result = new ServiceMethod.Builder<>(this, method).build();
        //将新创建的ServiceMethod存入缓存中
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

我们可以看到loadServiceMethod首先会判断当前缓存中是否已经存在当前Method对象的对应的ServiceMethod实例,如果有的话直接返回。如果不存在的话则新创建一个实例返回并存入缓存中。

ServiceMethod初始化过程中就不展开了,初始化的过程主要用了Builder建造者模式去生产ServiceMethod实例,在这个过程中会解析我们接口的抽象方法的注释返回值类型参数类型等等。还记得我们之前一个懵逼的点,ServiceMethodtoRequest方法嘛,生产的Requst对象所需要的参数,例如HttpMethod(POST、GET...)、请求参数啊等等,都是在这个此时产生的。

具体小伙伴们可以精读一下Retrofit是如何一步步解析接口的抽象方法,生成对应的OkHttp的Request对象的。

关注点3:

OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);

这一行我们创建了Retrofit的实现类OkHttpCall,它封装了OkHttp的网络请求逻辑,我们最初分析Retrofit异步请求enqueue方法,就是这个类的一个方法。我们看一下它是如何实例化的。

OkHttpCall(ServiceMethod<T, ?> serviceMethod, Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
}

只是保存了ServiceMethod对象和方法的参数数组仅此而已。然后供我们发起请求时候创建Request对象或者解析转换Response对象使用。就这么简单。

关注点4:

serviceMethod.callAdapter.adapt(okHttpCall);

这一行代码主要是获取到一个合适返回类型的CallAdapter生成对应的对象。在Android平台主要返回的是ExecutorCallbackCall对象。这样说可能有点抽象。

不急我们一步步来,这里的逻辑可能会有一点点绕。

首先我们来看一下ServiceMethod的callAdapter值是什么时候被赋值的。

public ServiceMethod build() {
      callAdapter = createCallAdapter();
      //省略不重要的代码
      .....
}

我们可以看到ServiceMethodbuild初始化的时候会调用createCallAdapter方法为callAdapter属性赋值。

接着我们来看下createCallAdapter方法是如何实现的。

    private CallAdapter<T, R> createCallAdapter() {
    	//获取到我们调用的抽象方法的返回值类型
      Type returnType = method.getGenericReturnType();
      //省略不重要代码
      //....
      //获取到我们抽象方法的注解
      Annotation[] annotations = method.getAnnotations();
      try {
        //noinspection unchecked
        // 传入retrofit.callAdapter方法中
        return (CallAdapter<T, R>) 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);
      }
    }

我们可以看到createCallAdapter方法主要做了下面3件事:

  1. 获取我们调用接口的抽象方法的返回值类型
  2. 获取到我们抽象方法的注解
  3. 调用retrofit.callAdapter方法,把获取到的返回值类型和注解传进去

我们可以看一下Retrofit类的callAdapter方法:

  public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }
  
  public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");
	
    int start = adapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
    	// 根据抽象方法的返回值类型和注解,在adapterFactories属性中寻找适合的adapterFactory,并调用adapterFactory的get方法返回合适的CallAdapter
      CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
  }

我们可以看到最终我们是根据抽象方法的返回值类型和注解寻找合适的CallAdapter.Factory,并通过CallAdapter.Factoryget方法返回对应的CallAdapter对象。

看到这里我们感觉一头雾水,看了半天看了个寂寞,那么这个adapterFactories又是何时被赋值的呢?这个数组里面又装了什么药呢?

Retrofit$Builder.class

private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
public Builder() {
			//获取到当前运行的平台,在Android平台获取到的是Android类,后面会提到
			//如果在Java平台,获取到的是Java8类
      this(Platform.get());
}

public Retrofit build() {
      //...省略不重要代码
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
      	//如果callbackExecutor为空,通过platform实例获取一个Executor对象
        callbackExecutor = platform.defaultCallbackExecutor();
      }
      //将Executor对象传入platform的defaultCallAdapterFactory方法中获得一个CallAdapter.Factory实例
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
}

我们可以看到在Retrofit.Builder对象调用build方法创建Retrofit实例的时候,会经理以下步骤:

  1. 如果属性callbackExecutor为空,通过platform实例获取一个Executor对象
  2. 将callbackExecutor属性传入platform的defaultCallAdapterFactory方法中获得一个CallAdapter.Factory实例
  3. 并将CallAdapter.Factory实例传入adapterFactories数组

这里我们遇到了很多陌生的对象,首先我们来看看platform实例对应的Platform对象是什么:

static class Android extends Platform {
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      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);
      }
    }
}

我们都知道Retrofit能够在Java平台和Android 平台都适用,所以在Android平台运行的时候,我们platform属性被赋值的类就是Android类。

callbackExecutor = platform.defaultCallbackExecutor();

我们可以看到我们调用defaultCallbackExecutor方法,最终返回的就是一个MainThreadExecutor对象,它其实实现的效果就是:将传入的Runnable任务切回主线程,我们都知道网络请求都是耗时的,都是在另外的异步线程执行,当网络请求完成后,我们要通过切换回主线程来执行回调返回结果,这样方便我们在回调中处理UI操作 。

adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

然后我们通过defaultCallAdapterFactory方法将ExecutorCallAdapterFactory这个继承于CallAdapter.Factory的对象加入到adapterFactories数组中。

最后我们终于回到我们最初的关注点:

serviceMethod.callAdapter.adapt(okHttpCall);


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

由于我们接口的抽象方法返回的是一个Call对象。因此会在ServiceMethod初始化的时候,callAdapter属性会通过ExecutorCallAdapterFactory对象的get方法获得一个CallAdapter对象。

final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
  final Executor callbackExecutor;

  ExecutorCallAdapterFactory(Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }

  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
      	//返回一个ExecutorCallbackCall对象
        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) {
    	//关注点1
    	//传入了MainThreadExecutor
      this.callbackExecutor = callbackExecutor;
      //传入了OkHttpCall
      this.delegate = delegate;
    }

    @Override public void enqueue(final Callback<T> callback) {
      checkNotNull(callback, "callback == null");
			//最终调用了OkHttpCall去请求网络,OkHttpCall又用了Okhttp去请求网络
      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          //通过MainThreadExecutor切换到主线程,回调结果
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              if (delegate.isCanceled()) {
                // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
              } else {
                callback.onResponse(ExecutorCallbackCall.this, response);
              }
            }
          });
        }

        @Override public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
    }
    
 		//省略不重要代码
 		//.....
  }
}

我们这里看到我们由于接口的抽象方法返回值是一个Call,最终我们会找到ExecutorCallAdapterFactory这个类通过get方法返回一个ExecutorCallbackCall类型。并把我们我们返回到主线程的MainThreadExecutorOKHttpCall对象通过实例化的时候传入到ExecutorCallbackCall类型中。

最终调用的是OKHttpCallenqueue方法,OKHttpCall把请求的结果回调给ExecutorCallbackCall后,它通过MainThreadExecutor的实例往UI主线程消息队列中塞进一个任务,并在UI主线程把网络请求的结果回调给开发者。

我们收获了什么

Retrofit 本质上是一个 RESTfulHTTP 网络请求框架的封装,即通过大量的设计模式封装了 OkHttp 框架,使得简洁易用。

  • 在阅读框架源码的时候我们能体会到整个框架的设计的优美。阅读优秀的源码能够帮助我们加深对框架的理解,同时能够帮助我们学习优秀的编码习惯。阅读源码的方法其实也不难,掌握正确适合自己的阅读源码的方法,能够帮助我们相对轻松的理解框架的设计架构和细节流程。
  • 在阅读我们Retrofit的时候我们能够学习到Retrofit的架构核心,通过动态代理去代理接口的方法,从而实现通过注解的方式去生成对应OkHttp请求所需要的Request和Response对象。大大简化了网络请求的使用难度。

希望这篇文章对阅读源码困难症患者有一些许帮助。