Retrofit原理探究[源码解析]

本文分析基于retrofit:2.3.0

1、概述        

        Retrofit对于Android开发人员是一个较熟悉的网络请求框架,其简洁的使用方式,一度备受开发者的喜爱。我在去年年尾的时候的时候,也将Retrofit应用到新项目的开发,以取代原有的网络请求框架,不得不说Retrofit使用起来太方便了,短短的几行代码就可以搞定繁琐的网络请求。

        在刚开始使用Retrofit的时候,我一度很好奇Retrofit是怎么做到如此的简洁的,如何将我们定义的接口转换为一个实际的网络请求的呢?

public interface ApiService {
            @GET("json_it")
            Call<String> test();
        }

        Retrofit retrofit = new Retrofit.Builder()
                .addConverterFactory(ScalarsConverterFactory.create())
                .baseUrl("http://blog.csdn.net/")
                .build();
        Call<String> call = retrofit.create(ApiService.class).test();
        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {

            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {

            }
        });
Retrofit的显著优点如下:

1、可以配置不同HTTP client来实现网络请求,如okhttp、httpclient等;
2、支持同步、异步和RxJava
3、超级解耦等
本篇博文目标:结合源码,探究Retrofit如何将我们定义的接口转换为一个实际的网络请求的并拿到响应结果的。

预备知识:JDK动态代理注解相关OkHttp原理探究Okio原理探究

如果你对以上预备知识都比较熟悉,那么你直接阅读源码将会比较轻松。

2、Retrofit整体执行流程


注意:上图只是简单的表述了Retrofit的整体流程思路,其具体的流程要比上图复杂的多。

a、JDK动态代理:

         与静态代理相比较,动态代理的显著优点就是可以减少开发者自定义的Java文件的数量,因为动态代理为我们生成了所需的Java字节码文件【注意:动态代理是在运行时为我们生成Java字节码文件的,非编译时】。

        JDK动态代理支持接口或类[必须实现某接口]  ----  生成的代理类是Proxy子类,同时会实现我们的接口中的所有方法,关键是我们实现的InvocationHandler,因为我们通过接口[开发者自定义的接口]调用动态代理生成的类中的方法都会被转发给InvocationHandler。这也是为什么我们仅仅定义一个接口文件就可以通过Retrofit去调用接口的原因,Retrofit的核心代码就是其实现的InvocationHandler。

        Retrofit实现了自己的InvocationHandler,并在invoke方法中实现了主要的逻辑,接口方法的主要信息[注解信息、参数信息、方法名等]都包含在了Method参数中。

@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
......}
      因此,我们可以通过反射拿到构建一个OkHttp请求所需的全部信息。

 b、ServiceMethod

     主要有两个方法:toRequest和toResponse。

     toRequest:根据上一步得到的信息构建一个OkHttp请求;

     toResponse:根据我们设置的Converter【比如scalars】返回响应信息。

c、OkHttpCall

      该类是Retrofit封装的OkHttp请求类,其主要作用是根据ServiceMethod的toRequest方法生成的OkHttp Request来生成OkHttp的RealCall。同时,利用ServiceMethod的toResponse方法返回我们需要的响应信息,比如接口返回的JOSN串。很显然ServiceMethod类是OkHttpCall的工具辅助类。

d、CallAdapter

      看到Call肯定就知道是和请求相关

e、Converter

       将响应解析成我们需要的形式,比如如果我们使用了Scalars,则会返回给我们JSON String串.

2.1、源码解析

解析将按照如下三部分进行:
//------------------------------第一部分--------------------------------------
        Retrofit retrofit = new Retrofit.Builder()
                .addConverterFactory(ScalarsConverterFactory.create())
                .baseUrl("http://blog.csdn.net/")
                .build();
//------------------------------第二部分(主要)------------------------------
        Call<String> call = retrofit.create(ApiService.class).test();
//------------------------------第三部分--------------------------------------
        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {

            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {

            }
        });

2.1.1、第一部分(创建Retrofit)

a、new Retrofit.Builder()
    Builder(Platform platform) {
      this.platform = platform;
      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
    }

    public Builder() {
      this(Platform.get());
    }
可以看到,默认为我们添加了内置的Converter,当然通常情况下我们还会自己在加一些满足我们自己需求的Converter,比如Scalars。我们注意到这里有个Platform:

Retrofit针对不同的平台,对部分方法进行了封装。
b、
.addConverterFactory(ScalarsConverterFactory.create())
.baseUrl("http://blog.csdn.net/")
.build();
addConverterFactory,baseUrl都很简单,就是设置Converter和请求的BaseUrl的,主要看下build()方法:
public Retrofit build() {
      if (baseUrl == null) {//必须设置baseUrl,否则抛出异常
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {//默认支持OkHttp
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();//Executor子类,稍后会看一下它的源码
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }
可以看到,build方法主要完成了基础设施的构建  ----  网络请求(OkHttp相关)、辅助类构建(CallAdapter、Converter、Executor)。单独看下callbackExecutor,为什么要单独看呢?因为该类在后面会用于线程切换呦
static class Android extends Platform {
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

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

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

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
  }
Android是Platform的内部类,其内部又包含了MainThreadExecutor内部类,MainThreadExecutor就是我们在前面Retrofit的build方法中设置的callBackExecutor。由于handler在构造函数中传入了MainLooper,因此该Executor实现了切换到UI线程。

2.1.2、第二部分(主要工作部分)

 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.callAdapter.adapt(okHttpCall);
          }
        });
  }
