充足的时间才是第一生产力。当源码看多了之后,对于很多点的梳理上可能就不会像之前那样更具体了,本篇主要还是在于对实现主流程的分析。Retrofit在之前项目中并没有用到过,这次决定来看一些Retrofit相关实现来了解其中的原理。对于想要了解Java中的动态代理应用,想要用更简洁的方式实现网络请求,Retrofit可能是最好的学习案例了,之前的项目中在写网络请求的时候,每次都需要写大量的样板代码来构造出一个网络请求,代码一旦增加,犯错误的概率也就会增加,维护成本也就增加,同时也降低了开发效率,工程化最佳实践的目标是让开发者只专注于自己的业务逻辑,而不需要关心下层具体的实现,尽可能暴露少的业务无关的内容给到上层。
对于Retrofit需要定义一个接口类,声明要请求的接口和其中参数的传递方式,然后通过调用create方法就可以构造一个Service类,调用相应的方法即可帮助我们进行网络请求的发送,代码非常简洁,同时我们也可以通过对不同的业务模块分Service的方式来实现对于不同业务接口的隔离。
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") .addConverterFactory(GsonConverterFactory.create()) .build(); GitHubService service = retrofit.create(GitHubService.class); 复制代码
Retrofit实例的创建是主要是进行一些信息的配置,这里核心分析的在于其create方法,本篇文章的分析将会顺着create方法的实现到其最终一个网络请求的调用,同时结合着对Retrofit源码的分析来进一步展开对于Java中动态代理实现的分析。
public <T> T create(final Class<T> service) { validateServiceInterface(service); return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); private final Object[] emptyArgs = new Object[0]; //对于代理中的继承自Object的方法还有平台方法直接invoke执行 @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); } //对于其它我们定义的方式调用loadServiceMethod return loadServiceMethod(method).invoke(args != null ? args : emptyArgs); } }); } 复制代码
在Retrofit的实现中create是一个很关键的方法,也是我们作为源码分析的一个切入点。首先看其第一个方法validateServiceInterface,该方法主要对传递的Service类进行校验,同时通过对注解的处理来获取到其中定义方法的相关信息。具体核心实现如下。
for (Method method : service.getDeclaredMethods()) { if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) { loadServiceMethod(method); } } 复制代码
对于service中定义的方法进行遍历,然后通过调用loadServcieMethod进行相应的处理
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; } 复制代码
loadServiceMethod方法是Retrofit的整个框架的实现的一个很核心的点了,首先会从ServiceMethodCache中进行相应的查找,如果查找到则直接返回,没有的话则会调用ServiceMethod的注解处理方法,parseAnnotaion来生成一个ServiceMethod,同时将其加入到缓存之中。那么接下来,我们来看一下ServiceMethod的相关实现,和其注解解析方法。
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) { RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); Type returnType = method.getGenericReturnType(); if (Utils.hasUnresolvableType(returnType)) { throw methodError(method, "Method return type must not include a type variable or wildcard: %s", returnType); } if (returnType == void.class) { throw methodError(method, "Service methods cannot return void."); } return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); } 复制代码
这里的核心实现在RequestFactory的parseAnnoations和HttpServiceMethod的parseAnnotations方法。接下我们RequestFactory只是通过retorift和method构建了一个RequestFactory,记录了方法的参数,返回值类型还有注解信息。最后将其作为参数传递至HttpServiceMethod中的parseAnnotations,对于注解的处理主要是来将我们在发送网络请求时需要的信息从注解中解析出来,当调用invoke方法的时候,结合解析出来的参数构建一个网络请求。
final ReturnT invoke(Object[] args) { Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter); return adapt(call, args); } 复制代码
在动态代理中当我们进行一个方法调用的时候实际执行代码如下
loadServiceMethod(method).invoke(args != null ? args : emptyArgs); 复制代码
对于loadServiceMethod方法,我们之前已经分析过,这里主要根据方法解析的缓存中查找,然后调用其invoke方法,来构造一个OkHttp的请求。然后调用okhttp的相应的请求方法。
对于Rerofit在项目中的使用,最上层是我们的业务逻辑,下面是我们针对每一个业务逻辑模块定义的API,第三层是Retrofit对于API层向网络请求OKhttp层的桥接,最终通过OkHttp将请求发送出去。同时其借助于注解与动态代理让代码的实现变的更优雅。借助于接口的形式来很好的实现网络请求在业务层的隔离。注解与动态代理搭配的形式对于后续的开发设计也是很有启发意义的。