Retrofit源码赏析二 —— 主流程

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第22天,点击查看活动详情

创建Retrofit实例

Retrofit采用构造者模式来创建实例。

new Retrofit.Builder()
        .client(okhttpClient)
        .baseUrl(baseUrl)
        .addConverterFactory(converterFactory)
        .addCallAdapterFactory(callAdapterFactory)
        .build();
复制代码

Retrofit.Builder

Retrofit.BuilderRetrofit的静态类部类,它有以下属性

private final Platform platform;
private @Nullable okhttp3.Call.Factory callFactory;
private @Nullable  HttpUrl baseUrl;
private final List<Converter.Factory> converterFactories = new ArrayList<>();
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
private @Nullable  Executor callbackExecutor;
private boolean validateEagerly;
复制代码
  • platform platform表示Retrofit当前运行的平台。它不能通过外部设置,只能由Retrofit自动识别。
  • callFactory callFactory表示网络请求器的工厂,主要作用是生成网络的请求器Call,一般情况下我们通过Builder#client()方法设置为OkHttpClient,当然,我们也可以通过Builder#callFactory()设置自定义工厂。
  • baseUrl baseUrl是我们网络请求中的基础Url,列如域名,一般情况下都是调用Builder#baseUrl(String)设置一个字符串,Retrofit会调用Retrofit#baseUrl(HttpUrl)方法自动转换成HttpUrl类型。
  • converterFactories 数据转换器列表,将请求到的数据转换成我们指定的类型。
  • callAdapterFactories 请求适配器列表,默认情况下我们得到的返回结果是Call<R>类型,通过自定义请求适配器,我们可以将结果转化为我们需要的类型,比如配合RxJava得到Observable<R>
  • callbackExecutor 回调执行器,用于Call#enqueue()方法中,可实现线程切换,如果不指定,就会获取platformdefaultCallbackExecutor
  • validateEagerly 是否提前对接口中的方法进行验证,如果Service声明的方法非常多,提前集中验证虽然能在早期就发现错误的方法,但是可能造成明显的性能损耗,所以一般在测试阶段设置为true保证即使发现问题,在正式环境中设置为false,这样接口中的方法就会分散验证,保证了性能问题。

创建Retrofit.Builder实例

Builder(Platform platform) {
}

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

Builder(Retrofit retrofit) {
}
复制代码

Retrofit.Builder提供了三个构造函数,但是暴露给外部调用的就只有无参构造函数,调用的时候会自动获取当前平台类型,然后调用Builder(Platform platform)方法进行初始化,同时Retrofit也提供了快速复制一个现有的Retrofit.Builder的方法。

public Builder newBuilder() {
    return new Builder(this);
}
复制代码

通过调用Retrofit#newBuilder()方法,内部就会调用Builder(Retrofit retrofit)构造函数,从而快速创建一个Builder,具体复制逻辑如下

Builder(Retrofit retrofit) {
	//1
    platform = Platform.get();
    callFactory = retrofit.callFactory;
    baseUrl = retrofit.baseUrl;
	//2
    for (int i = 1,
         size = retrofit.converterFactories.size() - platform.defaultConverterFactoriesSize();
         i < size;
         i++) {
        converterFactories.add(retrofit.converterFactories.get(i));
    }
    //3
    for (int i = 0,
         size =
         retrofit.callAdapterFactories.size() - platform.defaultCallAdapterFactoriesSize();
         i < size;
         i++) {
        callAdapterFactories.add(retrofit.callAdapterFactories.get(i));
    }
    callbackExecutor = retrofit.callbackExecutor;
    validateEagerly = retrofit.validateEagerly;
}
复制代码
  1. 可以看到即使是从一个现有的Retrofit快速创建一个Builder,platform属性也是由Retrofit自动识别,然后初始化。
  2. Retrofit在创建的时候会自动添加platform.defaultConverterFactories和BuiltIntConverters,所以这里复制的时候converterFactories就不需要再添加这两个了。
  3. 对于callAdapterFactories的添加和converterFactories类似。

设置baseUrl

public Builder baseUrl(URL baseUrl) {
    Objects.requireNonNull(baseUrl, "baseUrl == null");
    return baseUrl(HttpUrl.get(baseUrl.toString()));
}
复制代码
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;
}
复制代码

设置baseUrl有3个重载方法,无论调用那个都需要满足下面两点

  1. baseUrl不能为null。
  2. baseUrl必须以“/”结尾。

生成Retrofit实例

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();
    }
    List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
    callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

    List<Converter.Factory> converterFactories =
            new ArrayList<>(
                    1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
    converterFactories.add(new BuiltInConverters());
    converterFactories.addAll(this.converterFactories);
    converterFactories.addAll(platform.defaultConverterFactories());

    return new Retrofit(callFactory,baseUrl,unmodifiableList(converterFactories),unmodifiableList(callAdapterFactories),callbackExecutor,validateEagerly);
}
复制代码

