Retrofit流水账

210 阅读8分钟

出来混迟早要还,先记个账吧

马瘦毛长蹄子肥,儿子偷爹不算贼,瞎大爷娶个瞎大奶奶,老两口过了多半辈儿谁也没看见谁!

准备

 val INSTANCE: Retrofit = Retrofit.Builder()
                .baseUrl("http://www.wanandroid.com/")
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .client(httpClient)
                .build()

build()一个Retrofit对象

open interface HomeList {
    @GET("article/list/{pag}/json")
    fun getHomeList(@Path("pag") pag:Int): Observable<HomeBean>
}

准备一个接口

调用

var homeList: HomeList? = retrofit.create(HomeList::class.java)
val subscribe = homeList!!.getHomeList(HomeListState.page)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe( success, failed)

创建代理

为什么接口可以创建对象?为什么接口的方法可以调用? 动态代理登场

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 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

创建某一接口 Foo 的代理:

  InvocationHandler handler = new MyInvocationHandler(...);
      Class proxyClass = Proxy.getProxyClass(
      Foo.class.getClassLoader(), new Class[] { Foo.class });
      Foo f = (Foo) proxyClass.
          getConstructor(new Class[] { InvocationHandler.class }).
          newInstance(new Object[] { handler });

或使用以下更简单的方法:

  Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                          new Class[] { Foo.class },
                                          handler);

动态代理类(dynamic proxy class)(以下简称为代理类(proxy))是一个在运行时动态implements多个接口的类(也就是可以动态创建同时实现了多个接口的对象,同时这个对象还是Proxy类的子类),该类具有下面描述的行为。

代理接口代理类实现的一个接口。 代理实例 是代理类的一个实例。每个代理实例都可以通过实现接口InvocationHandler获得一个关联的调用处理程序(invocation handler) 对象。通过其中某个代理接口的代理实例上的方法调用将被指派到实例的InvocationHandler.invoke方法,并传递代理实例、识别调用方法的 java.lang.reflect.Method 对象以及包含参数的 Object 类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。

代理类具用以下属性:

  • 如果所有代理接口都是public的那么所创建出来的代理类就都是publicfinal的,而且不是abstract的如果有任何一个代理接口不是public的那么所创建出来的代理类就都不是public
    • 原文有两条可以直接翻译成代码,但是简直不是人话
    • Proxy classes are public, final, and not abstract if all proxy interfaces are public.
    • Proxy classes are non-public, final, and not abstract if any of the proxy interfaces is non-public.
  • 代理类在创建前名称没有限制。但是,以字符串 "$Proxy" 开头的类名空间应该为代理类保留。
  • 代理类继承自 java.lang.reflect.Proxy
  • 代理类会按创建时指定的接口的顺序准确地实现每个接口。
  • 如果代理类实现了非public接口,那么它将会被定义在与该接口相同的包中。否则,代理类的包也是未指定的。注意,包密封将不阻止代理类在运行时在特定包中的成功定义,也不会阻止相同类加载器和带有特定签名的包所定义的类。
  • 由于代理类将实现所有在其创建时指定的接口,所以对其 Class 对象调用 getInterfaces 将返回一个包含相同接口列表的数组(按其创建时指定的顺序),对其 Class 对象调用 getMethods 将返回一个包括这些接口中所有方法的 Method 对象的数组,并且调用 getMethod 将会在代理接口中找到期望的一些方法。
  • 如果给 Proxy.isProxyClass 方法传递的代理Class是由 Proxy.getProxyClass 返回的Class,或是由 Proxy.newProxyInstance 返回的对象的Class,则该方法返回 true,否则返回 false
  • 代理类的 java.security.ProtectionDomain 与由引导类加载器加载的系统类相同,好比 java.lang.Object,原因是代理类的代码由受信任的系统代码生成。此保护域通常被授予 java.security.AllPermission
  • 每个代理类都有一个公共构造方法,它接收一个参数实现自接口 InvocationHandler此方法用于设置代理实例的调用处理程序,但是并非必须使用反射 API 才能访问公共构造方法,通过调用 Proxy.newInstance 方法其中利用了 Proxy.getProxyClass 来调用处理程序后调用构造方法,也可以创建代理实例。

