网易云微专业安卓-解耦的套路,核心的代理,手撸Retrofit核动力

文章纯属个人学习的代码实现

网易云微专业公开课这节课 手写了这个Retrofit的简单实现,包含了基本的功能,能正常网络请求

我们先看一段 Retrofit请求的基本形式,代码如下

public class RetrofitTest {
    private final static String IP = "144.34.161.97";
    private final static String KEY = "aa205eeb45aa76c6afe3c52151b52160";
    private final static String BASE_URl = "http://apis.juhe.cn/";

    interface HOST {
        @GET("/ip/ipNew")
        Call get(@Query("ip") String ip, @Query("key") String key);
    }

    @Test
    public void retrofitTest() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URl)
                .build();
        HOST host = retrofit.create(HOST.class);
        Call call = host.get(IP, KEY);
        try {
            Response response = call.execute();
            if (response != null && response.body() != null) {
                System.out.println(response.body().string());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

这里包含三个部分
第一部分

    private final static String IP = "144.34.161.97";
    private final static String KEY = "aa205eeb45aa76c6afe3c52151b52160";
    private final static String BASE_URl = "http://apis.juhe.cn/";

这里主要是我们构建请求的一些基本变量
第二部分

 interface HOST {
        @GET("/ip/ipNew")
        Call get(@Query("ip") String ip, @Query("key") String key);
    }

熟悉Retrofit的都知道 ,这是Retrofitrestful风格的一种请求接口约定方式

第三部分

@Test
    public void retrofitTest() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URl)
                .build();
        HOST host = retrofit.create(HOST.class);
        Call call = host.get(IP, KEY);
        try {
            Response response = call.execute();
            if (response != null && response.body() != null) {
                System.out.println(response.body().string());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

这里就是我们用Retrofit构建一个基本的 请求的过程
首先我们需要一个 Retrofit对象

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URl)
                .build();

这里主要是通过一个工厂模式配置一些基本信息,比如请求地址的域名,可以看看合理的基本实现

public static class Builder {
        private HttpUrl baseUrl;
        private Call.Factory  callFactory;

        public Builder baseUrl(String base_uRl) {
            if (base_uRl.isEmpty()){
                throw new NullPointerException("baseUrl为空");
            }
            this.baseUrl= HttpUrl.parse(base_uRl);
            return this;
        }

        public Builder baseUrl(HttpUrl base_uRl) {
            if (base_uRl==null){
                throw new NullPointerException("baseUrl为空");
            }
            this.baseUrl= base_uRl;
            return this;
        }

        public Retrofit build() {
            if(this.baseUrl==null){
                throw new IllegalStateException("baseUrl 必须提供");
            }
            if(this.callFactory==null){
                 callFactory=new OkHttpClient();
            }
            return new Retrofit(this);
        }
    }

主要看看这个build()方法,初始化了baseUrlOkHttpClient对象,这个是OkHttpClient对象是用来执行Okhttp网络请求的


我们接着看着里

 HOST host = retrofit.create(HOST.class);

通过我们刚刚构造出来的retrofit对象的create方法去返回一个 HOST对象,我们可以看看这里怎么实现的。

public <T> T create(Class<T> t) {
        return (T) Proxy.newProxyInstance(t.getClassLoader(), new Class[]{t}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //在动态代理里面获取方法请求的相关参数和注解
                ServiceMethod serviceMethod=loadServiceMethod(method);
                //然后做请求
                return new OkhttpCall(serviceMethod,args);
            }
        });
    }

我们可以看到这其实就是动态代理,通过代理的形式实现AOP切入,在用户调用这句话Call call = host.get(IP, KEY);的时候,这里的invoke方法 就会被调用,从而实现参数拦截,方法前后处理等功能。

而这里其实就是通过loadServiceMethod(method)方法来完成参数和请求类型等解析。

主要方式当然是用到反射。我们可以大概看看代码

 private ServiceMethod loadServiceMethod(Method method) {
        //先去取  如果缓存里面有咱们就返回出去
        ServiceMethod serviceMethod=serviceMethodMap.get(method);
        if(serviceMethod!=null)return serviceMethod;

        //如果没有  我们就需要注意线程同步问题
        synchronized (serviceMethodMap){
            serviceMethod=serviceMethodMap.get(method);
            if(serviceMethod==null){
                serviceMethod=new ServiceMethod.Builder(this,method).build();
            }
        }

        return serviceMethod;

    }

这里只做了一个取缓存里面的method的一个处理,可以看出来这里加了一个synchronized (serviceMethodMap)防止线程同步出现问题。

核心解析参数啥的都在ServiceMethod.Builder(this,method).build()里面,我们接着看

   public Builder(Retrofit retrofit, Method method) {
           this.retrofit=retrofit;
           this.method=method;
           this.methodAnnotations=method.getAnnotations();
           this.parameterAnnotationsArray=method.getParameterAnnotations();
        }
 public ServiceMethod build() {
            for (Annotation methodAnnotation : methodAnnotations) {
                  //解析方法注解 比如GET POST
                  parseMethodAnnotation(methodAnnotation);
            }

            //开始解析方法参数注解
            int parameterLength = parameterAnnotationsArray.length;
            parameterHandlers = new ParameterHandler[parameterLength];
            for (int i = 0; i < parameterLength; i++) {
                //获取方法的参数的所有注解
                Annotation[] annotations = parameterAnnotationsArray[i];
                if(annotations == null){
                    throw new NullPointerException("不合规矩");
                }
                //开始解析Query这些参数
                parameterHandlers[i] = parseParameter(i,annotations);
            }
            return  new ServiceMethod(this);

        }

我们关注这两行代码

    this.methodAnnotations=method.getAnnotations();
           this.parameterAnnotationsArray=method.getParameterAnnotations();

这其实就是反射里面的获取方法的注释和获取方法的参数注解。

我们再继续看看ServiceMethod里的的核心方法

 /**
     * 最后要的一个方法 实际生成一个 请求对象
     */
    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());
    }

看不懂没关系,我就告诉你,是做了参数传值,然后构建了一个Request对象,然后用callFactory.newCall(requestBuilder.build());完成请求,这个callFactory其实就是我们Retrofit里面传入的OkHttpClient对象
然后我们基本就完成了请求
大家想了解更多直接去我的github看代码实现

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Retrofit 是目前作为网络请求的主流框架,使用起来很方便,仅需在接口中定义方法,打上注解,而且和 Rxjav...
    Kip_Salens阅读 3,736评论 0 3
  • 安卓开发领域中,很多重要的问题都有很好的开源解决方案,例如Square公司提供网络请求 OkHttp , Retr...
    aaron688阅读 5,894评论 1 20
  • Retrofit 2 源码解析 关于Retrofit 2的使用请看上一篇https://www.jianshu.c...
    gogoingmonkey阅读 3,550评论 0 1
  • Retrofit简介 Retrofit是一个基于OKHttp的RESTful网络请求框架。简单来说,Retrofi...
    htkeepmoving阅读 3,085评论 0 0
  • N一114 也许因为自身长的丑, 所以对美好像没什么概念。 人,感觉都一样, 只是对有才气之人特别喜欢, 对有才气...
    我是真幸福阅读 2,472评论 0 2