zoukankan      html  css  js  c++  java
  • 十分钟教你手撸一个简单的Retrofit demo

    十分钟教你手撸一个简单的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);
            }
    ​
        }
    }
    ​
  • 相关阅读:
    初识python 2.x与3.x 区别
    装饰器
    函数的进阶
    Spring Boot启动问题:Cannot determine embedded database driver class for database type NONE
    22.Spring Cloud Config安全保护
    23.Spring Cloud Bus 无法更新问题(踩坑) Spring cloud config server Could not fetch remote for master remote
    24.Spring Cloud之Spring Cloud Config及Spring Cloud Bus
    Spring Boot整合Spring Data Elasticsearch 踩坑
    项目中Spring Security 整合Spring Session实现记住我功能
    32.再谈SpringBoot文件上传
  • 原文地址:https://www.cnblogs.com/phyger/p/14029570.html
Copyright © 2011-2022 走看看