主题

我们本次讨论的主题如下:

  • retrofit使得okhttp的使用更加便利。
  • retrofit如何封装okhttp。

背景

  • Retrofit是什么?
    Retrofit 是一个RESTful 的HTTP 网络请求框架的封装。网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责 网络请求接口的封装。
    在这里插入图片描述
    上图说明了如下几点:
  1. App应用程序通过 Retrofit 请求网络,实际上是使用 Retrofit 接口层封装请求参数、Header、Url 等信息(稍后会解释),之后由 OkHttp 完成后续的请求操作。
  2. 在服务端返回数据之后,Okhttp将原始的结果交给 Retrofit, Retrofit根据用户的需求对结果进行解析。

二者的构建流程可参考下图
在这里插入图片描述

现在我们知道了上述的背景知识之后,我们就可以进入本文的主题了。

疑问

  • okhttp有什么不足之处?
    okhttp已经很强大了,为什么Android的大神们还要设计出retrofit这个框架呢?通过上述okhttp的使用案例,我们可以知道,okhttp虽然强大,但是还是有几个使用上的不便利之处的:
  1. okhttp只是拿到了服务器返回来的数据,一般都是json,但是我们在写代码的时候一般都是Gson来解析json字符串并将其映射到一个bean映射中,而okhttp并没有做到这一点,需要我们拿到数据之后手动映射;而retrofit则不然,在构建retrofit实例的时候传入一个gson适配器即可对所有的请求进行json解析与映射。
  2. 使用okhttp进行同步请求的时候必须要开子线程的,异步请求就不需要,因为异步请求里面使用了线程池。而客户端一般拿到服务端返回的数据之后大都会更新ui,这就意味着我们需要进行线程间的切换,使用上不是很便利,而retrofit内部就已经进行了线程间的切换,我们拿到数据之后就已经是在主线程了。
  3. 这一点是我从其他资料上查找到,Retrofit 接口层封装请求参数、Header、Url 等信息。retrofit只需要把请求的参数写入接口中,不需要向okhttp那样把请求的参数传入post方法中。但是,我个人觉得这点其实优化的不是很明显,用起来感觉都是差不多便利的。

基于上述这三点,我们可以清楚得知道retrofit使得okhttp的使用更加的便利。

接下来,我们进入第下一个主题:retrofit如何封装okhttp。
首先,我先看下构建Retrofit对象的代码:很简单,就是一个建造者模式,通过builder为retrofit配置各种需要的数据。

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                .addConverterFactory(GsonConverterFactory.create())//配置Gson转换器
                .build();

我们进入build方法看下做了什么事情?

public Retrofit build() {
            if (this.baseUrl == null) {
                throw new IllegalStateException("Base URL required.");
            } else {
                Factory callFactory = this.callFactory;
                //如果在构建时没有设置callFactory,默认是使用OkHttpClient
                if (callFactory == null) {
                    callFactory = new OkHttpClient();
                }
				
				//如果在构建时没有设置callbackExecutor,会使用默认的callbackExecutor
                Executor callbackExecutor = this.callbackExecutor;
                if (callbackExecutor == null) {
                    callbackExecutor = this.platform.defaultCallbackExecutor();
                }

                List<retrofit2.CallAdapter.Factory> callAdapterFactories = new ArrayList(this.callAdapterFactories);
                callAdapterFactories.addAll(this.platform.defaultCallAdapterFactories(callbackExecutor));
                List<retrofit2.Converter.Factory> converterFactories = new ArrayList(1 + this.converterFactories.size() + this.platform.defaultConverterFactoriesSize());
                //先加入BuiltInConverters到转换器工厂中,然后再加入我们设置的,最后再加入默认的转换器
                converterFactories.add(new BuiltInConverters());
                converterFactories.addAll(this.converterFactories);
                converterFactories.addAll(this.platform.defaultConverterFactories());
                return new Retrofit((Factory)callFactory, this.baseUrl, Collections.unmodifiableList(converterFactories), Collections.unmodifiableList(callAdapterFactories), callbackExecutor, this.validateEagerly);
            }
        }

