Retrofit 2.x 系列之二 - Retrofit的源码解析

文章使用的Retrofit版本为:2.3.0

在前面的文章已经介绍过了Retrofit的使用,这篇文章主要是分析Retrofit的源码。

源码解析

首先我们从Retrofit的初始化配置开始说起:

Retrofit retrofit = new Retrofit.Builder()
        .client(new OkHttpClient.Builder().build())
        .baseUrl("http://www.baidu.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build();

这是我们常见的Retrofit初始化的代码,从 Builder().build() 就能明白Retrofit和okhttp一样是使用建造者模式来构建对象的,看源码我们先看这个类的成员变量,好的代码规范从命名我们就能初步理解这个类大概是做哪些事:

public static final class Builder {
    private final Platform platform; // 运行的平台,Retrofit可以运行在ios、Android、Java3个平台环境下,会根据当前环境去适配不同的平台
    @Nullable
    private Factory callFactory;// Okhttp的工厂类
    private HttpUrl baseUrl;// baseUrl,HttpUrl类型
    private final List<retrofit2.Converter.Factory> converterFactories; // 数据转换器的集合,主要作用是将网络请求返回的数据转换成用户想要的格式,用户可自定义解析方式,比如Gson、FastJson
    private final List<retrofit2.CallAdapter.Factory> adapterFactories; // 数据适配器的集合,主要作用是将接口返回的对象适配成用户想要的对象,用户可自定义,比如RxJava
    @Nullable
    private Executor callbackExecutor;// 回调执行器,网络请求返回数据通过这个进行回调
    private boolean validateEagerly;// 是否提前解析注解等信息
    
    //省略其余代码...
}

代码中的注释是我自己加上去的,注释很详细,这里就不多解释了。

我们跟着 .build() 方法进去看源码:

public Retrofit build() {
    if (this.baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
    } else {
        Factory callFactory = this.callFactory;
        if (callFactory == null) {
            callFactory = new OkHttpClient();
        }

        Executor callbackExecutor = this.callbackExecutor;
        if (callbackExecutor == null) {
            callbackExecutor = this.platform.defaultCallbackExecutor();
        }

        List<retrofit2.CallAdapter.Factory> adapterFactories = new ArrayList(this.adapterFactories);
        adapterFactories.add(this.platform.defaultCallAdapterFactory(callbackExecutor));
        List<retrofit2.Converter.Factory> converterFactories = new ArrayList(this.converterFactories);
        return new Retrofit((Factory)callFactory, this.baseUrl, converterFactories, adapterFactories, callbackExecutor, this.validateEagerly);
    }
}

build() 方法中首先就判断了baseUrl是否为空,如果为空会抛出一个异常提示 Base URL required. 这也是为什么在 Retrofit 2.x 系列之一 - Retrofit的使用姿势(基础篇) 这篇文章中说 baseUrl()必需项了。
当baseUrl不为空时,走到else的代码分支,判断 this.callFactory 是否为空,如果为空则初始化一个默认的 OkhttpClient 对象,同样也验证了基础篇文章中说的 client()可选项,这一步我们先停下来看 this.callFactory 在哪里被赋值的:

 public Retrofit.Builder client(OkHttpClient client) {
     return this.callFactory((Factory)Utils.checkNotNull(client, "client == null"));
 }

 public Retrofit.Builder callFactory(Factory factory) {
     this.callFactory = (Factory)Utils.checkNotNull(factory, "factory == null");
     return this;
 }

通过 Builder().client() 方法传入 OkhttpClient 实例,紧接着调用自己的 callFactory() 方法,在该方法内部对 callFactory 这个成员变量赋值。我们回到 build() 方法继续看。
判断完 callFactory 之后,判断 callbackExecutor ,这个变量我们一般都不会去配置它,所以这里默认为null,Retrofit就会使用各自平台的 defaultCallbackExecutor 。因为我们是 Android 中使用,我们去看Android平台默认的 callbackExecutor 是什么:

static class Android extends Platform {
    Android() {
    }

    public Executor defaultCallbackExecutor() {
        return new Platform.Android.MainThreadExecutor();
    }

    Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
        if (callbackExecutor == null) {
            throw new AssertionError();
        } else {
            return new ExecutorCallAdapterFactory(callbackExecutor);
        }
    }

    static class MainThreadExecutor implements Executor {
        private final Handler handler = new Handler(Looper.getMainLooper());

        MainThreadExecutor() {
        }

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

代码中可以看到无参的 defaultCallbackExecutor() 方法中返回的是 MainThreadExecutor,也就是将返回接口切换到主线程中执行。

最后 build() 方法创建了 adapterFactoriesconverterFactories 的集合,并将我们添加进去的数据转换器(本文中为 GsonConverterFactory)、数据适配器(本文中为 RxJavaCallAdapterFactory)传入集合,然后使用配置好的所有参数构建了Retrofit对象。至此Retrofit就创建好了。

初始化配置完成后,第二步就是与网络请求的接口类配合,创建能够发起网络请求的对象。但是在Retrofit的使用步骤中,我们是创建了一个接口来配置网络请求的方式、参数、Header等等,接口是不能实例化的,所以,Retrofit使用了动态代理的方式(非常重要),动态代理的相关知识可以看:你真的完全了解Java动态代理吗?看这篇就够了 这篇文章,里面描述了动态代理的使用场景、设计思想和原理。我们看一下Retrofit是如何使用动态代理的:

Api api = retrofit.create(Api.class);

我们跟着 create() 方法进入源码:

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (this.validateEagerly) {
        this.eagerlyValidateMethods(service);
    }

    return Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service}, new InvocationHandler() {
        private final Platform platform = Platform.get();

        public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
            if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
            } else if (this.platform.isDefaultMethod(method)) {
                return this.platform.invokeDefaultMethod(method, service, proxy, args);
            } else {
                ServiceMethod<Object, Object> serviceMethod = Retrofit.this.loadServiceMethod(method);
                OkHttpCall<Object> okHttpCall = new OkHttpCall(serviceMethod, args);
                return serviceMethod.callAdapter.adapt(okHttpCall);
            }
        }
    });
}

