retrofit

设计模式

动态代理

动态代理的原理主要是在运行时动态生成代理类,然后根据代理类生成一个代理对象,在这个代理对象的方法中中又会调用InvocationHandler的invoke来转发对方法的处理。那么大家一定要关注一个细节,我们在使用retrofit的时候,对每一个网络请求的产生都必须要先调用create函数,也就是意味着,我们的请求都是通过代理类来进行处理的。但是代理类具体的代理行为是发生在哪里呢?很显然,他并不是在create函数执行的时候,而是在使用具体的接口创建具体网络请求Call的时候,当调用具体网络请求Call的代码示例如下:

适配器模式

适配器模式在此发挥了其应用的作用!!!
将网络请求的核心类OkHttpCall进行适配,你需要什么类型的数据就通过适配器适配,返回适配后的对象就是了。
正是这种CallApdate接口的设计,使得我们在使用Retrofit的时候可以自定义我们想要的返回类型。此接口的设计也
为RxJava的扩展使用做了很好的基础!!!
CallAdapter: 请求Call的适配,有okhttp,kotlin suspend,rxjavaCallAdapterFactory这几个方面的。

时序图

在进行网络请求的执行的时候,基本上就是调用,ServiceMethod中设置的各个内容如 :

  1. OkHttpCall进行网络请求,实则是进行okhttp的网络请求;
  2. 利用 converter进行网络请求数据的转换,一般是Gson();
  3. 利用 rxjava observable构建 rxjava类型的责任链访问方案,并进行线程切换;
  4. 如果没有rxjava的添加,那么就使用默认的callAdapter里面的callbackExecutor进行线程的切换, 进行网络请求.


    retrofit时序图

Okhttp给用户留下的问题

1)用户网络请求的接口配置繁琐,尤其是需要配置请求body,请求头,参数的时候;
2)数据解析过程需要用户手动拿到responsbody进行解析,不能复用;
3)无法适配自动进行线程的切换。
那么这几个问题谁来解决? 对,retrofit!

okhttp与retrofit的对比

在上图中,我们看到的对比最大的区别是什么?
0)okhttp创建的是OkhttpClient,然而retrofit创建的是 Retrofit实例
1)构建蓝色的Requet的方案,retrofit是通过注解来进行的适配
2)配置Call的过程中,retrofit是利用Adapter适配的Okhttp 的Call
3)相对okhttp,retrofit会对responseBody进行 自动的Gson解析
4)相对okhttp,retrofit会自动的完成线程的切换。

Retrofit的构建过程

Retrofit通过build模式来生成一个Retrofit对象,通过代码我们知道,Retrofit默认会使用OkHttp来发送网络请求,当然,我们也可以自己定制。
「看下面的代码」

public Retrofit build() {
     if (baseUrl == null) {
       throw new IllegalStateException("Base URL required.");
      }
    // 代码1
   //默认只支持okhttp请求,不支持 httpurlconnection 和 httpclient
     okhttp3.Call.Factory callFactory = this.callFactory;
     if (callFactory == null) {  
       callFactory = new OkHttpClient();
// 代码2
    // 添加一个线程管理 Executor,okhttp 切换线程需要手动操作,但是retrofit
     // 不需要,就是因为这个Executor 的存在,其实他是handler
     Executor callbackExecutor = this.callbackExecutor;
     if (callbackExecutor == null) {
       callbackExecutor = platform.defaultCallbackExecutor();
 //代码3        
    // 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, unmodifiableList(converterFactories),unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}

1:在代码 1 处
初始化 构建call 的工厂,但是这个地方直接就是使用了 okhttp的call,没有使用到工厂设计模式去添加构建httpclient 或者 httpurlconnection的方法来创建 call,说明retrofit 已经铁下心只支持okhttp创建call请求了。
那么call 是什么的抽象呢?
OkHttpClient是 http 请求的载体包含socket等可以复用的对象,协议配置等等一切。
Request 创建的是一个具体的有url,header,等请求信息的一个网络请求,表示这个具体的请求。
Call 通往请求的,去执行请求的整个过程的一个抽象。也是进行网络请求的最终接口。
所以,此次调用,目的就是创建了一个OkHttpClient,换句话说,这里的调用就是生产 Okhttp网络请求需要的请
求Call的,以备后面进行真正的网络请求。
2:在代码2处
网络请求需要在子线程中执行,那么就需要线程管理,所以就有了代码2的存在,深入源码后发现,这个地方就是运
用handler进行线程切换,当网络请求回来了进行线程切换,可以看下面的源码

static final class Android extends Platform {
   Android() {
     super(Build.VERSION.SDK_INT >= 24);
@Override public Executor defaultCallbackExecutor() {
     return new MainThreadExecutor();
static class MainThreadExecutor implements Executor {
     private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
       handler.post(r);
}

所以,此次调用,目的是构建一个用handler封装的Executor,以备后面进行网络请求成功后的线程切换用
3:在代码3处
设置默认CallAdapterFactory
在此添加的CallAdapterFactory属于系统默认的,当然,我们可以添加RxJavaCallAdapterFactory。默认的
CallAdapterFactory是 ExecutorCallAdapterFactory 类的对象,在Platform.java Class里面可以梳理出来

defaultCallAdapterFactory(Executor callbackExecutor) {
     return new ExecutorCallAdapterFactory(callbackExecutor);
}

所以构建的Retrofit都是用于进行后面请求的需要的内容的一个准备工作。也就是封装Okhttp需要的准备工作。

Retrofit构建 IxxxService 对象的过程

(Retrofit.create())
看下面的代码:

ISharedListService sharedListService =  retrofit.create(ISharedListService.class);
Call<SharedListBean> sharedListCall = sharedListService.getSharedList(2,1);

上面两行代码需要连起来才能正确的被阅读,因为,在create里面是使用了动态代理的技术方案,而动态代理是的,当我们看到看到create的时候只create的代码如下:

 public <T> T create(final Class<T> service) {
    validateServiceInterface(service);
// 通过动态代理的方式生成具体的网络请求实体对象
    return (T)
        Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[] {service},
            new InvocationHandler() {// 统一处理所有的请求方法
              private final Platform platform = Platform.get();
              private final Object[] emptyArgs = new Object[0];

              @Override
              public @Nullable 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);
                }
                args = args != null ? args : emptyArgs;
// 根据方法生成一个ServiceMethod对象(内部会将生成的ServiceMethod放入在缓存中,
           //如果已经生成过则直接从缓存中获取)
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
            });
  }

ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);//缓存
      if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

1)Retrofit的create方法通过动态代理的模式,生成了实现了具体的网络请求接口的对象,并在InvocationHandler的invoke方法中统一处理网络请求接口实体对象的方法;
2)invoke方法会通过方法构造一个ServiceMethod对象,并将其放入缓存中;
3)然后根据ServiceMethod对象和网络请求的参数args去构造一个OkHttpCall对象;
4)最后调用serviceMethod的callAdapter的adapt方法,传入将OkHttpCall对象,callAdapter的目的主要是为了适配OkHttpCall对象,其内部会对OkHttpCall对象进行包装,生成对应返回类型的对象。

