Retrofit2.0源码分析

转载请注明出处:http://blog.csdn.net/p892848153/article/details/50890500

整体介绍

最近注解式的框架非常火,注解以其轻量,简洁等特性被人们所喜爱者,关键是它解藕。网络请求的框架非常多,比较受欢迎的当属retrofit和okHttp了。连retrofit都是基于okHttp之上开发的。ok, 言归正传,我们来聊聊retrofit。我们假设读者都对okHttp有一定的了解!

先简单说下retrofit的用法,如果不懂,可以上网查查就知道了,当然最好还是看官方的文档。
public interface GitHubService {
   @GET("users/{user}/repos")
   Call<List<Repo>> listRepos(@Path("user") String user);
}
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .build();
GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");
上面的demo是来自官方文档的一小段,还有最后一步,上面没体现出来,那就是调用Call的enqueue或者execute方法实现真正的发起请求。这里要注意以下Call这个类,它不是okHttp里面的Call类,是Retrofit自己的类。
看上面demo可以发现,就是写一个接口,然后接口里面有写标签,再实例化一个retrofit对象,再然后就是关键所在了。 我们看下retrofit.create()方法。
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, 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);
            }
            return loadMethodHandler(method).invoke(args);
          }
        });
  }

  private void eagerlyValidateMethods(Class<?> service) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
      if (!platform.isDefaultMethod(method)) {
        loadMethodHandler(method);
      }
    }
  }

  MethodHandler loadMethodHandler(Method method) {
    MethodHandler handler;
    synchronized (methodHandlerCache) {
      handler = methodHandlerCache.get(method);
      if (handler == null) {
        handler = MethodHandler.create(this, method);
        methodHandlerCache.put(method, handler);
      }
    }
    return handler;
  }
可以发现它用到了动态代理,这里我们假设读者都懂java里面的动态代理。现在知道那个接口是干嘛用的了吧,至于上面的标签,无非就是提供一些信息,没什么神奇的。好的,回到create方法里面的代理,发现代理最后会调用MethodHandler的invoke方法,并且这个MenthodHandler还缓存在一个Map中。所以retrofit的网络请求带有缓存功能,如果两次调用同一个网络请求,第二次请求会快一些,少了实例化与解析注解的时间。还有就是你调用接口中的方法其实到最后都是委托给动态代理的Proxy.invoke()方法,该方法又会调用MenthodHandler.invoke()方法。
ok,这条线知道到这里就行了,以后再详解,再来看下MethodHandler.create方法是如何创建MethodHandler对象的。
static MethodHandler create(Retrofit retrofit, Method method) {
    CallAdapter<?> callAdapter = createCallAdapter(method, retrofit);
    Type responseType = callAdapter.responseType();
    if (responseType == Response.class || responseType == okhttp3.Response.class) {
      throw Utils.methodError(method, "'"
          + Types.getRawType(responseType).getName()
          + "' is not a valid response body type. Did you mean ResponseBody?");
    }
    Converter<ResponseBody, ?> responseConverter =
        createResponseConverter(method, retrofit, responseType);
    RequestFactory requestFactory = RequestFactoryParser.parse(method, responseType, retrofit);
    return new MethodHandler(retrofit.callFactory(), requestFactory, callAdapter,
        responseConverter);
  }
首先该方法会得到一个CallAdapter<?>对象,一般情况下,默认的到的会是由DefaultCallAdapterFactoty对象创建的CallAdapter对象,但是很不巧,Android平台更改了该工厂,返回的是ExecutorCallAdapterFactory对象。对CallAdapter的具体分析,以后再说。我们接着说MethodHandler.create方法。再然后是验证返回类型,再然后得到一个Converter<?, ?>对象,默认的到的是由BuiltinConverters创建的Converter对象,再然后由RequestFactoryParser.parse()方法解析注解标签了。最后就new 了一个MethodHandler对象返回。
至于Converter<?, ?> 是什么、有什么用、怎么用,CallAdapter是什么、有什么用、怎么用,RequestFactoryParser如何解析注解标签、所有注解标签的用法,我们会分三个段落来分别分析讲解。

我们知道Converter是由Converter.Factory的工厂方法生成的。我们看下Converter.Factory

abstract class Factory {
    
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }

    public Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
      return null;
    }

    public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }
  }
