Retrofit运行方式详解版

概述

   在最近的项目中,由于对于Retrofit仅仅处于用的状态,导致很多代码虽然实现了功能,但是性能和书写上出现了很大问题。在项目上线之后,现在终于有时间可以深入一些源码,通过看源码了解Retrofit的运行方式,文中基本上都是个人的想法,如果有错误,请指出。
   对于需要理解的Retrofit源码,大致需要知道如下几个类:convertFactory,callAdapterFactory,ServiceMethod,OkHttpClient,OkHttpCall,它们的关系基本上可以用下面一张图概述:

这里面很多新的类型的面孔,那么在源码分析中再慢慢说明吧。而对于convertFactory和callAdapterFactory的作用,基本上可以按照下图描述:

可以理解为convertFactory是决定接口返回值的泛型参数,也是http请求的数据最终需要转化的数据类型,而adapterFactory决定了接口的返回值。 这么说的,可能大家还是有些不太明白,那么我们还是一步一步看源码吧。

基本请求方式

  对于大多数使用Retrofit的网络请求,基本上的采取相似的套路,那就是Retrofit封装 + 定义请求接口 + 网络请求。那么我们也这么做一次吧。

Retrofit封装

   基本封装如下:

public class RetrofitUtils {
    private static Retrofit retrofit ;
    public static <T> T createService(Class<T> clazz) {
        if (retrofit == null) {
            synchronized (RetrofitUtils.class) {
                Retrofit.Builder builder = new Retrofit.Builder();
                retrofit = builder.baseUrl(Api.BASE_IP)
                        .addConverterFactory(ScalarsConverterFactory.create())
                        .addConverterFactory(GsonConverterFactory.create())
                        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                        .build();
            }
        }
        return retrofit.create(clazz);
    }
}

定义请求接口

   我们一般定义的请求接口如下,大致的相似套路也是相似的:

public interface Services {
    @GET("/")
    Call<String> getBaiduInfo() ;

    @GET("/userinfo/{uid}")
    Class<User> getUserInfo(@Path("uid") String uid , @Query("token") String token); 

    @GET("/all/bookinfo")
    Observable<List<Book>> getAllBookInfo(@Query("page") int page);
}

简单应用

   写个简单的请求小例子,比如我们使用第一个接口获取baidu首页的信息:

new Thread(){
    @Override
    public void run() {
    final Call<String> info = RetrofitUtils.createService(Services.class).getBaiduInfo();
    info.enqueue(new Callback<String>() {
         @Override
          public void onResponse(Call<String> call, final Response<String> response) {
                 Log.d("response",response.body());
         }
         @Override
         public void onFailure(Call<String> call, Throwable t) {
                        Log.d("tag failure " , t.toString());
                    }
                });
            }
        }.start();

所做的操作基本上就是这种模式,开发中无非是转变成Observable,然后使用Rx操作符实现各种变换,思路大致是一样的。

源码分析

  这次的分析,是根据Retrofit最新源码(17/08/12),版本2.3分析的,贴的代码基本上都是经过精简的代码,去掉了一些不太重要的代码,比如异常处理,空检查等等。如果大家想看实现过程,不妨去下载一下最新的代码尝试阅读。

Retrofit

  下面来分析一下Retrofit到底是怎么运行的。在RetrofitUtils代码中,可以很看出它是由经典的Builder模式构建的:

 builder.baseUrl(Api.BASE_IP)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();

Retrofit.Builder内部分别使用了两个List保存了convertFactory和adapterFactory,那么我们此时记录一下,converterFactories中现在存在ScalarsConverterFactory和GsonConvertFactory两个factory;adapterFactories现在存在一个RxJavaCallAdapterFactory,然后执行Builder.build方法。这个很重要,大家先打个tag。

  这里这个Builder.build需要说明一下,因为它默认了一些东西,有些值得我们重视,build源码精简如下:

okhttp3.Call.Factory callFactory = this.callFactory;   
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);

这里有三点需要注意一下
第一: 这里的callFactory我们没有赋值,因此它就是OkHttpClient(),这个先记住;
第二: 这里的callbackExecutor 我们也没有赋值,因此它也使用默认的callbackExector,而默认的platfrom.defaultCallbackExectuor(),也就是我们的大Android的Handler(Looper.mainLooper()); 这一点大家可以查看一下源码,因为简单我就贴出来了;
第三: adapterFactories除了添加了RxJavaCallAdapterFactory.create()之外,还默认添加了一个platform.defaultCallAdapterFactory(callbackExecutor). 这个先记住就好。
以上三点先做个小笔记,因为后面的解析中也会使用到它们的。

  好了,有了这些参数之后,我们就可以构建了new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,callbackExecutor, validateEagerly). 那么Retorfit的初始化就先告一段落了。

