Retrofit原理分析

Retrofit原理分析

温故而知新

还记得retrofit的使用方法嘛,下面我们来回顾一下

接口定义
public interface GitHubService {

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

}
获取Service的引用
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

GitHubService service = retrofit.create(GitHubService.class);
调用
Call<List<Repo>> repos = service.listRepos("octocat");

猜内部实现

Retrofit为什么这么神奇,为什么接口没有实现就能直接调用起方法;接口直接就能转化成我们写的那一大坨复杂的代码;为什么···

在不看源码的情况下我们大可猜测retrofit的实现,retrofit内部帮我们对接口进行了实现,并且解析了我们在函数上面的注解,生成了okhttp能够解析的http请求,然后调用okhttp发起网络请求;请求返回后根据接口函数定义的返回值的类型,使用fastjson或者gson或者其他格式数据的解析库,将返回数据实例化为java对象,最终返回给接口。我的大致猜测就是这样,然后用伪代码实现以下我的猜测:

GitHubService1 service = new GitHubService1() {

    @Override
    public Call<List<Repo>> listRepos(String user) {
        // 解析注解生成okhttp Request
        Request request;
        OkHttpClient client = new OkHttpClient();
        // 解析注解初始化client参数
        okhttp3.Call call = client.newCall(request);
        // 通过okhttp call 生成 retrofit call

        return null;
    }

};

初步猜内部实现是这样的,通过编译时注解生成对应接口的实现类。

原理分析

事实上我的猜测是错误的,光想着运行时注解会比较耗性能,觉得大家都不会用运行时注解,这种错误的思维方式,官方虽然不建议使用运行时注解,但是也要视情况而定,如果运行时注解能够带来很大的开发效率提升,同时对性能消耗也不多,那么也是可以接受的。比如retrofit的核心实际上是java自身提供的动态代理,加上一些运行时注解,实际运行效率也不低,且可大大提高开发效率,下面我们来看源码分析:

先从最常用的入口下手:

GitHubService service = retrofit.create(GitHubService.class);

这个方法内容也不多,全部贴出来

@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
        // 对接口方法进行预初始化,解析注解
        eagerlyValidateMethods(service);
    }
    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, @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);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);
          }
        });
}

核心就是这个方法啦,这个方法为接口创建了动态代理,对接口进行了实例化

Proxy.newProxyInstance

然后是这个接口InvocationHandler

InvocationHandler

// 当代理对象的方法被调用时就会回调到这个handler中来,我们就可以具体实现handler

接着看InvocationHandler#invoke中的内容,前面都是一些异常处理,最下面就是核心实现,ServiceMethod就是接口方法的具体实现。

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

这里是通过接口定义方法生成okhttpcall封装代理类。

然后再看看接口注解解析:


private void eagerlyValidateMethods(Class<?> service) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
      if (!platform.isDefaultMethod(method)) {
        loadServiceMethod(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 = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
}


public ServiceMethod build() {
    callAdapter = createCallAdapter();
    responseType = callAdapter.responseType();
    if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
        }
        responseConverter = createResponseConverter();

        // 解析接口方法注解
        for (Annotation annotation : methodAnnotations) {
            parseMethodAnnotation(annotation);
        }

        if (httpMethod == null) {
            throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
        }

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

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

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

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

    if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
    }
    if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
    }
    if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
    }
    if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
    }

    return new ServiceMethod<>(this);
}

相信看到这里,诸君都已经了解了retrofit的基本原理了,其他深入的细节还是留给诸君自己去挖掘,这里就不在赘述了,谢谢大家。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值