从源码中,我们一眼就看出了Proxy.newInstance,很确定Retrofit使用了JDK的动态代理技术。而create方法返回值就是动态代理为我们生成的Java类,该类是Proxy的子类,同时实现了service中的所有方法。
invoke方法内部的代码是整个Retrofit的核心代码,我们对接口方法(比如test方法)的调用[retrofit.create(ApiService.class).test()],实际都被转发给了InvocationHandler并最终调用了invoke方法的呀。
先解释一下上述的代码,首先判断是不是普通的Java类的动态代理调用,如果是,则直接返回method.invoke(this,args)了。否则,继续往下走。platform.isDefaultMethod这句代码不用管,因为如果是Android平台,该方法永远返回false。最核心的地方来了:
ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
Retrofit的执行全靠这几句代码了。首先通过ServiceMethod构造请求,然后将方法参数和ServiceMethod传递给OkHttpCall,因为OkHttpCall会使用ServiceMethod的toRequest和toResponse方法来构造Request和解析Response响应。 
虽然说就这几句,但这只是表面的,因为背后还是很多代码呢

关键点:ServiceMethod、OkHttpCall、CallAdapter;
a、ServiceMethod

ServiceMethod类是OkHttpCall的工具辅助类,因为前期的准备和后期的响应解析都需要ServiceMethod完成。下面看下其构造过程:
ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method);
这行代码的主要工作的是通过Method构造一个ServiceMethod,看一下loadServiceMethod:
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;
  }
先去缓存查找是否存在,否则通过建造者模式构造一个。简单看下ServiceMethod的构造过程:
Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

    public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      ......
      responseConverter = createResponseConverter();

      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

      ......

      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        ......

        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
        ......

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

      ......

      return new ServiceMethod<>(this);
    }
可以看到ServiceMethod为OkHttp发起网络请求进行了前期的准备工作,包括获取Method上的注解信息、参数的注解信息、创建或获取CallAdapter及Converter[这些CallAdapter、Converter既包括默认创建的,也包括我们在创建Retrofit的时候自己添加的,比如RxJavaCallAdapterFactory/ScalarsConverterFactory]。
在本例中,CallAdapter通过Retrofit的一个工厂类,最终为我们返回了一个实现的内部类:
new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
Converter也是通过Retrofit的工厂类,最终在本例使用的是ScalarsConverterFactory。至于Retrofit是如何查找这些CallAdapter、Converter的,感兴趣的可以去看下源码。
b、OkHttpCall

OkHttpCall是具体OkHttp最近的一层,因为Retrofit是通过OkHttpCall来向OkHttp发起真正的请求。该类的构造很简单:

OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
  }
private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }
看到木有,OkHttpCall就是用ServiceMethod的toRequest来构造Request的,最终调用okhttp3.Call.Factory的newCall方法生成最终的RealCall。

继续看OkHttpCall之enqueue,该方法在后面会用到,此处贴出来,方便后面查看,不想看的可以略过:

 @Override public void enqueue(final Callback<T> callback) {
    ......
    okhttp3.Call call;//真正的OkHttp3.Call终于出现了
    Throwable failure;

    synchronized (this) {
     ......

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall();//关键代码
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

    if (canceled) {
      call.cancel();
    }
   //OkHttp异步请求
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
          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();
        }
      }
    });
  }
c、返回最终可以直接调用的Call
return serviceMethod.callAdapter.adapt(okHttpCall);
serviceMethod.callAdapter我们前面已经知道是啥了,如下:
new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
adapt方法为我们返回了一个ExecutorCallBackCall对象,也就是invoke方法为我们返回了终极Call对象,也就是我们在此处的返回值Call:
 Call<String> call = retrofit.create(ApiService.class).test();
这个对象是Call的子类,有人可能就好奇了,OkHttpCall是Call的子类,怎么又来了一个Call的子类,好晕。其实Retrofit之所以提供这个子类,多了一层,完全是因为这个callbackExecutor,这个callbackExecutor是什么呢?其实就是前面提到的MainThreadExecutor,它可以从执行线程切换到UI线程。ExecutorCallBackCall就是为了在执行OkHttpCall之后,再次切回UI线程的。

2.1.3、第三部分(发送请求)

看下ExecutorCallBackCall的enqueue方法:
 @Override public void enqueue(final Callback<T> callback) {
      checkNotNull(callback, "callback == null");

      delegate.enqueue(new Callback<T>() {//这里的delegate就是okHttpCall
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(new Runnable() {//切回UI线程
            @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);
              }
            }
          });
        }

        @Override public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
    }
delegate.enqueue即调用了OkHttpCall的enqueue方法,其源码在b部分最后。
以上可以看到,最终结果回调的时候就且回了UI线程。

完事!


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值