create(final Class<T> clazz)

  这可以说是Retrofit中最重要的一个方法了,方法的量很小,只是使用了动态代理:

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

对于动态代理,这里我就不扯了,涉及到的东西太多了,而我也是个半吊子,我们这里只需要知道它可以返回一个clazz的代理实例就行了. 然后使用这个clazz的实例,调用它的方法,就要使用InvocationHandler#invoke()方法了,这个方法的意义是在代理实例上处理方法并返回相应的结果。本例中invoke方法精简如下:

   @Override 
    public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {

     ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
     OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
     return serviceMethod.callAdapter.adapt(okHttpCall);
   }

  光说理论可能有些找不到北,那么就说说例子吧。 就拿我们的Services接口来说,它存在三个方法:getBaiduInfo,getUserInfo,getAllBookInfo.对于java来说,万物皆为对象,那么这个三个方法在Class类的基础上就是三个Method对象。这里先记一笔,等会还要回来。

在invoke方法中,我们需要认识到一个非常重要的类:ServiceMethod. Retrofit中有个缓存ServiceMethod的Map,这里loadServiceMethod()方法就是通过Method获取ServiceMethod,如果没有则重新创建新的ServiceMethod。

ServiceMethod

  现在我们去看Retrofit.create()方法中动态代理的invoke()中有个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;
  }

这里的serviceMethodCache是Map型对象,是一种很典型的缓存思想,这里就不多说了,主要看new ServiceMethod.Builder<>(this,method).build()到底干了什么。可以清楚看到,ServiceMethod也是采用了Builder模式,源码如下:

public ServiceMethod build() {

      //这里获取合适的callAdapter
      callAdapter = createCallAdapter();
      //获取方法返回值的参数类型
      responseType = callAdapter.responseType();

      //这里获取合适的ConvertAdapter
      responseConverter = createResponseConverter();

      //解析注解
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

      //判断注解的合法性 主要和Http请求中设置有关 比如是否有请求方法 有没有请求体等参数
      //代码省略
        return new ServiceMethod<>(this);
    }

就如同图2中所解释的,callAdapter 和responseConverter 将伴随Retrofit的整个生命周期。这个ServiceMethod通过要使用的代理方法,通过方法的返回值和返回值参数,分别获取相应的合适的callAdapter和responseConverter。那么我们分别来看一下,这些callAdapter和responseConverter到底是怎么获取的。先看createCallAdapter():

 private CallAdapter<T, R> createCallAdapter() {
      Type returnType = method.getGenericReturnType();
      Annotation[] annotations = method.getAnnotations();
      return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
    }

这里的method.getGenericReturnType()是什么意思呢? 举个例子啊:

public Map<String,Double> getMap() {
        return null;
    }

这个getMap(),最为Method,它的getGenericReturnType结果是:

java.util.Map<java.lang.String, java.lang.Double>

也就是我们方法的返回值,而且还带有参数。这个比较高大上啊。这个先理解一下,因为下面会用得到的。

而method.getAnnotations()是返回方法头上的注解,什么@GET,@POST就可以通过这个方法进行获取的。

知道了两个参数的意思,那我们来看看retrofit.callAdapter(returnType,annotations)是怎么操作的吧,在Retrofit它最终会调用nextCallAdapter()方法,具体精简代码如下:

public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,Annotation[] annotations) {
    int start = 0;
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }

   throw RuntimException("not find the correct callAdapter");
}

需要从adapterFactories的List集合中拿数据进行判断,那我们需要看看adapterFactories.get(i).get(returnType,annotations,this)方法了。通过对Retrofit.Builder()的分析,我们知道现在adapterFactories中有两个adapter:RxJavaCallAdapterFactory.create() 和 platform.defaultCallAdapterFactory(callbackExecutor). 那我们先看一下RxJavaCallAdapterFactory.create()中
get(returnType,annotations,retrofit)返回的是什么吧。

为了简单,我们Services中的一个最简单的方法: Call<String> getBaiduInfo(),它的返回值为Call<String> 去看看adapterFactories中的get方法吧:
现在我们需要遍历adapterFactories种的Adapter,对于RxJavaCallAdapterFactory.create():,其中get()的源码为:

@Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    Class<?> rawType = getRawType(returnType);
    boolean isSingle = rawType == Single.class;
    boolean isCompletable = rawType == Completable.class;
    if (rawType != Observable.class && !isSingle && !isCompletable) {
      return null;
    }
    if (isCompletable) {
      return new RxJavaCallAdapter(Void.class, scheduler, isAsync, false, true, false, true);
    }
       return new RxJavaCallAdapter(responseType, scheduler, isAsync, isResult, isBody, isSingle,
        false);
}

这里的getRawType()是调用方法的返回值类型,那么Call<String> getBaiduInfo()的返回值类型就是Call了。那么RxJavaCallAdapterFactory.create().get()返回的是null。那么我们现在只有看一下platform.defaultCallAdapterFactory(callbackExecutor)中get()方法了:

@Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }

    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }
      @Override public Call<Object> adapt(Call<Object> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

由于getRawType(returnType)的返回值是Call.class,对,这就是我们需要CallAdapter了,此时responseType就是Call的参数值String.class,同时还需要注意的是adapt方法,返回的是一个new ExecutorCallbackCall(),这里先记住,回头还需要用到。

  我们到现在分析了ServiceMethid.Builder.build()中 createCallAdapter();同时也知道了responseType是String.class类型的,那么我们现在就需要分析 responseConverter = createResponseConverter();方法了。

  现在来看一看createResponseConverter()方法吧:

private Converter<ResponseBody, T> createResponseConverter() {
      Annotation[] annotations = method.getAnnotations();
      return retrofit.responseBodyConverter(responseType, annotations);
}

尼玛,又是辗转反侧又要向retrofit要东西,好吧,我们看看吧retrofit.responseBodyConverter,这个方法和callAdapter一样,最终也会调用nextResponseBodyConverter方法,代码如下:

public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
    int start = 0;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
      Converter<ResponseBody, ?> converter = converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter<ResponseBody, T>) converter;
      }
    }

  throw new RuntimeException("cannot find correct converter");
}

同样,还是需要找我converterFactories中查找合适的Converter。此时convertFactory中有ScalarsConverterFactory.create()和GsonConverterFactory.create()两个convertFactory,那么先看ScalarsConverterFactory.create():

@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    if (type == String.class) {
      return StringResponseBodyConverter.INSTANCE;
    }
    if (type == Boolean.class || type == boolean.class) {
      return BooleanResponseBodyConverter.INSTANCE;
    }
    ...
    //代码省略
    return null;
  }

此时我们的type是String.class,那我们就需要StringResponseBodyConverter.INSTANC,进去看一下:

static final class StringResponseBodyConverter implements Converter<ResponseBody, String> {
    static final StringResponseBodyConverter INSTANCE = new StringResponseBodyConverter();
    @Override public String convert(ResponseBody value) throws IOException {
      return value.string();
    }
  }

现在到了这里,我们已经知道了callAdapter和responseConverter的最终值了,关于ServiceMethod.Builder.build()也已经完成了【解析注解大家可以看一下,主要是根据不同的注解走不同的分支】,到现在为止,ServiceMethod的初始化完成。

OkHttpCall

  由于ServiceMethod的初始化已经完成,那么现在我们继续回到Retrofit.create()方法中的动态代理中,来看这个行代码:

ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

ServiceMethod我们已经知道是什么了,callAdapter也知道是哪一个了[不知道的往回看,的确是比较卡,因为东西的确是蛮多的]。现在不急于分析上述代码中OkHttpCall是什么东西,咋们先看一下我们的网络请求时怎么写的:

 final Call<String> info = RetrofitUtils.createService(Services.class).getAllInfos();

  info.enqueue(new Callback<String>() {
  @Override
  public void onResponse(Call<String> call, final Response<String> response) {
    Log.d("response",response.body());
  }
 @Override
 public void onFailure(Call<String> call, Throwable t) {
     Log.d("tag failure " , t.toString());
    }
});

我们现在已经知道了RetrofitUtils.createService(Services.class).getAllInfos()是ExecutorCallbackCall(来自platform.defaultCallAdapterFactory),就是serviceMethod.callAdapter.adapt()的返回值:

@Override public Call<Object> adapt(Call<Object> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }

而这个ExecutorCallbackCall的构造函数和enqueue方法为:

ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
   this.callbackExecutor = callbackExecutor;
   this.delegate = delegate;
}