从上述代码可以看到,我们在构建retrofit对象时可以不用设置callFactory、callbackExecutor、callAdapterFactories和converterFactories,因为源码中会帮助我们初始化一个默认的,但是我们一般都会添加一个gson转换器进行json解析。至此,build方法的源码分析完毕。
我们重点看下builder里面的各种成员变量分别是什么意思

 public static final class Builder {
        private final Platform platform;
        @Nullable
        private Factory callFactory;
        @Nullable
        private HttpUrl baseUrl;
        private final List<retrofit2.Converter.Factory> converterFactories;
        private final List<retrofit2.CallAdapter.Factory> callAdapterFactories;
        @Nullable
        private Executor callbackExecutor;
        private boolean validateEagerly;
}

重要的就是下面这几个

  • callFactory:网络请求工厂,一般我们如果要添加拦截器可以在这个添加
  • baseUrl:服务器域名,这个需要我们在创建时配置上去
  • converterFactories:数据转换器工厂的集合
  • callAdapterFactories:网络请求适配器工厂的集合,如果不想使用retrofit提供的call,想要自定义自己的call则要继承CallAdapter.Factory类才行。
  • callbackExecutor:回调方法执行器。笔者都是使用默认的没自定义过

接下来,我们看下retrofit中一个十分重要的方法——create方法。

public <T> T create(final Class<T> service) {
        this.validateServiceInterface(service);
        return Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service}, new InvocationHandler() {
            private final Platform platform = Platform.get();
            private final Object[] emptyArgs = new Object[0];

            @Nullable
            public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
                if (method.getDeclaringClass() == Object.class) {
                    return method.invoke(this, args);
                } else {
                    args = args != null ? args : this.emptyArgs;
                    //是否是平台默认的方法,是的话就使用平台默认的方法,否则就执行loadServiceMethod方法
                    return this.platform.isDefaultMethod(method) ? this.platform.invokeDefaultMethod(method, service, proxy, args) : Retrofit.this.loadServiceMethod(method).invoke(args);
                }
            }
        });
    }

这个方法接收一个class类型的参数,然后通过动态代理返回该class的一个代理类。而当我们通过该代理类执行相关的方法时,就会回调到invoke方法中。
例如:

public interface GitHubService {
   //配置GET请求和URL路径
    @GET("users/{user}/repos")
    //返回为Call<T>对象,泛型T表示网络解析后结果的类型
    //@Path("user")注解表示参数user会替换URL路径中的{user}
    Call<List<Repo>> listRepos(@Path("user") String user);//返回值必须声明成Retrofit中内置的Call类型,并通过泛型来指定服务器响应的数据应该装换成什么对象。
}

//使用Retrofit对象返回一个GitHubService的实现
  GitHubService gitHubService = retrofit.create(GitHubService.class);
  //在oncreate方法中添加下述代码
Call<List<Repo>> octocat = gitHubService.listRepos("octocat");//回调到invoke方法中

而invoke方法中看名字似乎返回了一个与method相关的对象。我们看下loadServiceMethod方法究竟做了什么事情。

ServiceMethod<?> loadServiceMethod(Method method) {
        ServiceMethod<?> result = (ServiceMethod)this.serviceMethodCache.get(method);
        if (result != null) {
            return result;
        } else {
            synchronized(this.serviceMethodCache) {
                result = (ServiceMethod)this.serviceMethodCache.get(method);
                if (result == null) {
                    result = ServiceMethod.parseAnnotations(this, method);
                    this.serviceMethodCache.put(method, result);
                }

                return result;
            }
        }
    }

这段代码大体就是如果cache中有该方法就从cache中拿,否则就进入parseAnnotations方法,我们看下parseAnnotations方法做了什么事情。

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
        RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
        Type returnType = method.getGenericReturnType();
        if (Utils.hasUnresolvableType(returnType)) {
            throw Utils.methodError(method, "Method return type must not include a type variable or wildcard: %s", new Object[]{returnType});
        } else if (returnType == Void.TYPE) {
            throw Utils.methodError(method, "Service methods cannot return void.", new Object[0]);
        } else {
            return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
        }
    }

