手写简易retrofit

作者 : 开心源码 本文共4134个字,预计阅读时间需要11分钟 发布时间: 2022-05-12 共152人阅读

首先我们知道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组合使用,这就是灵活之处。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 手写简易retrofit

发表回复