public void enqueue(final Callback<T> callback) {
  delegate.enqueue(new Callback<T>() {
    public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(new Runnable() {
        public void run() {
              if (delegate.isCanceled()) {
                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
              } else {
                callback.onResponse(ExecutorCallbackCall.this, response);
              }
            }
          });
        }
    public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(new Runnable() {
        public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
}

最终我们发现,网络请求是有delegate代理的,而这个delegate是从哪里来的呢?对了,是从serviceMethod.callAdapter.adapt()的参数中传递过来的,那么这个delegate就是OkHttpCall:

ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

那么我们去看一下OkHttpCall的enqueue方法吧:

@Override 
public void enqueue(final Callback<T> callback) {
    okhttp3.Call call;
    Throwable failure;
    synchronized (this) {

   //拼接Ok3的call请求
   call = rawCall = createRawCall();
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response = parseResponse(rawResponse);
        callSuccess(response);
      }
      @Override public void onFailure(okhttp3.Call call, IOException e) {
        callback.onFailure(OkHttpCall.this, e);
      }
      private void callFailure(Throwable e) {
        callback.onFailure(OkHttpCall.this, e);
      }
      private void callSuccess(Response<T> response) {
        callback.onResponse(OkHttpCall.this, response);
      }
    });
  }

这里有几点需要主要的是:
1.所有的请求都只能进行一次,多次进行就会报错;
2.因为Retrofit并未进行真正的网络请求,而是将网络请求外包给了OkHttp3,所以这里面有很多类型的概念,比如说Call,有Retrofit的call,也有Ok3的call,不过还好,Ok3的call都给出了前缀。

我们来看一下拼接ok3的createRawCall()方法:

private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    return call;
  }

它主要做了两件事,使用serviceMethod构建了网络请求的request,还记得我们使用的@GET,@POST,@PATH,@Header,url等网络基本请求都在serviceMethod中解析的吧,这个toRequest就是将各种参数组装,然后变成了Request参数,具体的细节我就不说了,大家有兴趣可以直接去看源码。

而serviceMethod.callFactory.newCall(request)是什么呢? 我们一步一步来看,serviceMethod.callFactory是来自Retorfit的callFactory,来看Retrofit的callFactory的构建过程:

okhttp3.Call.Factory callFactory = this.callFactory;
 if (callFactory == null) {
   callFactory = new OkHttpClient();
 }

是的,我们的callFactory并没有被初始化,因此它就是OkHttpClient(),对的,就是它。那我们就看看OkHttpClient.newCall(request)方法吧:

@Override 
public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
}

那么这个call其实真正起作用的就是RealCall类,回到createRawCall()方法中来,那么将由RealCall执行enqueue方法:

 @Override 
public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

看到client.dispatcher().enqueue(new AsyncCall(responseCallback)),我们可以理解为它在进行网络请求了,这里涉及到了OKHttp3的知识了,具体的内容,可以参考一下这篇文章。这里就不多讲了,到这里来,我们就认为是ok3给我们去进行网络请求了。

回到OkHttpCall的enqueue的方法,请求网络请求之后,结果以okhttp3.Response返回,那我们来看一下:

Response<T> response = parseResponse(rawResponse);

来看一下源码:

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();
  //各种responseCode != 200的情况 我们不考虑这么多 只考虑完全正常的情况
    T body = serviceMethod.toResponse(catchingBody);
    return Response.success(body, rawResponse);
  }

忽略次要的代码,看一下这行:

T body = serviceMethod.toResponse(catchingBody);

而ServiceMethod的toResponse方法为:

 R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

而responseCOnverter是我们已经定义好的StringResponseBodyConverter.INSTANC,那么它的convert方法是:

@Override 
public String convert(ResponseBody value) throws IOException {
      return value.string();
}

直接将我们的ResponseBody对象转化为了String对象。而这个Body最后作为参数进入到了Response.success(body,rawResponse)中,最后被回调到了Callback中。

好了,一次最基本的Retrofit网络请求就这样完成了,不得不说Retrofit设计得比较精巧,初次读起来还是比较难懂,这篇文章前前后后写了6,7个小时,就是因为有的东西忘了就给噎住了,又得重新再来一次。

最后还是来总结一遍内容:Retrofit通过Builder模式构建,通过动态代理方法,在ServiceMethod的帮助下,解析了方法注解,确定了请求方法的返回值和请求的结果最终转化的对象。这个框架本身需要一些Java的高阶知识,比如动态代理,反射方法还有各种接口组合,不过分析一遍之后,还是还是蛮清晰的,值得我们去学习一下,毕竟这货现在github上android类请求上高居榜首,真是不无道理的。

好了,今天的分析就到这里了,文章中可能有些理解不到位的,希望大家啊指出,共同进步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值