首先,它会调用RequestFactory类的parseAnnotations方法,解析我们在接口定义中使用到的注解。RequestFactory类里面是对一些注解进行解析,我个人认为不需要太多关注,最后会将RequestFactory.parseAnnotations方法返回值传入HttpServiceMethod.parseAnnotations方法中。我们看下HttpServiceMethod.parseAnnotations方法做了什么事情。
注意:方法的入参数以及返回值。

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(Retrofit retrofit, Method method, RequestFactory requestFactory) {
        boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
        boolean continuationWantsResponse = false;
        boolean continuationBodyNullable = false;
        Annotation[] annotations = method.getAnnotations();
        Object adapterType;
        Type responseType;
        if (isKotlinSuspendFunction) {
            Type[] parameterTypes = method.getGenericParameterTypes();
            responseType = Utils.getParameterLowerBound(0, (ParameterizedType)parameterTypes[parameterTypes.length - 1]);
            if (Utils.getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
                responseType = Utils.getParameterUpperBound(0, (ParameterizedType)responseType);
                continuationWantsResponse = true;
            }

            adapterType = new ParameterizedTypeImpl((Type)null, Call.class, new Type[]{responseType});
            annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
        } else {
            adapterType = method.getGenericReturnType();
        }

//上面的代码个人觉得基本可以忽略不看,我们应该重点关注requestFactory, callFactory, responseConverter, callAdapter这四个参数,应该它们与返回值有关系。
        CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, (Type)adapterType, annotations);
        responseType = callAdapter.responseType();
        if (responseType == okhttp3.Response.class) {
            throw Utils.methodError(method, "'" + Utils.getRawType(responseType).getName() + "' is not a valid response body type. Did you mean ResponseBody?", new Object[0]);
        } else if (responseType == Response.class) {
            throw Utils.methodError(method, "Response must include generic type (e.g., Response<String>)", new Object[0]);
        } else if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
            throw Utils.methodError(method, "HEAD method must use Void as response type.", new Object[0]);
        } else {
            Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);
            Factory callFactory = retrofit.callFactory;
            if (!isKotlinSuspendFunction) {
                return new HttpServiceMethod.CallAdapted(requestFactory, callFactory, responseConverter, callAdapter);
            } else {
                return (HttpServiceMethod)(continuationWantsResponse ? new HttpServiceMethod.SuspendForResponse(requestFactory, callFactory, responseConverter, callAdapter) : new HttpServiceMethod.SuspendForBody(requestFactory, callFactory, responseConverter, callAdapter, continuationBodyNullable));
            }
        }
    }

上面的源码中有下面2个方法需要注意一下:

  • createCallAdapter方法
    该方法最终会迭代callAdapterFactories,如果有一个callAdapter就返回,否则会抛出异常。
  • createResponseConverter方法
    该方法最终会迭代converterFactories,如果有一个converter就返回,否则会抛出异常。

我们接着进入CallAdapted方法一探究竟。

 static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
        private final CallAdapter<ResponseT, ReturnT> callAdapter;

        CallAdapted(RequestFactory requestFactory, Factory callFactory, Converter<ResponseBody, ResponseT> responseConverter, CallAdapter<ResponseT, ReturnT> callAdapter) {
            super(requestFactory, callFactory, responseConverter);
            this.callAdapter = callAdapter;
        }

        protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
            return this.callAdapter.adapt(call);
        }
    }

里面有点价值的就是adapt方法。但是别忘记了在Retrofit类的create方法的最后面是调用了invoke方法

Retrofit.this.loadServiceMethod(method).invoke(args);

而CallAdapted类是继承HttpServiceMethod类,HttpServiceMethod类中刚好有个invoke方法,里面调用了adapt方法。我们接着进入adapt方法看下。
在这里插入图片描述
发现是一个接口,我们很自然找到它的实现类,那我们应该找哪个实现类呢?记不记得我们在构建Retrofit类时会默认添加一个callAdapter到callAdapterFactories工厂中,这个就是DefaultCallAdapterFactory。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package retrofit2;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Objects;
import java.util.concurrent.Executor;
import javax.annotation.Nullable;
import okhttp3.Request;
import okio.Timeout;
import retrofit2.CallAdapter.Factory;

