Retrofit2.0源码完全解析

1 Retrofit 和 Okhttp 的关系

现在android端主流的网络框架就是 retrofit + okhttp,那么,它们之间的关系到底是什么呢,其实,真正做网络请求的是okhttp,retrofit只是对它的进一步封装。既然okhttp已经能够实现网络相关操作了,为什么还需要retrofit呢?

很简单,用一个词就能够解释,adapter,adapter的意思是适配器,可以实现各种的转化。比如,你想返回值是一个User类型,但是默认返回值是ResponsBody。这个时候你如果不添加任何的Converter,是会报错的。所以,我们经常需要添加这么一段代码 

addConverterFactory(GsonConverterFactory.create())

这段代码的意思是添加一个转化器,使得我们能够直接使用Gson对Json进行编码和解码。这样,我们就能使用自己需要的类型作为返回值。

更重要的是,它能够和Rxjava完美配合,实现Rxjava的异步功能。需要添加下面的代码

addCallAdapterFactory(RxJava2CallAdapterFactory.create())

这样,你不再是使用okhttp里面的Call和Callback,而是Observal和Observe。为什么转化成Rxjava这么重要呢,因为Rxjava能够很好的实现线程的调度。比如,网络操作是需要在子线程中操作的,而Retrofit的所有操作默认都是在主线程中实现的。然后,返回的结果又需要在主线程中显示。这个是最简单的。只需要切换两次,那复杂一点的呢,比如,网络结果需要先经过一些耗时操作,然后再把操作结果显示到界面上,这理又需要进行一次线程的切换,而使用Rxjava就能够优雅的实现,无论有多复杂的线程切换。

综上所诉,Okhttp是核心,实现相关网络操作。Retrofit是一层包在Okhttp外面的皮,它本身写起来很简单,用注解写一些接口和参数类型就可以,它配合ConverterFactory使解析更容易,它配合CallAdapterFactory使网络操作在线程间切换更方便。

2  Retrofit使用的设计模式—动态代理

面试的时候经常会问到Retrofit相关知识,其中一个必问的就是,Retrofit用的是什么设计模式。动态代理,大部分人基本都能答出来,接着会问,什么是动态代理。其实大家都有一些理解,也知道大概是怎么回事。但是却无法表达出来。那么,到底什么是动态代理呢。

GitHubService service = retrofit.create(GitHubService.class);

点金create查看创建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.adapt(okHttpCall);
      }
    });

这就是动态代理的实现,通过调用Proxy.newProxyInstance()。这个方法需要传三个参数,第一个是ClassLoader,也就是类加载器,第二个是传入一个Class,需要代理的类,这里把GithubService这个接受传进去,第三个最复杂,是InvocationHandler,需要重写它的invoke方法。那么,它具体做了什么事呢。其实很简单,比如,我们的GithubService是这样的。

@GET("users/123/repos")
Call<List<Repo>> listRepos();

 @GET("users/456/repos")
 Call<List<Repo>> listRepos2();

实际上,它会创建GithubService的实例,这这个实例实现了接口中的所有方法。同时,它会创建一个InvocationHandler对象,实现里面的invoke()方法,具体实现是在源码中。然后每一个实现的接口中,比如listRepos()和listRepos2()的具体实现,都会调用InvocationHandler的invoke()方法。当然,中间还需要运用反射来获取方法,因为这些都是在运行中才实现的。运行中获取方法,变量等,都需要用到反射。

3 Retrofit的源码解析

上文中,我们已经知道了动态代理的具体实现,很明显,最关键的代码就是InvocationHandler的invoke()的代码了,因为这里面的代码会被每一个具体的接口调用。接下来,我们看一下InvocationHandler的invoke()方法。

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.adapt(okHttpCall);
      }
    });

前面是两个if判断,第一个if上面有个注释,如果方法是来自Object的方法,则正常调用。意思就是,如果这个方法不是声明在GithubService里面的,那么直接调用这个方法本身,就是做一个简单的判断而已。第二个判断,是判断这个方法是不是默认的方法,因为我们的接口都是不实现,所以也不会走这个方法。所以,最主要的就是下面几行的代码。

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

