美文网首页
Retrofit2 源码解析

Retrofit2 源码解析

作者: 楚灵彦 | 来源:发表于2017-08-20 11:58 被阅读0次

    Retrofit2 源码解析

    1. Retrofit是什么

    来自Retrofit官网的介绍:

    A type-safe HTTP client for Android and Java
    

    一个基于OkHttp的RESTFUL Api请求工具

    2. Retrofit的用法

    • 创建Retrofit对象
    public static final String API_URL = "https://api.github.com";
    
    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(API_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build();
    
    • api新建一个Java接口,用Java注解来描述这个api
    public interface GitHubApiService {
    @GET("/users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
    // you can add some other meathod
    }
    
    • 这个retrofit对象创建一个GitHubService对象:
    GitHubApiService gitHubApiService=retrofit.create(GitHubApiService.class);//获取API接口的实现类的实例对象
    
    Call<List<Repo>> call = gitHubApiService.listRepos("octocat");
    
    
    • 异步回调结果
    // 请求数据,并且处理response
    call.enqueue(new Callback<List<Repo>>() {
        @Override
        public void onResponse(Response<List<Repo>> list) {
            Log.d("list: " + list);
        }
        @Override
        public void onFailure(Throwable t) {
        }
    });
    
    • 同步回调结果
     List<Repo> list = call.execute();
    

    3. Retrofit的原理

    Retrofit整个工作流程:将java接口,转换成一个个Http请求,交由Okhttp进行发送

    Retrofit库依赖了Okhttp库和okio库,核心库分为Http包其他核心类

    Http包里面包含各种定义的注解,帮忙开发者定义请求方式和请求参数。

    如何将一个请求放到Okhttp里面去?

    答案是: Retrofit利用了java的动态代理技术

    动态代理代码如下:

    /** Create an implementation of the API defined by the {@code service} interface. */
    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, 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 serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
    

    首先看动态代理方法,这里是j接口动态代理的标准写法

     Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler(){});
    

    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.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
    

    api对象其实是一个动态代理对象,类型是接口类型,真正的对象是被动态代理拦截,然后调用Proxy.newProxyInstance方法中的InvocationHandler对象。

    其中invoke方法:

    • Object proxy: 代理对象

    • Method method:调用的方法

    • Object... args:方法的参数

    然后Retrofit就会用Java反射获取到方法的注解信息,配合args参数,创建一个ServiceMethod对象。

    ServiceMethod就像是一个处理器,传入Retrofit对象和Method对象,调用各个接口和解析器,最终生成一个Request,包含api 的域名、path、http请求方法、请求头、是否有body、是否是multipart等等。最后返回一个Call对象,Retrofit2中Call接口的默认实现是OkHttpCall,它默认使用OkHttp3作为底层http请求client

    翻译请求方式和参数,同时构建出ServiceMethod对象

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

    总结来说:使用Java动态代理的目的就要拦截被调用的Java方法,然后解析这个Java方法的注解,最后生成Request由OkHttp发送。

    相关文章

      网友评论

          本文标题:Retrofit2 源码解析

          本文链接:https://www.haomeiwen.com/subject/bqlfdxtx.html