//如果想要自定义call,可以参考该类
final class DefaultCallAdapterFactory extends Factory {
    @Nullable
    private final Executor callbackExecutor;

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


//在迭代callAdapterFactory时,会调用到get方法,而get方法就调用跑了adapt方法
    @Nullable
    public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    //必须要先判断是否是想要的call类型
        if (getRawType(returnType) != Call.class) {
            return null;
        } else if (!(returnType instanceof ParameterizedType)) {
            throw new IllegalArgumentException("Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
        } else {
            final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType)returnType);
            //判断是否使用了SkipCallbackExecutor注解,如果使用了就将executor赋值为null,否则就使用this.callbackExecutor
            final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class) ? null : this.callbackExecutor;
            return new CallAdapter<Object, Call<?>>() {
                public Type responseType() {
                    return responseType;
                }

                public Call<Object> adapt(Call<Object> call) {
                //只要没有使用SkipCallbackExecutor注解,就执行默认的executor
                    return (Call)(executor == null ? call : new DefaultCallAdapterFactory.ExecutorCallbackCall(executor, call));

//至此,我们可以知道动态代理返回的是一个ExecutorCallbackCall对象,当我们调用enqueue方法或execute方法时,调用的是ExecutorCallbackCall对象的对象方法

                }
            };
        }
    }

    static final class ExecutorCallbackCall<T> implements Call<T> {
        final Executor callbackExecutor;
        final Call<T> delegate;

        ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
            this.callbackExecutor = callbackExecutor;
            this.delegate = delegate;
        }

        public void enqueue(final Callback<T> callback) {
            Objects.requireNonNull(callback, "callback == null");
            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);
                    });
                }
            });
        }

        public boolean isExecuted() {
            return this.delegate.isExecuted();
        }

        public Response<T> execute() throws IOException {
            return this.delegate.execute();
        }

        public void cancel() {
            this.delegate.cancel();
        }

        public boolean isCanceled() {
            return this.delegate.isCanceled();
        }

        public Call<T> clone() {
            return new DefaultCallAdapterFactory.ExecutorCallbackCall(this.callbackExecutor, this.delegate.clone());
        }

        public Request request() {
            return this.delegate.request();
        }

        public Timeout timeout() {
            return this.delegate.timeout();
        }
    }
}

上述中,我们知道了动态代理最终返回了ExecutorCallbackCall对象给我们,那么ExecutorCallbackCall对象时什么呢?记不记得,我们在构建retrofit对象时,有一个callbackExecutor方法,ExecutorCallbackCall对象就是在这个被赋值的。我们具体看下里面是怎么实现的
在这里插入图片描述
在这里插入图片描述
呃,怎么是个null呢?这样的话按照我们上面的分析,它是不会使用ExecutorCallbackCall对象的。别急,我们看下它的子类是怎么实现的就知道了。

static final class Android extends Platform {
        Android() {
            super(VERSION.SDK_INT >= 24);
        }

        public Executor defaultCallbackExecutor() {
            return new Platform.Android.MainThreadExecutor();
        }

        @Nullable
        Object invokeDefaultMethod(Method method, Class<?> declaringClass, Object object, Object... args) throws Throwable {
            if (VERSION.SDK_INT < 26) {
                throw new UnsupportedOperationException("Calling default methods on API 24 and 25 is not supported");
            } else {
                return super.invokeDefaultMethod(method, declaringClass, object, args);
            }
        }

        static final class MainThreadExecutor implements Executor {
            private final Handler handler = new Handler(Looper.getMainLooper());

            MainThreadExecutor() {
            }

            public void execute(Runnable r) {
                this.handler.post(r);
            }
        }
    }

哦!原来,retrofit底层也是通过handler进行线程间的切换的。这样,我们拿到服务端返回值之后就自动切换到主线程了。

最后

本文中使用的retrofit的版本是2.9.0,如果使用其他版本,可能有些地方会不一致但是思想大体都是一样的。
感谢各位,看到最后!!!

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