查看loadServiceMethod(method)的源码,我们会发现里面用到建造者模式,查看它的源码,我们会发现,里面除了做一些判断外,最主要的是parseMethodAnnotation(annotation) 这个方法,这个方法的部分代码如下。

if (annotation instanceof DELETE) {
  parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
  parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
  parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
  if (!Void.class.equals(responseType)) {
    throw methodError("HEAD method must use Void as response type.");
  }
} else if (annotation instanceof PATCH) {
  parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof POST) {
  parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof PUT) {
  parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
} else if (annotation instanceof OPTIONS) {
  parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
}

看到这里,应该恍然大悟,原来这个方法就是对注解进行判断,我们平时写起来那么轻松,用@GET,@POST就可以设置请求方法,那具体是怎么操作的呢,都在这个方法里面。而且逻辑也非常简单,就是用if来判断。另外还有一个重要的方法是parseParameterAnnotation,从名字我们能够大概猜到这个方法的意思,解析参数的注解。果不其然,是对参数进行解析,最常用的就i是@Path,@Body这些。再大概看一下其它的方法,就能够看出loadServiceMethod这个方法的主要作用,就是负责获取我们创建的interface中的所有信息,通过一系列的判断解析这些信息。

下一行是创建okHttpCall的对象,我们看一下这个方法的内部具体做了什么。首先okHttpCall是实现了retrofit的Call接口的一个对象,而里面是先创建一个okhtt3的call,然后对它进行封装。它把上文解析的serviceMethod对象封装进okHttpCall里面,把里面包含的信息创建一个okhttp3的call,并用这个okhttp3的call发起网络请求,然后对结果进行一些处理。

@Override public synchronized Request request() {
  okhttp3.Call call = rawCall;
  if (call != null) {
    return call.request();
  }
  if (creationFailure != null) {
    if (creationFailure instanceof IOException) {
      throw new RuntimeException("Unable to create request.", creationFailure);
    } else if (creationFailure instanceof RuntimeException) {
      throw (RuntimeException) creationFailure;
    } else {
      throw (Error) creationFailure;
    }
  }
  try {
    return (rawCall = createRawCall()).request();
  } catch (RuntimeException | Error e) {
    throwIfFatal(e); // Do not assign a fatal error to creationFailure.
    creationFailure = e;
    throw e;
  } catch (IOException e) {
    creationFailure = e;
    throw new RuntimeException("Unable to create request.", e);
  }
}

最后一行代码是serviceMethod.adapt(okHttpCall),调用的是adapt方法,而adapt一般是和适配器相关的,应该是一些转化。点进去看一下,发现它调用的是 callAdapter.adapt(call)方法,那么,我们先看一下 callAdapter是什么东西。点击它的源码,最终我们看到如下代码

@Override public void enqueue(final Callback<T> callback) {
  checkNotNull(callback, "callback == null");

  delegate.enqueue(new Callback<T>() {
    @Override public void onResponse(Call<T> call, final Response<T> response) {
      callbackExecutor.execute(new Runnable() {
        @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);
        }
      });
    }
  });
}

从这个方法中可以看出,是把callBack的onResponse和onFailure交给callbackExecutor.execute来实现,而这个很明显是进行线程的切换的,把后台线程发起的网络请求在主线程中调用返回结果,实现线程的自动切换。最终我们得出结论,这个adapt方法是和线程切换有关的。当然,它不止把返回结果切换到主线程这么简单,还能和RxJava完美适配。

上述所说的,就是retrofit的主体结构,是由三部分构成,每部分都有自己对应的功能。第一部分是读取接口中的相关信息,第二部分是把相关信息封装进自己的call对象,然后在这个call对象中调用okhttp3的call方法进行网络请求。第三部分是最复杂的,是进行一个切换,也可以说是一个适配,默认帮我们实现的有,把网络请求的结果回调在主线程中,还能完美适配RxJava,使用RxJava的相关功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值