Call的适配

Call<SharedListBean> sharedListCall = sharedListService.getSharedList(2,1);

在执行上面的代码的时候,它会走代理设计模式的InvocationHandler里面的invoke()函数,也就是所有的网络请求在创建具体网络请求Call的时候,都会走Invoke,从而我们可以在invoke里面进行各种行为的统一处理,比如:接口的统一配置,也就是注解的解读和网络请求参数的拼接。

ServiceMethod

每一个method 都有一个自己的ServiceMethod,这就意味着ServiceMethod是属于函数的,而不是类的。也就是我
们定义的网络访问接口类,在接口类里面的每一个函数都会在反射阶段形成自己的serviceMethod。那么
ServiceMethod是什么呢?
ServiceMethod其实是用来存储一次网络请求的基本信息的,比如Host、URL、请求方法等,同时ServiceMethod还
会存储用来适配OkHttpCall对象的CallAdpater。ServiceMethod的build方法会解读传入的Method,首先
ServiceMethod会在CallAdpaterFactory列表中寻找合适的CallAdapter来包装OkHttpCall对象,这一步主要是根据
Method的返回参数来匹配的,比如如果方法的返回参数是Call对象,那么ServiceMethod就会使用默认的
CallAdpaterFactory来生成CallAdpater,而如果返回对象是RxJava的Obserable对象,则会使用
RxJavaCallAdapterFactory提供的CallAdpater。然后build方法会解读Method的注解,来获得注解上配置的网络请
求信息,比如请求方法、URL、Header等

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;

    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    if (isKotlinSuspendFunction) {
      Type[] parameterTypes = method.getGenericParameterTypes();
      Type responseType =
          Utils.getParameterLowerBound(
              0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
      if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
        // Unwrap the actual body type from Response<T>.
        responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
        continuationWantsResponse = true;
      } else {
        // TODO figure out if type is nullable or not
        // Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
        // Find the entry for method
        // Determine if return type is nullable or not
      }

      adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
      annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    } else {
      adapterType = method.getGenericReturnType();
    }

    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    if (responseType == okhttp3.Response.class) {
      throw methodError(
          method,
          "'"
              + getRawType(responseType).getName()
              + "' is not a valid response body type. Did you mean ResponseBody?");
    }
    if (responseType == Response.class) {
      throw methodError(method, "Response must include generic type (e.g., Response<String>)");
    }
    // TODO support Unit for Kotlin?
    if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
      throw methodError(method, "HEAD method must use Void as response type.");
    }

    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>)
          new SuspendForResponse<>(
              requestFactory,
              callFactory,
              responseConverter,
              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>)
          new SuspendForBody<>(
              requestFactory,
              callFactory,
              responseConverter,
              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
              continuationBodyNullable);
    }
  }

Converter

作用:转换请求体和响应体。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容