可以看到该类中有三个方法,第一个方法responseBodyConverter()的作用是返回一个Converter<ResponseBody, ?>,该Converter的作用是将ResponseBody转换成一个你想要的类,具体要转换成什么,看你的实现了。第二个requestBodyConverter()方法同理,返回一个可以将一个实体类转换成RequestBody的Converter, 最后一个方法的作用很明显是返回一个可以将类转换成String的Converter。在这里要注意一下,RequestBody和ResponseBody类是okhttp里面的类,不是Retrofit里面的。稍后会分析一下GsonConverterFactory是如何实现工作的。

Converter<?, String>和Converter<?, RequestBody>

通过上篇,我们知道Retrofit默认返回的Converter.Factory是BuiltInConverters类,该类根据返回类型会返回各种各样的Converter,不过他们基本实现都很简单。那么问题来了,Converter将一个特定的类转换成RequestBody进行网络传输的方法在什么时候,在哪里会被使用到呢?

回到MethodHandler.create()方法,该方法里面有个RequestFactoryParser.parse()来解析标签,我们进入该方法。好吧,这么说有点啰嗦,以后在说到从哪个方法进入到哪个方法都用'->'表示了。我们跟着方法来:RequestFactoryParser.parse() -> RequestFactoryparser.parseParameters()在该方法中.

private void parseParameters(Retrofit retrofit, Annotation[] methodAnnotations) {
......
} else if (parameterAnnotation instanceof Path) {
......
Converter<?, String> converter =
                retrofit.stringConverter(parameterType, parameterAnnotations);
            action = new RequestAction.Path<>(name, converter, path.encoded());
}
......
} else if (parameterAnnotation instanceof Part) {
......
Converter<?, RequestBody> converter =
                  retrofit.requestBodyConverter(iterableType, parameterAnnotations,
                      methodAnnotations);
              action = new RequestAction.Part<>(headers, converter).iterable();
......

会发现在解析Path, Query等标签的时候会得到一个Converter<?, String>对象,然后传递给RequestAction,而在解析Part, PartMap等标签时会得到一个Converter<?, RequestBody>对象,然后传递给RequestAction,我们进RequestAction查看,发现只有一个perform()方法需要实现,还有两个方法,它们的调用最终都是要回到perform()方法。在RequestAction中,有各种各样的实现,比如RequestAction.Path。各个标签的解析都会生成一个RequestAction对象,保存在数组中,然后在RequestFactoryParser.toRequestFactory()方法中将数据都保存在RequestFactory中。然后将CallAdapter, Converter, RequestFactory都传递给MethodHandler中,最后在Retrofit.create中调用了MethodHandler.invoke方法。那问题是perform方法是怎么被调用的呢?

说来话长,我就长话短说。回到MethodHandler.invoke()方法

Object invoke(Object... args) {
    return callAdapter.adapt(
        new OkHttpCall<>(callFactory, requestFactory, args, responseConverter));
  }

上篇我们说到MethodHandler.create中的CallAdapter是ExecutorCallAdapterFactory.get()得到的,在该方法中找到默认的CallAdapter对象

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

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

知道CallAdapter.adapt()方法其实返回一个ExecutorCallbackCall对象。所以在我们最后真正发起网络请求的时候,比如调用Call.enqueue()其实是调用ExecutorCallbackCall.equeue()方法,该方法又会调用OkHttpCall.enqueue()。ok, 看下OkHttpCall.enqueue()

@Override public void enqueue(final Callback<T> callback) {
    okhttp3.Call call;
......
call = rawCall = createRawCall();
......
}
private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }
看到requestFactory.create()了吧,
Request create(Object... args) throws IOException {
    RequestBuilder requestBuilder =
        new RequestBuilder(method, baseUrl.url(), relativeUrl, headers, contentType, hasBody,
            isFormEncoded, isMultipart);

    if (args != null) {
      RequestAction[] actions = requestActions;
      if (actions.length != args.length) {
        throw new IllegalArgumentException("Argument count ("
            + args.length
            + ") doesn't match action count ("
            + actions.length
            + ")");
      }
      for (int i = 0, count = args.length; i < count; i++) {
        actions[i].perform(requestBuilder, args[i]);
      }
    }

    return requestBuilder.build();
  }