create() 方法中第一步先验证一下传递进来的接口类的合法性,第二步判断是否要提前解析接口类中的方法,第三步就是创建动态代理了。这里的动态代理会将我们编写的请求接口类中所有方法,都委托到这个动态代理去处理,每当我们调用一个方法,动态代理中 InvocationHandler 对象的 invoke 方法就会执行一次。 invoke 方法中前两个 if 代码块中都是别的平台(ios、java8),我们只要关注最后的 else 代码块,里面的三行代码就是Retrofit最重要的地方了,我们跟着第一个方法 loadServiceMethod 进去看:

 ServiceMethod<?, ?> loadServiceMethod(Method method) {
     ServiceMethod<?, ?> result = (ServiceMethod)this.serviceMethodCache.get(method);
     if (result != null) {
         return result;
     } else {
         Map var3 = this.serviceMethodCache;
         synchronized(this.serviceMethodCache) {
             result = (ServiceMethod)this.serviceMethodCache.get(method);
             if (result == null) {
                 result = (new retrofit2.ServiceMethod.Builder(this, method)).build();
                 this.serviceMethodCache.put(method, result);
             }

             return result;
         }
     }
 }

该方法会先从缓存中获取 ServiceMethod 对象,如果获取不到就利用 Builder 创建一个,再加入缓存。我们看看 ServiceMethod.Builder对象是什么,因为 Builder 是用来创建 ServiceMethod 的,知道了 Builder 就能知道 ServiceMethod,和查看 Retrofit 类一样,先看下成员变量都有什么:

static final class Builder<T, R> {
    final Retrofit retrofit;
    final Method method;
    final Annotation[] methodAnnotations; // 方法的注解:@Post、@Header 等等
    final Annotation[][] parameterAnnotationsArray; // 方法中参数的注解:@Field、@Query 等等
    final Type[] parameterTypes; //参数类型
    Type responseType; //返回类型
    boolean gotField;
    boolean gotPart;
    boolean gotBody;
    boolean gotPath;
    boolean gotQuery;
    boolean gotUrl;
    String httpMethod; // 请求方法
    boolean hasBody; // 是否有请求体
    boolean isFormEncoded; // 是否是表单
    boolean isMultipart; // 是否是文件
    String relativeUrl;
    Headers headers; // 请求头
    MediaType contentType; // 媒体类型
    Set<String> relativeUrlParamNames;
    ParameterHandler<?>[] parameterHandlers; // 根据请求方法将参数中的注解处理成网络请求的请求体内容
    Converter<ResponseBody, T> responseConverter; //返回数据的转换器
    CallAdapter<T, R> callAdapter; // 方法返回值得对象适配器
    //省略其余代码...
}

