ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

十分钟教你手撸一个简单的Retrofit demo

2020-11-24 12:34:32  阅读:198  来源: 互联网

标签:demo Builder factory private public 十分钟 baseUrl new Retrofit


十分钟教你手撸一个简单的Retrofit demo

众所周知,retrofit框架是square公司旗下的著名的http请求框架,今天我们来理一理它的主要逻辑,并写一个demo,这里涉及到java中注解,反射,泛型等知识点和构建者模式、动态代理,和我一起来一探究竟吧。

Retrofit的具体用法请自行百度,下面是关键的三行代码

//利用构建者模式实例化Retrofit    
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://restapi.amap.com/").build();
//通过动态代理生成接口的实例对象,并实现接口中定义的方法;
apiService = (ApiService) retrofit.create(ApiService.class);
//代理对象去做http请求
Call call = (Call) apiService.get("110101","ae6c53e2186f33bbf240a12d80672d1b");

先想一想做http请求,我们需要什么?需要域名,请求方式,请求参数,嗯,还需要Okhttp

实现Retrofit第一步

构建MyRetrofit类,在里边儿定义一个内部Builder类,构建者模式是将一个复杂的对象一步一步建立起来。然后在Builder类型中baseUrl方法准备好域名,callFactory准备好OKHttp ,build()方法将获得的参数传入MyRetrofit构造方法,返回MyRetrofit实例对象。


public class MyRetrofit {
​
    public final HttpUrl baseUrl;
    public final Call.Factory factory;
​
    public MyRetrofit(HttpUrl baseUrl, Call.Factory factory){
        this.baseUrl = baseUrl;
        this.factory = factory;
    }
​
    public static final class Builder{
        private HttpUrl baseUrl;
        private Call.Factory factory;
​
        public Builder baseUrl(String url){
            if(url != null){
                baseUrl = HttpUrl.get(url);
            }
            return this;
        }
        
        public Builder callFactory(Call.Factory factory){
            if(factory != null){
                this.factory = factory;
            }
            return this;
        }
​
        public MyRetrofit build(){
            if(this.factory == null){
                this.factory = new OkHttpClient();
            }
            return new MyRetrofit(baseUrl,factory);
        }
    }
}

实现Retrofit第二步

通过接口创建代理对象, 并在invoke的回调中实现apiService中定义的方法。代理模式是类似于中介代理的某项服务,类似于租房,你不需要知道房东是谁,只需要找中介就能租,静态代理是租房找租房中介,留学找留学中介,相亲找媒婆;而动态代理就很牛了,不管是租房,留学还是相亲都可以找这个中介,即静态代理是一个代理功能对应一个代理类,动态代理是多个代理功能对应一个代理类。下面是生成http请求的动态代理类,Proxy.newProxyInstance()中通过接口生成动态代理类,newProxyInstance()方法中先对interfaces接口做拷贝,再根据拷贝的接口和类加载器生成代理类,最后获取代理类的构造方法,再返回由构造方法实例化的代理对象。在InvocationHandler回调的invoke方法中实现http请求逻辑,具体的实现方式交给ServiceMethod。

 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 serviceMethod.invoke(args);
            }
        });
    }
@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);
​
        //对interfaces接口做拷贝
        final Class<?>[] intfs = interfaces.clone();
        
        // Android-removed: SecurityManager calls
        /*
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        */
​
        /*
         * Look up or generate the designated proxy class.
         */
         //根据拷贝的接口和类加载器生成代理类
        Class<?> cl = getProxyClass0(loader, intfs);
​
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            // Android-removed: SecurityManager / permission checks.
            /*
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            */
​
            //获取代理类的构造方法
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                // BEGIN Android-changed: Excluded AccessController.doPrivileged call.
                /*
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
                */
​
                cons.setAccessible(true);
                // END Android-removed: Excluded AccessController.doPrivileged call.
            }
            //返回由构造方法实例化的代理对象
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

实现Retrofit第三步

在第二步利用动态代理获取代理对象的create方法回调中的invoke方法中实现http请求,这一步讲讲ServiceMethod如何实现http请求;

ServiceMethod要根据create传入的api请求接口

public interface Apitest1 {
    @POST("/v3/weather/weatherInfo")
    Call postTest1(@Field("city") String city, @Field("key") String key);
​
    @GET("/v3/weather/weatherInfo")
    Call getTest1(@Query("city") String city, @Query("key") String key);
}

通过反射获取Apitest1接口中声明方法的注解和方法参数上的注解获得请求方式,请求url,请求参数等,利用OkHttp发送这次请求。