现在看到RequestAction.perform()方法的调用了吧,而这个args就是你接口中方法的参数,它跟标签是一一对应的。也就是说接口中方法的参数每个都要配上标签。ok,绕了一圈,终于回到了Converter来了,我们看下Path.perform();
@Override void perform(RequestBuilder builder, T value) throws IOException {
      if (value == null) {
        throw new IllegalArgumentException(
            "Path parameter \"" + name + "\" value must not be null.");
      }
      builder.addPathParam(name, valueConverter.convert(value), encoded);
    }
Converter.convert()方法将实体类转换成String,然后交给RequestBuilder.addPathParam()去处理。总之不同的标签最后都大概类似的处理,都交给RequestBuilder的不同方法处理,然后在RequestBuilder.build()方法中根据这些数据生成一个okhttp的Request对象。到此,Converter<?, String>和Converter<?, RequestBody>是什么,怎么用都清楚了吧。

Converter<ResponseBody, ?>

再来看看Converter<ResponseBody, ?>是什么鬼,怎么用?上面说到网络请求实际上是OkHttpCall在发起。比如enqueue()方法。我们再回到OkHttpCall.enqueue()方法

@Override public void enqueue(final Callback<T> callback) {
    okhttp3.Call call;
......
 call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }
......
}
可以看到真正发起请求的是okhttp3.Call.enqueue()方法,返回成功时会调用parseResponse()方法
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
......
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
      T body = responseConverter.convert(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      // If the underlying source threw an exception, propagate that rather than indicating it was
      // a runtime exception.
      catchingBody.throwIfCaught();
      throw e;
    }
  }
看到Converter<ResponseBody, ?>.convert方法了吧,被转化得到的T body被至于Response<T>中,然后通过回调Callback.onResponse(OkHttpCall, Response<T>)返回。那Converter<ResponseBody, ?>对象是在何时传递进来的呢。我们回到MethodHandler.create()方法,就是在这里传递进去的。Converter<ResponseBody, ?>也是通过Converter.Factory.responseBodyConverter()方法返回的。好了,到这里大家都请求Converter类是怎么工作的了吧。本来想说一下GsonConverterFactory的,不过看了下,没几行,就不聊了。我相信现在大家应该都会自定义Converter.Factory了吧,然后调用Retrofit.Builder.addConverterFactory就可以了。

现在来说说CallAdapter在retrofit中的作用,相信弄懂这些,自定义CallAdapter就不难了。也能对理解RxAndroid配合retrofit使用有帮助。

CallAdapter

好的,言归正传。首先我们看下MethodHandler.create()方法,在该方法中会得到一个CallAdapter对象,跟着方法找MethodHandler.create() -> MethodHandler.createCallAdapter() -> Retrofit.callAdapter() -> Retrofit.nextCallAdapter()

public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");

    int start = adapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
......
}
看到这里,我们知道CallAdapter是由CallAdapter.Factory.get()返回的。我们找到android系统默认给我们的Factory:ExecutorCallAdapterFactory类。看下ExecutorCallAdapterFactory.get()是如何返回一个CallAdapter对象的。

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

      @Override public <R> Call<R> adapt(Call<R> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }
ok, 现在知道系统返回一个匿名类给我们, 关键点是它的adapt()方法。从 Retrofit2.0源码分析(二)Converter 我们知道哪里会调用到adapt() 方法,就在MethodHandler.invoke()方法里面,CallAdapter的作用就在这体现出来了。adapt()方法接收一个OkHttpCall对象,返回一个ExecutorCallbackCall对象。到这里要说下这个设计非常好,OkHttpCall跟okhttp3包的耦合性很高。ExecutorCallbackCall跟OkHttpCall一样实现Call<T>,ExcutorCallbackCall还包含OkHttpCall对象,看起来就是就是一个代理模式。

在ExecutorCallbackCall中可以对OkHttpCall进行扩展,做一些你想做的事。所以如果你想做一些系统不提供的功能,可以自己实现一个CallAdapter.Factory, 一个CallAdapter,一个Call<T>,当然目前看来,这个Call<T>还是得包含一个OkHttpCall对象。

那么系统提供的ExecutorCallbackCall有什么用呢,它的作用就是把你的回调方法弄到主线程中去,保证你可以在回调Callback的方法中更新UI。说到这里还没完,CallAdapter不是还有一个responseType()方法么,看注释的意思是该方法返回的Type在将HTTP response body 转换成一个Java object的时候会用到,而且不是Method.getGenericReturnType()返回的Type。看的我有点晕,总之这个Type = retrofit.Utils.getCallResponseType(Method.getGenericReturnType());希望有高手懂的,帮忙讲清楚这个Type。

