美文网首页
Retrofit 源码解析的初次尝试

Retrofit 源码解析的初次尝试

作者: 大豆油 | 来源:发表于2016-11-13 00:39 被阅读31次
    enter image description here

    目录

    • Retrofit 简介
    • Retrofit 的具体使用
    • 重点分析

    Retrofit 简介

    Retrofit 是 Square 推出的 HTTP 框架,主要用于 Android 和 Java。Retrofit 将网络请求变成方法的调用,使用起来非常简洁方便。

    • Retrofit adapts a Java interface to HTTP calls by using annotations on the declared methods to
    • define how requests are made. Create instances using {@linkplain Builder
    • the builder} and pass your interface to {@link #create} to generate an implementation.

    这是源码开头的解释,大致意思是 Retrofit 利用方法上的注解接口转化成一个 HTTP 请求。


    Retrofit 的使用
    1. 首先建立API接口类:
    public interface ZhihuApi {
        String HOST = "http://news-at.zhihu.com/api/";
        @GET("4/news/latest")
        Call<ZhihuList> getZhihuListNews(int page);
    }
    
    1. 具体使用过程:
     // 创建 Retrofit 实例
     Retrofit retrofit = new Retrofit.Builder()
         .baseUrl(ZhihuApi.HOST)
         .addConverterFactory(GsonConverterFactory.create())
         .build();
    
     // 生成接口实现类
     ZhihuApi zhihuApi = retrofit.create(ZhihuApi.class);
    
     // 调用接口定义的请求方法,并且返回 Call 对象
     Call<ZhihuList> call = zhihuApi .getZhihuListNews(1);
    
     // 调用 Call 对象的异步执行方法
     call.enqueue(Callback callback)
    

    以上就是 Retrofit 的基础用法,注意这里的 addConverterFactory() 方法,它是用于吧返回的 http response 转换成 Java 对象,对应方法的返回值就是 ZhihuList 这个自定义的类。大致流程就是获得 retrofit 对象之后,接着调用 create()方法创建 ZhihuApi 的实例,然后调用 ZhihuApi 的方法请求网络,返回的是一个 Call对象,然后就可以使用 Call 方法的 enqueue 或者 execute 来执行发起请求,enqueue 是是异步执行,而 execute是同步执行。


    Retrofit 的重点解析
    1. Retrofit 的创建( Builder 模式)
     // 用于缓存解析出来的方法
      private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();
      // 请求网络的 OKHttp 的工厂,默认是 OkHttpClient
      private final okhttp3.Call.Factory callFactory;
      // baseurl
      private final HttpUrl baseUrl;
      // 请求网络得到的 response 的转换器的集合 默认会加入 BuiltInConverters    
      private final List<Converter.Factory> converterFactories;
      // 把 Call 对象转换成其它类型,如 Call 以及 Observer
      private final List<CallAdapter.Factory> adapterFactories;
      // 用于执行回调 Android 中默认是 MainThreadExecutor
      private final Executor callbackExecutor;
      // 是否需要立即解析接口中的方法
      private final boolean validateEagerly;
    

    以上是几个 Retrofit 中比较关键的变量,每个都有注释,一看便知。接下来看一看 内部类 Builderbuild()方法:

    public Retrofit build() {
          if (baseUrl == null) {
            throw new IllegalStateException("Base URL required.");
          }
    
          okhttp3.Call.Factory callFactory = this.callFactory;
          if (callFactory == null) {
          // 默认创建一个 OkHttpClient
            callFactory = new OkHttpClient();
          }
    
          Executor callbackExecutor = this.callbackExecutor;
          if (callbackExecutor == null) {
          // Android 中返回的是 MainThreadExecutor
            callbackExecutor = platform.defaultCallbackExecutor();
          }
    
          // Make a defensive copy of the adapters and add the default Call adapter.
          //adapterFactories 把 Call 对象转换成其他对象,如 Observer
          List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
          adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
    
          // Make a defensive copy of the converters.
          // 请求网络得到的 response 的转换器的集合
          List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
    
          return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
              callbackExecutor, validateEagerly);
        }
      }
    

    以上代码可以看出:callAdapter 是请求返回的对象,Converter 是转换器,转换请求的响应到对应的实体对象,OkHttpClient 是具体的 OkHttp 的请求客户端,在创建 Retrofit 的时候,如果没有指定 OkHttpClient,会创建一个默认的。如果没有指定 callbackExecutor,会返回平台默认的,在 Android 中是 MainThreadExecutor,并利用这个构建一个 CallAdapter 加入 adapterFactories

    1. Create() 方法
      有了 Retrofit 对象后,便可以通过 create() 方法创建网络请求接口类的对象。直接上代码:
    public <T> T create(final Class<T> service) {
        // 检查传入的类是否为接口并且无继承
        Utils.validateServiceInterface(service);
        if (validateEagerly) {
          // 提前解析方法
          eagerlyValidateMethods(service);
        }
        // 重点是这里
        // 首先会返回一个利用代理实现的 ZhihuApi 对象
        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);
                }
                // 为了兼容 Java8 平台,Android 中不会执行
                if (platform.isDefaultMethod(method)) {
                  return platform.invokeDefaultMethod(method, service, proxy, args);
                }
    
                // 解析方法 这里用到了注解(Runtime)
                ServiceMethod serviceMethod = loadServiceMethod(method);
                // 将刚刚解析完毕包装后的具体方法封装成 OkHttpCall ,你可以在该实现类找到 okhttp 请求所需要的参数
                // 所以它是用来跟 okhttp 对接的。
                OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
                 // 将以上我们封装好的 call 返回给上层,这个时候我们就可以执行 call 的同步方法或者异步进行请求。
                return serviceMethod.callAdapter.adapt(okHttpCall);
              }
            });
      }
    

    这里使用的是 动态代理模式 ,简单的描述就是Proxy.newProxyInstance 根据传进来的 Class 对象生成了代理类,当这个代理类执行某个方法时总是会调用 InvocationHandler(Proxy.newProxyInstance 中的第三个参数) 的invoke方法,在这个方法中可以执行一些操作(这里是解析方法的注解参数等),通过这个方法真正的执行我们编写的接口中的网络请求。我们再来理一下思路:
    Create() -->
    return (T) Proxy.newProxyInstance(...){...}-->
    Call<ZhihuList> call = zhihuApi .getZhihuListNews(1);-->
    public Object invoke(...){...}调用代理类的invoke()
    记住动态代理,是在方法调用时才会真正的触发过程。

    1. ServiceMethod
      下面看一下在 invoke 中解析网络请求方法的几行,ServiceMethod serviceMethod = loadServiceMethod(method);代码如下:
     ServiceMethod loadServiceMethod(Method method) {
        ServiceMethod result;
        synchronized (serviceMethodCache) {
          // 从缓存中获取该方法
          result = serviceMethodCache.get(method);
          if (result == null) {
            // 为空就创建并且加入缓存
            result = new ServiceMethod.Builder(this, method).build();
            serviceMethodCache.put(method, result);
          }
        }
        return result;
      }
    

    Retrofit 有一个双链表用来缓存方法
    private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();避免一个网络请求在多次调用的时候频繁的解析注解,毕竟注解解析过程消耗比较大。这个部分重点在ServiceMethod.Builder(this, method).build();
    build()源码很长,只截取重点:

    // 用来发送请求的 client
    callAdapter = createCallAdapter();
    ...
    // 结果的转换器(Gson,FastJson …)之类
    responseConverter = createResponseConverter();
    
    // 遍历解析注解
    for (Annotation annotation : methodAnnotations) {
       parseMethodAnnotation(annotation);
    }
    ...省略代码
    
    1. OkHttpCall

    在得到 ServiceMethod 对象后,把它连同方法调用的相关参数传给了 OkHttpCall 对象,就是前面的这行代码:
    OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);

    这个OkHttpCall才是真正OkHttp请求的回调,但是针对我们使用的不同的回调,比如:RxJava的Observable、Call,所以有一层转换的关系,把OkHttpCall转成对应的Observable和Call。就是serviceMethod.callAdapter.adapt(okHttpCall)的工作。

    请求方法参数,请求客户端,返回值转换,我们都定义好了之后,便完成最后一步,构建好适合请求客户端的请求方法,Retrofit 默认的是 okhttpCall 。接下来把 OkHttpCall 传给 serviceMethod.callAdapter 对象

    那么CallAdapter是干嘛的呢?上面调用了adapt方法,它是为了把一个Call转换成另一种类型,比如当 Retrofit 和 RxJava 结合使用的时候,接口中方法可以返回 Observable<T>,这里相当于适配器模式。默认情况下得到的是一个 Call对象,它是ExecutorCallbackCall,代码如下:

    @Override
      public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        if (getRawType(returnType) != Call.class) {
          return null;
        }
        final Type responseType = Utils.getCallResponseType(returnType);
        return new CallAdapter<Call<?>>() {
          @Override public Type responseType() {
            return responseType;
          }
    
          @Override public <R> Call<R> adapt(Call<R> call) {
            return new ExecutorCallbackCall<>(callbackExecutor, call);
          }
        };
      }
    

    以上代码是在ExecutorCallAdapterFactory类中,这个类继承自CallAdapter.Factory,可以看到adapt()方法接受一个Call对象,看下ExecutorCallbackCall的部分源码:

    static final class ExecutorCallbackCall<T> implements Call<T> {
        final Executor callbackExecutor;
        final Call<T> delegate;
    
        ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
          this.callbackExecutor = callbackExecutor;
          this.delegate = delegate;
        }
    
        @Override public void enqueue(final Callback<T> callback) {
          if (callback == null) throw new NullPointerException("callback == null");
    
          delegate.enqueue(new Callback<T>() {
            @Override public void onResponse(Call<T> call, final Response<T> response) {
              callbackExecutor.execute(new Runnable() {
                @Override public void run() {
                  if (delegate.isCanceled()) {
                    // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
                    callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                  } else {
                    callback.onResponse(ExecutorCallbackCall.this, response);
                  }
                }
              });
            }
            ...省略
      }
    

    enqueue 方法中,调用了 OkHttpCallenqueue,所以这里相当于静态的代理模式。OkHttpCall 中的 enqueue其实又调用了原生的OkHttp中的 enqueue,这里才真正发出了网络请求,部分代码如下:

    @Override public void enqueue(final Callback<T> callback) {
        if (callback == null) throw new NullPointerException("callback == null");
        //真正请求网络的 call
        okhttp3.Call call;
        Throwable failure;
    
        synchronized (this) {
          if (executed) throw new IllegalStateException("Already executed.");
          executed = true;
          //省略了部分发代码
          ...
          call = rawCall;
          //enqueue 异步执行
        call.enqueue(new okhttp3.Callback() {
          @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
              throws IOException {
            Response<T> response;
            try {
            //解析数据 会用到 conveterFactory,把 response 转换为对应 Java 类型
              response = parseResponse(rawResponse);
            } catch (Throwable e) {
              callFailure(e);
              return;
            }
            callSuccess(response);
          }
    
          @Override public void onFailure(okhttp3.Call call, IOException e) {
            try {
              callback.onFailure(OkHttpCall.this, e);
            } catch (Throwable t) {
              t.printStackTrace();
            }
          }
    
      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    
      private void callSuccess(Response<T> response) {
        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
     });
    }
    

    OkHttp 获取数据后,解析数据并回调callback响应的方法,一次网络请求便完成了。

    相关文章

      网友评论

          本文标题:Retrofit 源码解析的初次尝试

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