build()方法主要执行了以下操作

  1. 检查baseUrl,如果为null直接抛出异常。
  2. 检查网络请求器工厂callFactory,如果没有设置就默认使用OkHttpClient对象。
  3. 检查回调执行器callbackExecutor,如果没有设置就默认使用当前platform提供的默认执行器,对于Android平台来说,使用的是MainThreadExecutor
  4. 添加请求适配器,包括平台提供的和用户自定义的。
  5. 添加数据转换器,包括BuiltInConverters和平台提供的以及用户自定义的。
  6. 将converterFactories和callAdapterFactories转换成只读的List,然后创建Retrofit。

创建API接口实例

有了Retrofit实例之后,调用其create()方法获取API接口实例,在此之前,我们先准备一个GitHubService

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

然后调用create()方法

GitHubService service = retrofit.create(GitHubService.class);
复制代码

Retrofit.create()

create()方法实现比较简单,主要是使用动态代理生成传进来的API接口的实例。

public <T> T create(final Class<T> service) {
    validateServiceInterface(service);
    return (T)
        Proxy.newProxyInstance(
                service.getClassLoader(),
                new Class<?>[]{service},
                new InvocationHandler() {
				//省略invoke()实现
                });
}
复制代码

这里主要做了2件事。

  1. 验证APIService
  2. 利用动态代理生成APIService实例。

验证APIService

接口验证

private void validateServiceInterface(Class<?> service) {
    if (!service.isInterface()) {
        throw new IllegalArgumentException("API declarations must be interfaces.");
    }

    Deque<Class<?>> check = new ArrayDeque<>(1);
    check.add(service);
    while (!check.isEmpty()) {
        Class<?> candidate = check.removeFirst();
        if (candidate.getTypeParameters().length != 0) {
            throw new IllegalArgumentException(//......省略异常);
        }
        Collections.addAll(check, candidate.getInterfaces());
    }
    if (validateEagerly) {
        //......省略方法验证
    }
}
复制代码

validateServiceInterface从以下方面进行检测

  1. APIService只能是一个接口类型。
  2. APIService以及其继承的接口不能包含泛型参数。
  3. 如果设置validateEagerly为true,就会立即对APIService中的方法进行检测,提前检测虽然能及时发现异常方法,但是会造成性能损耗。

方法验证

private void validateServiceInterface(Class<?> service) {
	//......省略接口验证
    if (validateEagerly) {
        Platform platform = Platform.get();
        for (Method method : service.getDeclaredMethods()) {
            if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
                loadServiceMethod(method);
            }
        }
    }
}
复制代码

可以看到,提前验证会跳过default方法和static修饰的方法。对于其他方法会调用loadServiceMethod()进行加载。

加载方法

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);
            serviceMethodCache.put(method, result);
        }
    }
    return result;
}
复制代码

加载ServiceMethod是一个耗时的过程,所以这里使用了缓存,对于加载过的方法,会缓存到serviceMethodCache中,serviceMethodCache定义如下:

private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
复制代码

ConcurrentHashMap是线程安全的,如果serviceMethodCache中有当前method则直接取出返回,否则调用ServiceMethod.parseAnnotations()创建一个ServiceMethod,然后放入serviceMethodCache并返回结果。

ServiceMethod的parseAnnotations方法会创建ServiceMethod,这里只暂时只关注主流程,就不深入查看。

Method.invoke()

public <T> T create(final Class<T> service) {
    //......省略其他代码
    new InvocationHandler() {
        private final Platform platform = Platform.get();
        private final Object[] emptyArgs = new Object[0]; 
        public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)throws Throwable {
            if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
            }
            args = args != null ? args : emptyArgs;
            return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
        }
}
复制代码

首先,如果当前方法method来源于Object,则正常调用,对于其他方法,如果是default方法则交由平台platform的invokeDefaultMethod()方法执行,否则将method转换成ServiceMethod然后执行。

执行请求

经过前面我们获取到了APIService的实例,然后通过它就可以调用具体方法,然后执行请求

Call<List<Repo>> repos = service.listRepos("user").execute();
复制代码

请求的执行有同步和异步两种,这里以同步为例

//OkHttpCall.java
public Response<T> execute() throws IOException {
	okhttp3.Call call;
	synchronized (this) {
	  if (executed) throw new IllegalStateException("Already executed.");
	  executed = true;
	  call = getRawCall();
	}
	if (canceled) {
	  call.cancel();
	}
	return parseResponse(call.execute());
}
复制代码

可以看到Retrofit最后其实是调用来的OkHttp的Call执行了请求,它主要做了以下操作

  1. 判断请求是否执行过,如果执行过了就抛出异常。
  2. 通过getRawCall()方法将retrofit2.Call转换成okhttp3.Call。
  3. 通过okhttp3.Call执行请求。
  4. 上一步执行请求返回的结果是okhttp3.Response,通过parseResponse()将okhttp3.Response转化为retrofit2.Response。

到这里Retrofit使用的主流程分析完了,对于Call和Response转换等源码将在后面进行分析。

猜你喜欢

转载自juejin.im/post/7110416098784182279