现在来对retrofit的所有注解标签做一遍解释。首先是方法上的标签,然后是参数上的标签。

Method Annotation

DELETE,GET,HEAD,PATCH,POST,PUT,OPTIONS,HTTP :代表不同的网络访问,它的值是相对地址(relativeUrl),可以包含'?'字符,'?'字符后面跟key=value&key=value...但是不可以出现“{var}”这种在地址中代表变量的字符。

比如@GET("users/{user}/repos") 对

@POST("users/{user}/repos?password=123456") 对

        @HEAD("users/{user}/repos?password={pwd}") 错, '?' 后面不能出现"{var}"类型字符

Headers :它的值是一个字符串数组,然后会将值解析后构造出一个okhttp3.Headers对象。

private okhttp3.Headers parseHeaders(String[] headers) {
    okhttp3.Headers.Builder builder = new okhttp3.Headers.Builder();
    for (String header : headers) {
      int colon = header.indexOf(':');
      if (colon == -1 || colon == 0 || colon == header.length() - 1) {
        throw methodError(method,
            "@Headers value must be in the form \"Name: Value\". Found: \"%s\"", header);
      }
      String headerName = header.substring(0, colon);
      String headerValue = header.substring(colon + 1).trim();
      if ("Content-Type".equalsIgnoreCase(headerName)) {
        contentType = MediaType.parse(headerValue);
      } else {
        builder.add(headerName, headerValue);
      }
    }
    return builder.build();
  }
数组中的每个字符串都至少有一个  ' : '  字符,且该字符第一次出现的位置不能是首位。

Multipart, FormUrlEncoded: 这两个标签起标记作用,没有值。

引用两句话解释下这两个标签的意思

application/x-www-form-urlencoded :窗体数据被编码为名称/值对。这是标准的编码格式。

multipart/form-data : 窗体数据被编码为一条消息,页上的每个控件对应消息中的一个部分。

Parameter Annotation

@Url: 用来设置relativeUrl的。不能出现多个, 不能跟@Path标签一起用,不能在@Query标签后面出现,如果方法头部标明了relativeUrl,就不能再使用这个了。该标签后面可以跟着String, java.net.URI, Class<android.net.Uri>三种类型的参数。

@Path : 用来给relativeUrl中的"{var}"赋值的,不能赋值null。不能在@Query标签后面出现, 不能跟@Url标签一起用,必须跟relativeUrl一起使用,它的值必须符合正则表达式"[a-zA-Z][a-zA-Z0-9_-]*",它的值必须是relativeUrl中以"{var}"形式出现过的var。
@Query:用来表示传给服务器的key=value。它修饰的参数值可以是Iterable(或者子类), Array或者其他的什么

@QueryMap : 用来表示传给服务器的多个key=value。值只能是Map类型或者其子类, Map的内容必须包含一般类型(e.g., Map<String, String>), Map的key必须是String
@Header : 用来给请求头赋值的。值会传给okhttp3.Headers。它修饰的参数值的取值跟@Query一样。
@Field :用来表示传给服务器的key=value。只有在有@FormUrlEncoded标签时才能使用此标签。它修饰的参数值的取值跟@Query一样
@FieldMap: 用来表示传给服务器的多个key=value。只有在有@FormUrlEncoded标签时才能使用此标签。它修饰的参数值的取值跟@QueryMap一样
@Part : 标签的值是key, 标签修饰的参数值是要传递给服务器的数据。只有在有@Multipart标签时才能使用此标签。它修饰的参数值的取值跟@Query一样,使用此标签时会创建一个okhttp3.Headers以供使用
okhttp3.Headers headers = okhttp2.Headers.of("Content-Disposition", "form-data; name=\"") + part.value() + "\"",
"Content-Transfer-Encoding", part.encoding());
看下官方给的Demo:
@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
@PartMap : 表示使用了多个@Part标签。只有在有@Multipart标签时才能使用此标签。它修饰的参数值的取值跟@QueryMap一样
@Body: 表示传递一个RequestBody过去,将会使用Converter把数据转化成RequestBody。使用此标签时,不能使用@FormUrlEncoded或者@Multipart。只能使用一次

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值