代码中添加了注释,通过注释我们能知道,这个 ServiceMethod 类就是对我们自定义的 ApiService接口 中的所有注解、方法、参数逐一去进行解析,解析的步骤在该类的 build() 方法中:

 public ServiceMethod build() {
     this.callAdapter = this.createCallAdapter();
     this.responseType = this.callAdapter.responseType();
     if (this.responseType != Response.class && this.responseType != okhttp3.Response.class) {
         this.responseConverter = this.createResponseConverter();
         Annotation[] var1 = this.methodAnnotations;
         int p = var1.length;
         
         for(int var3 = 0; var3 < p; ++var3) {
             Annotation annotation = var1[var3];
             this.parseMethodAnnotation(annotation);
         }

         if (this.httpMethod == null) {
             throw this.methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
         } else {
             if (!this.hasBody) {
                 if (this.isMultipart) {
                     throw this.methodError("Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
                 }

                 if (this.isFormEncoded) {
                     throw this.methodError("FormUrlEncoded can only be specified on HTTP methods with request body (e.g., @POST).");
                 }
             }

             int parameterCount = this.parameterAnnotationsArray.length;
             this.parameterHandlers = new ParameterHandler[parameterCount];

             for(p = 0; p < parameterCount; ++p) {
                 Type parameterType = this.parameterTypes[p];
                 if (Utils.hasUnresolvableType(parameterType)) {
                     throw this.parameterError(p, "Parameter type must not include a type variable or wildcard: %s", parameterType);
                 }

                 Annotation[] parameterAnnotations = this.parameterAnnotationsArray[p];
                 if (parameterAnnotations == null) {
                     throw this.parameterError(p, "No Retrofit annotation found.");
                 }

                 this.parameterHandlers[p] = this.parseParameter(p, parameterType, parameterAnnotations);
             }

             if (this.relativeUrl == null && !this.gotUrl) {
                 throw this.methodError("Missing either @%s URL or @Url parameter.", this.httpMethod);
             } else if (!this.isFormEncoded && !this.isMultipart && !this.hasBody && this.gotBody) {
                 throw this.methodError("Non-body HTTP method cannot contain @Body.");
             } else if (this.isFormEncoded && !this.gotField) {
                 throw this.methodError("Form-encoded method must contain at least one @Field.");
             } else if (this.isMultipart && !this.gotPart) {
                 throw this.methodError("Multipart method must contain at least one @Part.");
             } else {
                 return new ServiceMethod(this);
             }
         }
     } else {
         throw this.methodError("'" + Utils.getRawType(this.responseType).getName() + "' is not a valid response body type. Did you mean ResponseBody?");
     }
 }

第一个for循环中的 this.parseMethodAnnotation(annotation); 主要是对请求方法(@GET、@POST、@DELETE等等)进行解析。

然后我们回到创建动态代理的 invoke() 方法源码中:

ServiceMethod<Object, Object> serviceMethod = Retrofit.this.loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

通过 loadServiceMethod() 方法已经创建好 ServiceMethod ,而 ServiceMethod 类中也保存了解析后的请求所需的各种相关信息,紧接着创建了 OkHttpCall 对象,这个对象是 Retrofit 中对 OkHttpCall 接口的实现类,当我们调用 异步请求 enqueue 或者 同步请求 execute 时,就是通过 OkHttpCall 去调用 OkHttp 的请求,所以我们说 Retrofit 和其他 OkHttpUtils 的开源项目一样,只是对 OkHttp 的一种封装,底层的网络请求还是通过 OkHttp 发起的。由于篇幅过长, OkHttpCall 源码相对简单,我们这里就不展开对它的源码解析了,感兴趣的道友可自行查看。

回到主题,我们接着看最后一行代码,调用了 serviceMethod.callAdapter.adapt(okHttpCall); ,这里就是为什么我们通过添加 RxJavaCallAdapterFactory 之后,我们可以在请求结果返回 RxJava 的对象了。

到这里我们还存在一个疑问,RxJavaCallAdapterFactory 的工作时机我们看到了,那 GsonConverterFactory 呢?
这就要说回 OkhttpCall 了,假设我们执行的是 异步请求 enqueue ,在其内部最终会调用 OkHttpcallenqueue 方法:

call.enqueue(new okhttp3.Callback() {
    public void onResponse(okhttp3.Call call, Response rawResponse) throws IOException {
        retrofit2.Response response;
        try {
            response = OkHttpCall.this.parseResponse(rawResponse);
        } catch (Throwable var5) {
            this.callFailure(var5);
            return;
        }

        this.callSuccess(response);
    }

	//省略其他代码...
});

在结果的回调中,调用了自己的 parseResponse 方法:

retrofit2.Response<T> parseResponse(Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();
    rawResponse = rawResponse.newBuilder().body(new OkHttpCall.NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())).build();
    int code = rawResponse.code();
    if (code >= 200 && code < 300) {
        if (code != 204 && code != 205) {
            OkHttpCall.ExceptionCatchingRequestBody catchingBody = new OkHttpCall.ExceptionCatchingRequestBody(rawBody);

            try {
                T body = this.serviceMethod.toResponse(catchingBody);
                return retrofit2.Response.success(body, rawResponse);
            } catch (RuntimeException var9) {
                catchingBody.throwIfCaught();
                throw var9;
            }
        } else {
            rawBody.close();
            return retrofit2.Response.success((Object)null, rawResponse);
        }
    } else {
        retrofit2.Response var5;
        try {
            ResponseBody bufferedBody = Utils.buffer(rawBody);
            var5 = retrofit2.Response.error(bufferedBody, rawResponse);
        } finally {
            rawBody.close();
        }

        return var5;
    }
}

在一个try catch代码块中,调用了 this.serviceMethod.toResponse(catchingBody); 这里就是去找到我们创建 Retrofit 时传递进去的 ConverterFactory 把网络请求返回的数据转换成我们自己想要的格式。

至此,整个 Retrofit 的源码就解析完了,在整个过程中我们会发现 Retrofit 将设计模式充分运用在了整个项目中,比如:Builder模式(建造者模式)、适配器模式(Adapter)、工厂模式(Factory)、代理模式(Procy)、装饰模式(OkHttpCall)等等,这也让我意识到设计模式能够给我们的架构带来质的飞跃,因此还需要多看源码,才能不断进步。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值