复习 Retrofit 的两个小问题

854 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

写惯了 Flutter ,Android 知识还得在熟悉熟悉,就从最简单的三方库开始复习吧,以问问题的方式来回忆 Android,闲话不多说,开始吧!

问题一:Retrofit 是如何组织 Http 协议的

首先,Http 协议是网络传输的协议,包含了请求和响应两部分,详细信息可以看这里 👉HTTP协议超级详解,在Retrofit 用于请求的信息是通过注解标记的,比如:

@GET("users/{user}/repos")
fun listRepos(
  @Path("user") user: String?,
  @HeaderMap headers: Map<String, String>
): Call<List<Repo>>

上面代码反应的信息如下:

  • Http 请求的方法是 GET,所以用不到 body 请求体
  • 路径是 users/{user}/reposuser 的参数是 listRepos 方法 @Path 注解的 user
  • 自定义的请求头,通过 @HeaderMap 注解的 headers 参数设置

这些是我们常用到的信息,那么注解是如何解析的呢?答案是 Method 对象。 我们写的用于请求网络的 Retrofit 接口,仅仅是一个普通的接口,并不具备方法调用的功能,真正的 listRepos 调用是通过动态代理实现的。

动态代理类的创建就是:

val service = retrofit.create(XXX::class.java)

create 方法如下: WeChat52f81bd8c9b82631775deb24ec6cbb45.png

实际返回的代理类是通过标准的动态代理模版创建的,也就是 Proxy.newProxyInstance,这个方法的最后一个参数就是方法处理器,也就是匿名 InvocationHandler

我们调用 listRepos 时,就会调用到匿名类的 invoke 方法中,参数如下:

  • proxy 就是代理对象
  • method 就是 listRepos 方法
  • args 就是 listRepos 方法的参数,我们这里就是 userheaders

会先加载方法,然后调用方法。 从性能考虑,Retrofit 会将已经调用过的方法缓存下来,从而减少一些不必要的解析。加载就是看方法有没有解析过。

WeChat922b8099fe688a3866cd0ce127b336ea.png

我们接着看上面的解析方法:

WeChatd94546c4f3a428c400f02271a2b0097e.png

解析主要是两个箭头处的代码,以第一处代码为例:

WeChat0d234fa2324898443014e3b938fa6805.png

对于请求方法来说,就是硬编码解析。箭头指向的参数的 annotation,就是通过 method.getAnnotation 获得的。那么解析出来的值给谁了呢?那就是 okhttp3Request 类,其他的注解解析相似。

总结起来就是:动态代理拿到方法的 Method 对象,通过 Method 获取所有的注解,然后将解析出来的值一次赋值给构造出来的 Request

问题二:Retrofit 是如何实现子线程请求网络,主线程刷新 UI 的。

val call: Call<List<Repo>> = service.listRepos("octocat")
    
call.enqueue(object : Callback<List<Repo>> { // 子线程请求网络
  override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo>>) {
    // 主线程更新 UI
  }

  override fun onFailure(call: Call<List<Repo>>, t: Throwable) {
  // 主线程更新 UI
  }

})

call 是我们接口返回的,是 retrofit2 包中的一个接口类,所以需要找到实现。上面介绍了实现是动态代理类,最终会走到 HttpServiceMethodinvoke 方法中。

@Override final @Nullable ReturnT invoke(Object[] args) {
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  return adapt(call, args); 第一处
}

HttpServiceMethod 有三个子类,不同的子类,adapt 有不同的逻辑,一般来说,HttpServiceMethod 的子类是 CallAdapted。从名字就知道这玩意是一个适配器模式,而且适配的对象就是参数的call。

WeChate315cdcd3d44c60d74b74c9c730453a5.png

CallAdapted 的 adapt 的关键就两个:callAdapter 是谁,call 是谁。call 好说,就是 OkHttpCallcallAdapter 是谁呢? callAdapter 是在构造方法赋值的,所以就在初始化的地方,经过追踪 callAdapter 就是 HttpServiceMethodcreateCallAdapter 返回值。


private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
    Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
  try {
    return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
  } catch (RuntimeException e) { 
    throw methodError(method, e, "Unable to create call adapter for %s", returnType);
  }
}

我们看直接调用了 retrofitcallAdapter,那就追到 retrofit 去看看。

public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
  return nextCallAdapter(null, returnType, annotations);
}

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

我们看是直接从 callAdapterFactories 集合中获取的,而 callAdapterFactories 集合我们并没有赋值,肯定就是我们构造 retrofit 的时候,框架帮我们赋的值。果然,在 Retrofit build() 中有这么一段代码:

public Retrofit build() {
 List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
  callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
}

就是添加了平台默认的 CallAdapter,即 DefaultCallAdapterFactory get 出来的。 所以看到这里就找到了: CallAdapted 的 adapt 的两个关键:callAdapterDefaultCallAdapterFactory get 出来的。callOkHttpCall

所以我们继续回到我们的主流程, CallAdaptedadapt 方法。

static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
  @Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
    return callAdapter.adapt(call);
  }
}

callAdapter 是 DefaultCallAdapterFactory get 出来的东西。

@Override public @Nullable CallAdapter<?, ?> get(
    Type returnType, Annotation[] annotations, Retrofit retrofit) {
  /// 代码省略
  
  final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
      ? null
      : callbackExecutor;

  return new CallAdapter<Object, Call<?>>() {
    @Override public Type responseType() {
      return responseType;
    }

    @Override public Call<Object> adapt(Call<Object> call) {
      return executor == null
          ? call
          : new ExecutorCallbackCall<>(executor, call); //最终的返回值
    }
  };
}

我们写的:

val call: Call<List<Repo>> = service.listRepos("octocat")

最终的返回值就是 ExecutorCallbackCall<>(executor, call),call 依旧是 OkHttpCall, executor 是谁呢?

DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
  this.callbackExecutor = callbackExecutor;
}

是 DefaultCallAdapterFactory 构造方法设置的,也就是 Android 平台的主线程执行器。

static final class Android extends Platform {
  @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 和 主线程的 Looper,大家就熟悉了。 adapt 的返回值就是主线程, 既然知道了主线程,我们再去找子线程。

val call: Call<List<Repo>> = service.listRepos("octocat")

我们知道了 ExecutorCallbackCall<>(executor, call) 是 service 方法返回的对象类型,并且 executor 是主线程执行器,参数call 是 OkHttpCall。 所以我们看返回类型的 enqueue 逻辑。

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

      });
    }

    public void onFailure(Call<T> call, Throwable t) {
      ExecutorCallbackCall.this.callbackExecutor.execute(() -> {
        callback.onFailure(ExecutorCallbackCall.this, t);
      });
    }
  });
}

这一下子就明白了吧! delegate 是 call,也就是 OkHttpCall, callbackExecutor 是主线程执行器。 OkHttpCall 的 enqueue 是通过线程池实现的子线程请求网络。 这就把两者关联起来了。

总结一下:**Retrofit 使用适配器模式,将 OkHttpCall 和 主线程执行器进行了一次适配,连接了任务的两端。真正发送网络的子线程是 Okhttp 的线程池创建的,回调主线程是通过 Handler + 主线程 Lopper 实现的。 **