手写简易retrofit

作者: 碎碎想 | 来源:发表于2018-12-01 18:29 被阅读17次

    首先我们知道retrofit底层是基于OkHttp的:
    接着我们通过一个测试类,看一下retrofit的一般使用方法:

    public class RetrofitTest {
        interface Weather{
            @GET("/v3/weather/weatherInfo")
            Call get(@Query("city") String city, @Query("key") String key);
        }
        @Test
        public void test(){
            Retrofit retrofit = new Retrofit.Builder().baseUrl("http://restapi.amap.com/").build();
            Weather weather = retrofit.creat(Weather.class);
            Call call = weather.get("北京", "13cb58f5884f9749287abbead9c658f2");
            try {
                Response response = call.execute();
                System.out.println(response.body().string());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    我们可以看出Retrofit是通过Builder模式生成的,这样的优点应该都知道,不仅能帮我们初始化一系列的参数,还可以让调用者不用过于关心内部构造。
    接着我们看Retrofit的creat传入的是一个接口类,所以这里应该是会用到代理模式,通过代理模式拿到Weather的方法,再通过反射的方法获取到我们需要的地址的后缀和我们需要传递的参数。

    public class Retrofit {
        private HttpUrl baseUrl;
        private Call.Factory callFactory;
    
       private ConcurrentHashMap<Method,ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();
    
        public Retrofit(Builder builder) {
            this.baseUrl = builder.baseUrl;
            this.callFactory = builder.callFactory;
        }
    
        public HttpUrl baseUrl() {
            return baseUrl;
        }
    
        public Call.Factory callFactory() {
            return callFactory;
        }
    
        public <T> T creat(Class<T> clazz){
            return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz},
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                           
                            //采集数据
                            ServiceMethod serviceMethod = loadServiceMethod(method);
    
                            return serviceMethod.toCall(args);
                        }
                    });
        }
    
        private ServiceMethod loadServiceMethod(Method method){
            ServiceMethod serviceMethod = serviceMethodCache.get(method);
    
            if(null == serviceMethod){
                //采集
                serviceMethod = new ServiceMethod.Builder(this,method).bulid();
                serviceMethodCache.putIfAbsent(method,serviceMethod);
            }
            return serviceMethod;
        }
    
        /*
            构建者模式
             */
        public static final class Builder{
    
            private HttpUrl baseUrl;
            private Call.Factory callFactory;
    
            public Builder baseUrl(String baseUrl){
                this.baseUrl = HttpUrl.parse(baseUrl);
                return this;
            }
            public Builder callFactory(Call.Factory callFactory){
                this.callFactory = callFactory;
                return this;
            }
    
    
            public Retrofit build(){
                if(baseUrl == null){
                    throw new IllegalArgumentException("baseUrl not set");
                }
                if(callFactory == null){
                    callFactory = new OkHttpClient();
                }
                return new Retrofit(this);
            }
    
    
        }
    
    }
    

    这里有几点需要说一下:
    1、creat方法:其实creat方法主要的功能就是通过动态代理获取到接口类的实例,然后通过实例获取到请求方法的各个参数(包括注解上面的地址后缀和每个接口需要传递的参数);我在这里把这些放到了一个缓存中,这样也可以加快效率。在
    2、callFactory方法:其实retrofit中这种工厂设计模式使我们想要使用它的理由之一,它的这种插拔式的使用让我们可以把数据转为Gson或者Xml等类型的数据,并且还可以和RxJava组合使用,这里没有在向外延伸。
    3、因为调用weather的get方法返回的是Call对象,所以需要在creat方法中的invoke中返回Call对象。这里的Call是okhttp3包下的。

    public Call toCall(Object[] args) {
        //1.创建Request
        Request.Builder requestBuilder = new Request.Builder();
        //1.1地址
        if (urlBuilder == null) {
            urlBuilder = baseUrl.newBuilder(relativeUrl);
        }
        //1.2如果是get请求,将参数放到地址中
    
        for (int i = 0; i < parameterHandlers.length; i++) {
            parameterHandlers[i].apply(this, String.valueOf(args[i]));
    
        }
    
        if (formBuilder != null) {
            formBody = formBuilder.build();
        }
        requestBuilder.url(urlBuilder.build());
        Request request = requestBuilder.method(httpMethod, formBody).build();
        //2.创建call
        return  callFactory.newCall(request);
    }
    

    4、因为获取到方法上面的参数需要拼接和判断(这里只做了GET和POST)。所以用了一个serviceMethod来处理这一系列的问题;

    public ServiceMethod bulid() {
        //处理方法的注解
        for (Annotation annotation : methodAnnotation) {
            //GET
            paseMethodAnnotation(annotation);
        }
        //处理参数的注解
        parameterHandlers = new ParameterHandler[parameterAnnotations.length];
        //遍历参数注解
        for (int i = 0; i < parameterAnnotations.length; i++) {
            Annotation[] parameterAnnotation = parameterAnnotations[i];
            //遍历一个参数上的左右注解
            for (Annotation annotation : parameterAnnotation) {
                if (annotation instanceof Query) {
                    Query query = (Query) annotation;
                    String value = query.value();
                    parameterHandlers[i] = new ParameterHandler.Query(value);
                } else if (annotation instanceof Field) {
                    Field field = (Field) annotation;
                    String value = field.value();
                    parameterHandlers[i] = new ParameterHandler.Filed(value);
                }
            }
        }
        return new ServiceMethod(this);
    }
    

    这样的话我们把地址和参数都拼装好了,而且也获取到Call对象了,然后执行call.execute()就可以获取到Response了。

    try {
        Response response = call.execute();
        System.out.println(response.body().string());
    } catch (IOException e) {
        e.printStackTrace();
    }
    

    总结:对于不理解retrofit的人来说,刚开始对于retrofit的用法和原理都是一头雾水,还有一些人都是停止在会用retrofit的方法之上就没有深入研究。其实可以简单的对retrofit理解为:它就是对okhttp的一个封装,只不过可扩展性更强。内部的根本原理就是OkHttp。因为内部对于请求方法的类使用了代理模式获取请求地址和想要传递的参数,所以请求方法类必须使用接口,并且在Retrofit的内部使用 ConverterFactory工厂类来让用户选择返回数据的格式(可以是Gson,也可以是XML等各种数据格式),更加的方便灵活。甚至还可以通过CallAdapterFactory的适配器模式来与RxJava组合使用,这就是灵活之处。

    相关文章

      网友评论

        本文标题:手写简易retrofit

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