美文网首页
Retrofit源码看不下去?带你手写一个!

Retrofit源码看不下去?带你手写一个!

作者: 千雨茶 | 来源:发表于2019-10-14 16:24 被阅读0次

    前言

    Retrofit是一个优秀的网络请求框架的封装,它本身不请求数据,网络请求的工作由OKhttp负责。现在比较流行的MVP框架通常是由Retrofit+Rxjava+MVP来构建的。任何一款优秀的框架,都有值得我们去学习的亮点。下面我们先看一下Retrofit的使用方法,然后写一个我们自己的Retrofit。

    Retrofit的使用

    • 定义接口
        interface API {
            @GET("/ip/ipNew")
            Call get(@Query("ip") String ip, @Query("key") String key);
        }
    
    • 初始化Retrofit类,获得接口实现
        Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL).build();
        API api = retrofit.create(API.class);
    
    • 调用接口
        Call call = api.get(IP, KEY);
        Response response = call.execute();
        if (response != null && response.body() != null) {
            System.out.println("GET请求:" + response.body().string());
        }
    

    自定义Retrofit

    首先定义四个注解,分别用来描述请求方式和参数类型

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface GET {
        String value();
    }
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface POST {
        String value();
    }
    
    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Query {
        String value();
    }
    
    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Field {
        String value();
    }
    

    定义一个Retrofit类,使用建造者模式。

        private HttpUrl baseUrl;
        private Call.Factory callFactory;
        /**
         * 缓存请求的方法
         *
         * @param builder
         */
        private final Map<Method, ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();
    
        private Retrofit(Builder builder) {
            this.baseUrl = builder.baseUrl;
            this.callFactory = builder.callFactory;
        }
    
        public HttpUrl getBaseUrl() {
            return baseUrl;
        }
    
        public Call.Factory getCallFactory() {
            return callFactory;
        }
    
        public static class Builder {
    
            private HttpUrl baseUrl;
    
            private Call.Factory callFactory;
    
    
            public Builder baseUrl(String baseUrl) {
                if (baseUrl.isEmpty()) {
                    throw new NullPointerException("baseUrl == null");
                }
                this.baseUrl = HttpUrl.parse(baseUrl);
                return this;
            }
    
    
            public Builder baseUrl(HttpUrl baseUrl) {
                if (baseUrl == null) {
                    throw new NullPointerException("baseUrl == null");
                }
                this.baseUrl = baseUrl;
                return this;
            }
    
    
            public Builder callFactory(Call.Factory callFactory) {
                this.callFactory = callFactory;
                return this;
            }
    
            public Retrofit build() {
                if (this.baseUrl == null) {
                    throw new IllegalStateException("BaseUrl required.");
                }
    
                if (this.callFactory == null) {
                    callFactory = new OkHttpClient();
                }
    
                return new Retrofit(this);
            }
        }
    

    其中serviceMethodCache是用来缓存接口类定义的方法,以方法Method为key,ServiceMethod为value,ServiceMethod是请求方法属性的封装类,该类中定义了解析Method中所有参数信息的方法,同样使用建造者模式。下面是其实现:

    public class ServiceMethod {
        //OKHTTPClient唯一实现接口
        private final Call.Factory callFactory;
    
        //接口请求的地址
        private final HttpUrl baseUrl;
    
        //方法请求方式
        private final String httpMethod;
    
        //方法的注解的值("/ip/ipNew")
        private final String relativeUrl;
    
        //方法参数的数组
        private final ParameterHandler[] parameterHandlers;
    
        //是否有请求体
        private final boolean hasBody;
    
    
        private ServiceMethod(Builder builder) {
            this.callFactory = builder.retrofit.getCallFactory();
            this.baseUrl = builder.retrofit.getBaseUrl();
            this.httpMethod = builder.httpMethod;
            this.relativeUrl = builder.relativeUrl;
            this.parameterHandlers = builder.parameterHandlers;
            this.hasBody = builder.hasBody;
        }
    
    
    
        okhttp3.Call toCall(Object... args){
            RequestBuilder requestBuilder = new RequestBuilder(httpMethod,baseUrl,relativeUrl,hasBody);
    
            ParameterHandler[] handlers = this.parameterHandlers;
            int argumentCount = args != null ? args.length : 0;
            if (argumentCount != handlers.length){
                throw new IllegalArgumentException("");
            }
    
            for (int i = 0; i < argumentCount; i++) {
                handlers[i].apply(requestBuilder,args[i].toString());
            }
    
            //创建请求
            return callFactory.newCall(requestBuilder.build());
        }
    
    
        static final class Builder {
    
            final Retrofit retrofit;
            //带注解的方法
            final Method method;
            //方法的所有注解
            final Annotation[] methodAnnotations;
            //方法参数的所有注解
            final Annotation[][] parameterAnnotationsArray;
            //方法的请求方式get post
            private String httpMethod;
            //方法注解的值
            private String relativeUrl;
            //方法参数的数组(每个对象包含:参数注解值、参数值)
            private ParameterHandler[] parameterHandlers;
            //是否有请求体
            private boolean hasBody;
    
    
            public Builder(Retrofit retrofit, Method method) {
                this.retrofit = retrofit;
                this.method = method;
    
                this.methodAnnotations = method.getAnnotations();
                this.parameterAnnotationsArray = method.getParameterAnnotations();
    
            }
    
            ServiceMethod build() {
                for (Annotation annotation : methodAnnotations) {
                    parseMethodAnnotation(annotation);
                }
    
                //定义方法参数的数组长度
                int parameterCount = parameterAnnotationsArray.length;
                //初始化方法参数的数组
                parameterHandlers = new ParameterHandler[parameterCount];
                for (int i = 0; i < parameterCount; i++) {
                    //获取方法的每个参数的多个注解
                    Annotation[] parameterAnnotations = parameterAnnotationsArray[i];
                    if (parameterAnnotations == null) {
                        throw new NullPointerException("参数无注解");
                    }
    
                    parameterHandlers[i] = parseParameter(i, parameterAnnotations);
    
                }
    
                return new ServiceMethod(this);
            }
    
            //解析参数的所有注解 嵌套循环
            private ParameterHandler parseParameter(int i, Annotation[] annotations) {
                ParameterHandler result = null;
               for (Annotation annotation : annotations) {
                    ParameterHandler annotationAction = parseParameterAnnotation(annotation);
    
                    if (annotationAction == null) {
                        continue;
                    }
                    result = annotationAction;
                }
                if (result == null) {
                    throw new IllegalArgumentException("没有Retrofit注解的支持");
                }
                return result;
            }
    
            //解析参数的注解
            private ParameterHandler parseParameterAnnotation(Annotation annotation) {
                if (annotation instanceof Query) {
                    Query query = (Query) annotation;
                    //参数注解的值
                    String name = query.value();
                    return new ParameterHandler.Query(name);
                } else if (annotation instanceof Field) {
                    Field field = (Field) annotation;
                    String name = field.value();
                    return new ParameterHandler.Field(name);
                }
                return null;
            }
    
            /**
             * 解析方法的注解 GET/POST
             *
             * @param annotation 注解
             */
            private void parseMethodAnnotation(Annotation annotation) {
                if (annotation instanceof GET) {
                    parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
                } else if (annotation instanceof POST) {
                    parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
                }
            }
    
    
            private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
                this.httpMethod = httpMethod;
                this.relativeUrl = value;
                this.hasBody = hasBody;
            }
        }
    }
    

    实例化Retrofit后,调用create(Class<T> service)方法,通过动态代理,解析接口方法参数信息,获取到请求方式、请求参数名、请求参数值等,最终通过OKhttp创建网络请求。create(Class<T> service)方法的实现如下:

        public <T> T create(Class<T> service) {
            return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    ServiceMethod serviceMethod = loadServiceMethod(method);
                    return new OKHttpCall(serviceMethod, args);
                }
            });
        }
    
        /**
         * 获得方法中所有内容,包括方法名、注解、参数、参数注解等
         *
         * @param method
         * @return
         */
        private ServiceMethod loadServiceMethod(Method method) {
            ServiceMethod result = serviceMethodCache.get(method);
            if (result != null) {
                return result;
            }
            synchronized (serviceMethodCache) {
                result = serviceMethodCache.get(method);
                if (result == null) {
                    result = new ServiceMethod.Builder(this, method).build();
                    serviceMethodCache.put(method, result);
                }
            }
            return result;
        }
    

    其中loadServiceMethod(method)方法,管理维护serviceMethodCache,通过调用ServiceMethod.Builder()获取并实例化方法参数信息ServiceMethod,缓存在serviceMethodCache中。
    然后在代理方法处理中,创建一个OKhttp请求,return new OKHttpCall(serviceMethod, args)。我们看一下OKhttpCall的实现:

    public class OKHttpCall implements Call {
    
        private ServiceMethod serviceMethod;
        private Object[] args;
        private okhttp3.Call rawCall;
    
        public OKHttpCall(ServiceMethod serviceMethod, Object[] args) {
            this.serviceMethod = serviceMethod;
            this.args = args;
            this.rawCall = serviceMethod.toCall(args);
        }
    
        @Override
        public Request request() {
            return rawCall.request();
        }
    
        @Override
        public Response execute() throws IOException {
            return rawCall.execute();
        }
    
        @Override
        public void enqueue(Callback responseCallback) {
            rawCall.enqueue(responseCallback);
        }
    
        @Override
        public void cancel() {
            rawCall.cancel();
        }
    
        @Override
        public boolean isExecuted() {
            return rawCall.isExecuted();
        }
    
        @Override
        public boolean isCanceled() {
            return rawCall.isCanceled();
        }
    
        @Override
        public Timeout timeout() {
            return rawCall.timeout();
        }
    
        @Override
        public Call clone() {
            return new OKHttpCall(serviceMethod,args);
        }
    }
    

    可以看到,在这里实际上我们使用了ServiceMethod.toCall()方法生成的okhttp3.Call来全权处理网络访问。我们再来看一下这个toCall()方法

        okhttp3.Call toCall(Object... args) {
            RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, hasBody);
    
            ParameterHandler[] handlers = this.parameterHandlers;
            int argumentCount = args != null ? args.length : 0;
            //方法真实的参数个数是否等于收集的参数个数
            if (argumentCount != handlers.length) {
                throw new IllegalArgumentException("");
            }
    
            //循环拼接每个参数名+参数值
            for (int i = 0; i < argumentCount; i++) {
                handlers[i].apply(requestBuilder, args[i].toString());
            }
    
            //创建请求
            return callFactory.newCall(requestBuilder.build());
        }
    

    我们知道,这里的callFactory是默认的OkHttpClient,所以这里的newCall(requestBilder.build()),实际上就是调用了OKHttpClient中的这个方法,我们看一下这个方法的实现:

        @Override public Call newCall(Request request) {
        return RealCall.newRealCall(this, request, false /* for web socket */);
      }
    

    所以这里创建的是官方的默认Call接口的实现类RealCall
    其中ParameterHandler是用来处理请求参数类型,如QueryField等,它的apply()方法中调用RequestBuilder中拼接参数的方法,RequestBuilder是最终拼接参数的操作类。ParameterHandler的实现如下:

    abstract class ParameterHandler {
    
        /**
         * 抽象方法 外部复制和调用,自己的内部类实现了
         *
         * @param builder 请求构建者(拼装者)
         * @param value   方法的参数值
         */
        abstract void apply(RequestBuilder builder, String value);
    
    
        static final class Query extends ParameterHandler{
    
            //参数名
            private String name;
    
            Query(String name){
                if (name.isEmpty()){
                    throw new NullPointerException("");
                }
                this.name = name;
            }
    
    
            @Override
            void apply(RequestBuilder builder, String value) {
                //此处的value是参数值
                if (value == null){
                    return;
                }
                builder.addQueryParam(name,value);
            }
        }
    
    
        static final class Field extends ParameterHandler{
    
            private final String name;
    
            Field(String name){
                if (name.isEmpty()){
                    throw new NullPointerException("");
                }
                this.name = name;
            }
    
            @Override
            void apply(RequestBuilder builder, String value) {
                if (value == null){
                    return;
                }
                //拼接Field参数,此处name为参数注解的值,value为参数值
                builder.addFormField(name,value);
            }
        }
    }
    

    它本身是个抽象类,抽象类中定义了两个自己的实现类。
    RequestBuilder的实现如下:

    public class RequestBuilder {
    
        private final String method;
        private final HttpUrl baseUrl;
        private String relativeUrl;
        private HttpUrl.Builder urlBuilder;
        private FormBody.Builder formBuilder;
        private final Request.Builder requestBuilder;
    
    
        public RequestBuilder(String method, HttpUrl baseUrl, String relativeUrl, boolean hasBody) {
            this.method = method;
            this.baseUrl = baseUrl;
            this.relativeUrl = relativeUrl;
    
            requestBuilder = new Request.Builder();
            if (hasBody) {
                formBuilder = new FormBody.Builder();
            }
        }
    
        public void addQueryParam(String name, String value) {
            if (relativeUrl != null) {
                //拼接访问地址
                urlBuilder = baseUrl.newBuilder(relativeUrl);
    
                relativeUrl = null;
            }
    
            urlBuilder.addQueryParameter(name, value);
        }
    
        public void addFormField(String name, String value) {
            formBuilder.add(name, value);
        }
    
    
        Request build() {
            HttpUrl url;
            if (urlBuilder != null) {
                url = urlBuilder.build();
            } else {
                url = baseUrl.resolve(relativeUrl);
                if (url == null) {
                    throw new IllegalArgumentException("Malformed URL. Base: " + baseUrl + ",Relative:" + relativeUrl);
                }
            }
    
            RequestBody body = null;
            if (formBuilder != null) {
                body = formBuilder.build();
            }
    
            //构建完整请求
            return requestBuilder
                    .url(url)
                    .method(method, body)
                    .build();
        }
    }
    

    可以看到,在上面ServiceMethod中的toCall()方法中返回的callFactory.newCall(requestBuilder.build())中,newCall(Request request)方法的参数request最终就是调用了这里来生成。

             return requestBuilder
                    .url(url)
                    .method(method, body)
                    .build();
    

    到这里,我们的仿写就完成了。
    总结一下,在Retrofit中的create(Class<T> service)方法中,loadServiceMethod(method)方法收集接口API中定义方法的所有参数信息,new OKHttpCall(serviceMethod, args)中处理接口的参数,创建代理对象并返回,当执行

    Call call = api.get(IP, KEY);
    

    的时候,调用代理对象相应的方法执行。

    测试

    使用第二步Retrofit的使用中的代码来测试,打印数据:

    GET请求:{"resultcode":"200","reason":"查询成功","result":{"Country":"美国","Province":"加利福尼亚","City":"","Isp":""},"error_code":0}
    

    测试成功,示例代码已上传Github

    后记

    不知道大家有没有一个疑问,我上面直接使用Retrofit来请求数据,定义的接口是这样的:

    @GET("/ip/ipNew")
    Call get(@Query("ip") String ip, @Query("key") String key);
    

    然而在与Rxjava结合使用的时候,是这样定义的:

    @GET("/ip/ipNew")
    Observable<Bean> get(@Query("ip") String ip, @Query("key") String key);
    

    返回类型不一样,而上面我们在实现中,代理对象是RealCall,那这样的话,就不可能返回Observalbe类型啊?
    OK,那就让我们来看一下,为个啥。来看一下官方Retrofitcreate(Class<T> service)方法的实现:

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

    看最后一句

    return serviceMethod.callAdapter.adapt(okHttpCall);
    

    这里返回的类型是由callAdapter来决定的。很明显这是个请求适配器,它的初始化工作在ServiceMethodbuild()函数中

    public ServiceMethod build() {
          callAdapter = createCallAdapter();
          responseType = callAdapter.responseType();
          ...
    

    我们看一下这个createCallAdapter()方法:

    private CallAdapter<T, R> createCallAdapter() {
          Type returnType = method.getGenericReturnType();
          if (Utils.hasUnresolvableType(returnType)) {
            throw methodError(
                "Method return type must not include a type variable or wildcard: %s", returnType);
          }
          if (returnType == void.class) {
            throw methodError("Service methods cannot return void.");
          }
          Annotation[] annotations = method.getAnnotations();
          try {
            //noinspection unchecked
            return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
          } catch (RuntimeException e) { // Wide exception range because factories are user code.
            throw methodError(e, "Unable to create call adapter for %s", returnType);
          }
        }
    

    可以看到是调用了retrofit.callAdapter()方法。而retrofit中的callAdapter的初始化时机在Retrofit的nextCallAdapter方法中:

    public CallAdapter<?, ?> nextCallAdapter(@Nullable 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;
          }
        }
        ...
    

    是拿adapterFactories获取的,这是个请求适配器工厂的集合,定义如下

    final List<CallAdapter.Factory> adapterFactories;
    

    那这个集合中的数据何时添加的呢?这里我们回想一下,大家在使用Retrofit+Rxjava的时候,是怎么配置Retrofit的?是不是下面这样:

    Retrofit retrofit = new Retrofit.Builder()  
                      .baseUrl(baseUrl)  
                      .addConverterFactory(GsonConverterFactory.create())  
                      .addCallAdapterFactory(RxJavaCallAdapterFactory.create())  
                      .build();
    

    看到了吗?是我们添加了RxJavaCallAdapterFactory这个适配器工厂,所以最终定义接口的时候,可以返回Observable类型。这个RxJava2CallAdapterFactory的支持库,正是大神Jake Wharton的杰作之一。
    什么?这人是谁不认识?不怕,咱现场百度,感受一下大佬气息

    2019-10-14-16-08-54.png
    啥也不说了,渣渣的我瑟瑟发抖

    相关文章

      网友评论

          本文标题:Retrofit源码看不下去?带你手写一个!

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