03 Retrofit源码解析-2.2.0

作者: 凤邪摩羯 | 来源:发表于2021-08-23 09:30 被阅读0次

    版本号:2.5.0

    一.基本使用

    1.定义请求接口

    interface GithubService {
        //通过注解定义请求的方法以及路径,“{}”里面的表示:该内容是可变的,通过下面方法的参数赋值
        @GET("users/{user}/repos")
        fun listRepos(@Path("user") user: String): Call<Any>
    }
    
    

    2.创建Retrofit对象,通过Call对象发送网络请求

    //创建Retrofit对象
    val retrofit = Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .addConverterFactory(GsonConverterFactory.create())//设置数据解析器
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//设置支持Rxjava平台
            .build()
    //创建网络请求接口实例
    val githubService = retrofit.create(GithubService::class.java)
    //调用相应的接口获取对应的call对象
    val call = githubService.listRepos("user")
    //通过call对象执行异步请求
    call.enqueue(object : Callback<Any> {
        override fun onFailure(call: Call<Any>, t: Throwable) { }
        override fun onResponse(call: Call<Any>, response: Response<Any>) {  }
    })
    
    

    通过上面的代码可以看出,真正发送网络请求的是Call对象,也就是OkhttpRetrofit只是对网络请求参数的封装,真正的请求是通过Okhttp完成的。

    • 应用通过Retrofit发送网络请求,实际上是使用Retrofit接口层封装请求参数,之后由Okhttp完成后续的请求操作,在服务端返回数据之后,Okhttp将原始的结果交给Retrofit,Retrofit根据用户的需求对结果进行解析。

    二.源码分析

    1.创建对象

    网络请求参数的封装是通过Retrofit完成的,所以先看一下给对象的创建代码:
    Retrofit.Builder()

    public static final class Builder {
        //Retrofit支持平台
        private final Platform platform;
        //网路请求的okhttp的工厂,默认就是OkhttpClient
        private @Nullable okhttp3.Call.Factory callFactory;
        //请求的基地址
        private @Nullable HttpUrl baseUrl;
        //数据转换器工厂集合;数据转换器就是将从网络获取的数据转成java对象
        private final List<Converter.Factory> converterFactories = new ArrayList<>();
        //适配器工厂集合;适配器工厂:将我们Call对象转换成其他类型能用的请求,比如:Rxjava
        private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
        //用于执行异步回调的,在Android是默认在主线程中回调
        private @Nullable Executor callbackExecutor;
        //标志位,用于后面是否立即解析方法参数
        private boolean validateEagerly;
    
        Builder(Platform platform) {
          this.platform = platform;
        }
    
        public Builder() {
          this(Platform.get());
        }
    
         public Retrofit build() {
          if (baseUrl == null) {
            throw new IllegalStateException("Base URL required.");
          }
          //其他的成员变量判断
          ......
          //创建Retrofit对象,该对象中也有对应的成员变量,除了platform
          return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
              unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
        }
    
    }
    
    

    重点关注一下成员变量,如注释所示。在上述代码中Builder()会调用Builder(Platform platform)传入一个Platform对象,下面看一个该对象创建的代码:Platform.get()

    class Platform {
        private static final Platform PLATFORM = findPlatform();
        static Platform get() {
        return PLATFORM;
        }
    
        private static Platform findPlatform() {
        try {
          //加载指定的类
          Class.forName("android.os.Build");
          //如果Build.VERSION.SDK_INT != 0,说明是Android平台
          if (Build.VERSION.SDK_INT != 0) {
            return new Android();
          }
        } ......
        try {
          Class.forName("java.util.Optional");
          return new Java8();
        }......
        return new Platform();
    }
    
    

    从上述代码可以看出,Retrofit支持AndroidJava8平台。这里我们只关注Android平台,下面看一下Android中做了什么。

    static class Android extends Platform {
        ......
        @Override public Executor defaultCallbackExecutor() {
          return new MainThreadExecutor();
        }
    
        @Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
            @Nullable Executor callbackExecutor) {
          if (callbackExecutor == null) throw new AssertionError();
          ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor);
          return Build.VERSION.SDK_INT >= 24
            ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
            : singletonList(executorFactory);
        }
        ......
        @Override List<? extends Converter.Factory> defaultConverterFactories() {
          return Build.VERSION.SDK_INT >= 24
              ? singletonList(OptionalConverterFactory.INSTANCE)
              : Collections.<Converter.Factory>emptyList();
        }
        ......
        static class MainThreadExecutor implements Executor {
          private final Handler handler = new Handler(Looper.getMainLooper());
          @Override public void execute(Runnable r) {
            handler.post(r);
          }
        }
    }
    
    

    Android中提供了默认的Executor、ConverterFactor和CallAdapterFactory,其中默认的异步异步回调处理是在AndroidUI线程处理的new Handler(Looper.getMainLooper());,这也就是为什么Retrofit网络请求回调是在主线程中执行的(Okhttp的回调是在子线程中执行的)

    2.参数设置

    baseUrl()

    public Builder baseUrl(String baseUrl) {
         checkNotNull(baseUrl, "baseUrl == null");
         //HttpUrl对baseUrl进行解析分段,如获取:scheme、host、port和PathSegments
         return baseUrl(HttpUrl.get(baseUrl));
    }
    
    public Builder baseUrl(HttpUrl baseUrl) {
        checkNotNull(baseUrl, "baseUrl == null");
        List<String> pathSegments = baseUrl.pathSegments();
        //baseUrl的必须以“/”结尾
        if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
            throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
        }
        this.baseUrl = baseUrl;
        return this;
    }
    
    

    注意:baseUrl的必须以“/”结尾,否则抛出异常IllegalArgumentException(baseUrl must end in /:)

    addConverterFactory()

    public Builder addConverterFactory(Converter.Factory factory) {
        converterFactories.add(checkNotNull(factory, "factory == null"));
        return this;
    }
    
    

    该方法很简单,就是把设置的工厂添加到对应的集合中,重点看一下Converter.Factory对象的创建:GsonConverterFactory.create()

    public static GsonConverterFactory create() {
        return create(new Gson());
    }
    
    public static GsonConverterFactory create(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        return new GsonConverterFactory(gson);
    }
    
    private GsonConverterFactory(Gson gson) {
        this.gson = gson;
    }
    
    

    创建的过程就是创建一个Gson对象,然后给GsonConverterFactory的属性gson赋值。

    addCallAdapterFactory()

    该方法同addConverterFactory()类似,将Factory对象添加到相应的集合中。RxJava2CallAdapterFactory.create()方法也类似,创建一个RxJavaScheduler对象并赋值。

    3.获取请求接口实例

    retrofit.create(GithubService::class.java)

    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();
              private final Object[] emptyArgs = new Object[0];
    
              //proxy:代理对象
              //method:调用的方法信息
              //args:调用方法的参数
              //当代理对象方法调用的时候就会调用该方法
              @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
                  throws Throwable {
                // 如果调用的是Object类中的方法,比如:toString()方法等
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                //如果是平台默认的方法,Android平台该方法返回false,所以if条件不成立。
                if (platform.isDefaultMethod(method)) {
                  return platform.invokeDefaultMethod(method, service, proxy, args);
                }
                //最终会调用该方法
                return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
              }
            });
    }
    
    

    Android平台下调用请求方法时,是通过动态代理来实现的,在这个过程中最终会调用loadServiceMethod(method).invoke(args != null ? args : emptyArgs);

    我们先看一下loadServiceMethod(method)方法

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

    loadServiceMethod方法就是返回了一个ServiceMethod对象,该对象是对请求方法method的封装,如果缓存中存在就从缓存中获取,否则直接创建一个:ServiceMethod.parseAnnotations(this, method);ServiceMethod是一个抽象类,调用的是它子类HttpServiceMethodparseAnnotations方法,在该方法中直接new HttpServiceMethod对象,下面就看一下该类中的相关属性和方法:

    final class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
      ......
      //请求工厂,里面封装着请求方法先关的信息
      private final RequestFactory requestFactory;
      //请求工厂:其实就是OkhttpClient
      private final okhttp3.Call.Factory callFactory;
      private final CallAdapter<ResponseT, ReturnT> callAdapter;
      private final Converter<ResponseBody, ResponseT> responseConverter;
      ......
    
      @Override ReturnT invoke(Object[] args) {
        return callAdapter.adapt(
            new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
      }
    }
    
    

    下面重点看一下RequestFactory

    final class RequestFactory {
        ......
        private final Method method;
        private final HttpUrl baseUrl;
        final String httpMethod;
        private final @Nullable String relativeUrl;
        private final @Nullable Headers headers;
        private final @Nullable MediaType contentType;
        private final boolean hasBody;
        private final boolean isFormEncoded;
        private final boolean isMultipart;
        //参数解析器
        private final ParameterHandler<?>[] parameterHandlers;
        ......
    }
    
    

    RequestFactory的成员变量可以看出该类中包括请求相关的所有信息。

    创建好ServiceMethod对象以后,调用该对象的invoke方法:

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

    该方法就会返回一个Call对象,allAdapter.adapt()方法就是将OkHttpCall对象转换成其他平台能用的Call对象,比如:RxJava。这里返回的是OkHttpCall的对象,该类是Retrofit中定义的,是对Okhttp中的Call对象的封装。下面看一下该类中的相关属性和方法:

    final class OkHttpCall<T> implements Call<T> {
        private final RequestFactory requestFactory;
        private final Object[] args;
        private final okhttp3.Call.Factory callFactory;
        private final Converter<ResponseBody, T> responseConverter;
    
        private volatile boolean canceled;
    
        @GuardedBy("this")
        private @Nullable okhttp3.Call rawCall;
        ......
        //异步请求方法
        @Override public void enqueue(final Callback<T> callback) {
            ......
            okhttp3.Call call;
            Throwable failure;
            synchronized (this) {
              if (executed) throw new IllegalStateException("Already executed.");
              executed = true;
              call = rawCall;
              failure = creationFailure;
              if (call == null && failure == null) {
                try {
                  call = rawCall = createRawCall();
                }......
              }
            }
            ......
            //调用Okhttp-》call的enqueue方法,完成网络请求
            call.enqueue(new okhttp3.Callback() {
                @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
                    Response<T> response;
                    try {
                      //解析响应数据
                      response = parseResponse(rawResponse);
                    }......
    
            }
        }
        ......
        //创建Call对象:okHttpClient.newCall(request)
        private okhttp3.Call createRawCall() throws IOException {
            okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
            ......
            return call;
        }
    
        Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
            ResponseBody rawBody = rawResponse.body();
            rawResponse = rawResponse.newBuilder()
                .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
                .build();
    
            int code = rawResponse.code();
            //code判断
            if (code < 200 || code >= 300) {
              try {
                ResponseBody bufferedBody = Utils.buffer(rawBody);
                return Response.error(bufferedBody, rawResponse);
              } finally {
                rawBody.close();
              }
            }
            if (code == 204 || code == 205) {
              rawBody.close();
              return Response.success(null, rawResponse);
            }
            ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
            try {
              //转换成实体类
              T body = responseConverter.convert(catchingBody);
              return Response.success(body, rawResponse);
            }......
        }
    }
    
    

    OkHttpCallOkhttpCall封装了一下,它里面的enqueue方法其实调用的就Call.enqueue,在它的回调成功方法中,通过配置的Converter.Factory将响应数据转换成对应的实体类(Java对象)。还有一个需要关注的地方就是,Requst的创建,是通过requestFactory.create(args)创建的,下面看一下该方法:

    final class RequestFactory {
        ......
        okhttp3.Request create(Object[] args) throws IOException {
            @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
            //方法参数解析器,解析接口中定义的方法
            ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
            //调用方法传入的参数
            int argumentCount = args.length;
            if (argumentCount != handlers.length) {
            throw new IllegalArgumentException("Argument count (" + argumentCount
              + ") doesn't match expected count (" + handlers.length + ")");
            }
            //通过Builder模式创建Request对象
            RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
            headers, contentType, hasBody, isFormEncoded, isMultipart);
    
            List<Object> argumentList = new ArrayList<>(argumentCount);
            //设置请求参数
            for (int p = 0; p < argumentCount; p++) {
                argumentList.add(args[p]);
                handlers[p].apply(requestBuilder, args[p]);
            }
    
            return requestBuilder.get()
            .tag(Invocation.class, new Invocation(method, argumentList))
            .build();
        }
        ......
    }
    
    

    该方法就是根据ParameterHandler解析的参数以及传入的参数值args创建Request对象

    总结

    在调用Retrofitcreate方法获取接口请求实例Call对象,内部使用的是动态代理的方式,在调用相应的网络请求方法的时候,会回调invoke方法,在该方法中会调用ServiceMethod()invoke方法。
    ServiceMethod是一个抽象类,实际使用的是HttpServiceMethod,该类是对请求方法信息的封装,里面有几个比较重要的成员变量:

    • 1.RequestFactory :包含请求相关的信息
      private final Method method;
      private final HttpUrl baseUrl;
      final String httpMethod;
      private final @Nullable String relativeUrl;
      private final @Nullable Headers headers;
      private final @Nullable MediaType contentType;
      private final boolean hasBody;
      private final boolean isFormEncoded;
      private final boolean isMultipart;
      //参数解析器
      private final ParameterHandler<?>[] parameterHandlers;
    
    
    • 2.okhttp3.Call.Factory
    • 3.callAdapter
    • 4.responseConverter

    ServiceMethod.invoke的方法会返回一个OkHttpCall对象,该对象是对OkHttpCall的封装。真正的网络请求是通过OkHttpCall来实现的。

    三 总结

    Retrofit底层是基于Okhttp实现的,使用运行时注解的方式

    1、 原理

    通过java接口以及注解来描述网络请求,并用动态代理的方式生成网络请求的request,然后通过client调用相应的网络框架(默认okhttp)去发起网络请求,并将返回的response通过converterFactorty转换成相应的数据model,最后通过call adapter转换成其他数据方式(如rxjava Observable)

    2、 Retrofit流程

    (1)通过解析 网络请求接口的注解 配置 网络请求参数

    (2)通过 动态代理 生成 网络请求对象

    (3)通过 网络请求适配器 将 网络请求对象 进行平台适配

    (4)通过 网络请求执行器 发送网络请求

    (5)通过 数据转换器 解析服务器返回的数据

    (6)通过 回调执行器 切换线程(子线程 ->>主线程)

    (7)用户在主线程处理返回结果

    3、 Retrofit优点

    1.可以配置不同HTTP client来实现网络请求,如okhttp、httpclient等;

    2.请求的方法参数注解都可以定制;

    3.支持同步、异步和RxJava;

    4.超级解耦;

    5.可以配置不同的反序列化工具来解析数据,如json、xml等

    6.框架使用了很多设计模式

    相关文章

      网友评论

        本文标题:03 Retrofit源码解析-2.2.0

        本文链接:https://www.haomeiwen.com/subject/epkjultx.html