代理实例具有以下属性:

  • 给定一个代理实例proxy和一个由其代理类Foo实现的接口之一,下面的表达式将返回true:
    proxy instanceof Foo
    
    并且以下的强制转换操作将会成功(而不抛出 ClassCastException):
    (Foo) proxy
    
  • 每个代理实例都有一个关联的调用处理程序,即传递给它的构造函数的调用处理程序(invocation handler)。静态方法Proxy.getInvocationHandler将返回与代理实例关联的调用处理程序作为其参数。
  • 代理实例上的接口方法调用将被编码,并按照InvocationHandler#invoke方法的文档中所描述的那样,发送到调用处理程序(invocation handler)的InvocationHandler.invoke方法中。
  • 在代理对象中调用hashCodeequalstoString这种在java.lang.Object中声明的方法同样会按照上述规则编码并发送。被声明为Method类型的对象会视作java.lang.Object对象发送给invoke方法。继承自Object的代理实例的其他公共方法不会被代理类覆盖,因此这些方法的调用行为就像它们对java.lang.Object的实例一样。

这贴怎么变成翻译贴了(╯°Д°)╯︵ ┻━┻总之剩下的注释就是说当有多个同名方法的情况下的处理规则实在懒得翻了——斜体字掀桌子更有气势了(  ̄ 3 ̄)y▂ξ

所以Retrofit .create方法其本质在运行时创建一个对象,而这个对象调用接口声明的方法时,会派发给InvocationHandler接口的实现类并调用invoke方法,而参数中的Method method也就是当前调用方法的Method对象,其中会包括方法在声明时所写的很多信息,比如各种注解,不管是方法上的还是参数上的,而用到这些信息的地方:

ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);

就是这句创建了一个ServiceMethod对象,这个类的代码太长了他的主要作用就是根据方法和参数上的注解生成request和response:

 /** Builds an HTTP request from method arguments. */
  Request toRequest(@Nullable Object... args) throws IOException {
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
        contentType, hasBody, isFormEncoded, isMultipart);

    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

    int argumentCount = args != null ? args.length : 0;
    if (argumentCount != handlers.length) {
      throw new IllegalArgumentException("Argument count (" + argumentCount
          + ") doesn't match expected count (" + handlers.length + ")");
    }

    for (int p = 0; p < argumentCount; p++) {
      handlers[p].apply(requestBuilder, args[p]);
    }

    return requestBuilder.build();
  }

  /** Builds a method return value from an HTTP response body. */
  R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

生成回调器

那么这两个方法是在哪里会调用呢OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);这句代码中将serviceMethod对象设置给了OkHttpCall这个类可以说是OkHttp库中Call类的包装类由于都是自家产物两者抽象层的方法末班都是一样的:

Response<T> execute()
void enqueue(Callback responseCallback)
boolean isExecuted()
void cancel()
boolean isCanceled()
Call clone()
Request request()

这些方法中enqueueexecute这些方法都会发起请求这时就会调用createRawCall方法:

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

可以看到此处先调用了serviceMethod.toRequest方法生成request对象并利用此对象生成OkHttp库中的Call对象然后在requestenqueueexecute方法中各种调用OkHttp库的对应的方法,也就是到这一步真正发起了请求。

请求

但是现在这个OkHttpCall对象只是被创建出来其中的逻辑尚未被执行,它被放到了serviceMethod.callAdapter.adapt(okHttpCall);中,而跟踪代码可知callAdapt对象是根据调用方法的返回值类型从最初构建Retrofit对象时.addCallAdapterFactory(RxJava2CallAdapterFactory.create())设置进去的适配器列表中取出的,这也是为什么他可以适配多种回调方式的原因,此时我们才得到了真正的Call根据不同适配器的调用方法执行代码之后实际上会调用到okhttp3.Call这个类中的enqueue或者execute方法此时真正发起请求。

响应

而当请求响应时在OkHttpCall类中会先使用Response<T> parseResponse(okhttp3.Response rawResponse)方法将响应包装成自己的Response在这个过程中如果响应code就会调用serviceMethod.toResponse方法跟踪代码这个方法中也就是利用构建Retrofit对象时.addConverterFactory(GsonConverterFactory.create())设置进去的变换器来解析Response并将结果返回,然后发送给回调接口。

在邻居穿墙而过的歌声中算完了帐,突然觉得没有充QQ音乐的会员真是赚了,本文从头流到尾,应该不适合饭后观看,看久了会晕,晕了会想吐,吐了就会浪费粮食,所以我把提示写在了最后,不要客气,我是红领巾。