ServiceMethod类的对象也是通过构建者模式建立起来的,在ServiceMethod类中定义内部类Builder。

定义Builder的构造方法,传参Method和MyRetrofit 在构造方法中做两件事情

第一 ,通过反射method.getDeclaredAnnotations()获取方法上的注解,得到请求方式(post or get),请求url。

第二,通过method.getParameterAnnotations()获得方法参数的注解,得到请求参数(这里的请求参数是 @Query("city")中的city),然后根据不同注解类型将获取的注解值放入不同的ParamHandler中,Field类型放FieldParamHandler,Query类型放QueryParamHandler中。

最后build()方法返回ServiceMethod实例。

在invoke方法中传入参数为Apitest1声明的函数中的参数,再将参数和ParamHandler数组构建成请求体formBody,最后由url,formBody和请求方式创建request请求,最后用OKhttp框架中factory.newCall(request)执行请求。


public class ServiceMethod {
    private static HttpUrl.Builder formBuilder;
    private static FormBody.Builder formBodyBuilder;
    private Call.Factory factory;
    private HttpUrl baseUrl;
    private String relativeUrl;
    private Boolean hasBody;
    private String httpMethod;
    private ParamHandler[] paramHandlers;
​
    public ServiceMethod(Builder builder){
        this.factory = builder.factory;
        this.baseUrl = builder.baseUrl;
        this.relativeUrl = builder.relativeUrl;
        this.hasBody = builder.hasBody;
        this.httpMethod = builder.httpMethod;
        this.paramHandlers = builder.methodParamHandler;
    }
​
    public Object invoke(Object[] args) {
        for (int i = 0; i < paramHandlers.length; i++) {
            paramHandlers[i].apply(this, (String) args[i]);
        }
        if(formBuilder == null){
            formBuilder = baseUrl.newBuilder(relativeUrl);
        }
        HttpUrl url = formBuilder.build();
        if(formBodyBuilder == null){
            formBodyBuilder = new FormBody.Builder();
        }
        FormBody formBody = null;
        if(hasBody){
            formBody = formBodyBuilder.build();
        }
        Request request = new Request.Builder().url(url).method(httpMethod,formBody).build();
        return factory.newCall(request);
    }
​
    public static void addParamToFormBody(String key,String value){
        if(formBodyBuilder == null){
            formBodyBuilder = new FormBody.Builder();
        }
        formBodyBuilder.add(key,value);
    }
​
    public void addParamToQueryFormBody(String key, String value){
        if(formBuilder == null){
            formBuilder = baseUrl.newBuilder(relativeUrl);
        }
        formBuilder.addQueryParameter(key,value);
    }
​
    public static class Builder{
        private  ParamHandler[] methodParamHandler;
        private Annotation[] methodOverAnnotations;
        private Annotation[][] methodAnnotations;
        private Call.Factory factory;
        private HttpUrl baseUrl;
        private String relativeUrl;
        private Boolean hasBody;
        private String httpMethod;
        public Builder(Method method,MyRetrofit myRetrofit){
            this.methodOverAnnotations = method.getDeclaredAnnotations();
            this.methodAnnotations = method.getParameterAnnotations();
            this.factory = myRetrofit.factory;
            this.baseUrl = myRetrofit.baseUrl;
            for (Annotation methodOverAnnotation : methodOverAnnotations) {
                if(methodOverAnnotation instanceof POST){
                    this.hasBody = true;
                    this.httpMethod = "POST";
                    this.relativeUrl = ((POST) methodOverAnnotation).value();
                }else if(methodOverAnnotation instanceof GET){
                    this.httpMethod = "GET";
                    this.hasBody = false;
                    this.relativeUrl = ((GET) methodOverAnnotation).value();
                }
​
            }
            int length = methodAnnotations.length;
            methodParamHandler = new ParamHandler[length];
            for (int i = 0; i < length; i++) {
                Annotation[] annotations = methodAnnotations[i];
                for (Annotation annotation : annotations) {
                    if(annotation instanceof Field){
                        methodParamHandler[i] = new ParamHandler.FieldParamHandler(((Field) annotation).value());
                    } else if(annotation instanceof Query){
                        methodParamHandler[i] = new ParamHandler.QueryParamHandler(((Query) annotation).value());
                    }
                }
            }
        }
​
        public ServiceMethod build(){
            return new ServiceMethod(this);
        }
​
    }
}
​

 

标签:demo,Builder,factory,private,public,十分钟,baseUrl,new,Retrofit
来源: https://www.cnblogs.com/phyger/p/14029570.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有