美文网首页待写
Android知名三方库Retrofit(二) - 手写核心代

Android知名三方库Retrofit(二) - 手写核心代

作者: 信仰年輕 | 来源:发表于2021-07-01 22:44 被阅读0次

    源代码
    GitHub源代码

    本文目标

    Retrofit核心代码简易版手写实现(仅供学习)

    基本用法

        public void click(View view) {
            RetrofitClient
                    .getServiceApi()
                    .userLogin("yd", "123456")
                    .enqueue(new Callback<UserLoginResult>() {
                        @Override
                        public void onResponse(Call<UserLoginResult> call, Response<UserLoginResult> response) {
                            final String result = response.body.toString();
                            Log.i("TAG",result);
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    Toast.makeText(MainActivity.this,result,Toast.LENGTH_SHORT).show();
                                }
                            });
                        }
    
                        @Override
                        public void onFailure(Call<UserLoginResult> call, Throwable t) {
                            Log.e("TAG",t.getMessage());
                        }
                    });
        }
    

    从最简单最基础的用法开始入手,首先先看RetrofitClient

    RetrofitClient

    /**
     * Author: 信仰年轻
     * Date: 2021-07-02 15:02
     * Email: hydznsqk@163.com
     * Des:
     */
    public class RetrofitClient {
    
        private final static ServiceApi mServiceApi;
    
        static {
            //1.首先创建了一个OkHttpClient的对象
            OkHttpClient okHttpClient = new OkHttpClient
                    .Builder()
                    .addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
                        @Override
                        public void log(String message) {
                            Log.i("TAG", message);
                        }
                    }).setLevel(HttpLoggingInterceptor.Level.BODY))
                    .build();
    
            //2.然后创建Retrofit类设置baseUrl并把OkHttpClient给添加进去
            Retrofit retrofit = new Retrofit
                    .Builder()
                    // 访问后台接口的主路径
                    .baseUrl("https://www.fastmock.site/mock/b5b5b4f8bf5a7178e46771346c7940ca/YdHttpServer/")
                    // 添加 OkHttpClient,不添加默认就是 光杆 OkHttpClient
                    .client(okHttpClient)
                    .build();
    
            //3.创建一个 实例对象
            mServiceApi = retrofit.create(ServiceApi.class);
        }
    
        public static ServiceApi getServiceApi() {
            return mServiceApi;
        }
    }
    
    • 1.首先创建了一个OkHttpClient的对象
    • 2.然后创建Retrofit类设置baseUrl并把OkHttpClient给添加进去
    • 3.retrofit去创建ServiceApi接口的实例对象
      基本用法都看完了,然后我们开始看 Retrofit

    1.Retrofit

    /**
     * Author: 信仰年轻
     * Date: 2021-07-01 17:44
     * Email: hydznsqk@163.com
     * Des: 1.动态代理
     *      2.解析方法上的注解和解析参数上的注解
     *      3.封装OkHttp请求
     */
    public class Retrofit {
    
        String mBaseUrl;
        okhttp3.Call.Factory mCallFactory;
        private Map<Method, ServiceMethod> serviceMethodMap = new ConcurrentHashMap<>();
    
        public Retrofit(Builder builder) {
            this.mBaseUrl = builder.baseUrl;
            this.mCallFactory = builder.callFactory;
        }
    
        /**
         * 1.动态代理
         */
        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 {
                    //每执行一个方法都会来到这里
                    // 判断是不是 Object 的方法
                    if (method.getDeclaringClass() == Object.class) {
                        return method.invoke(this, args);
                    }
                    //2.解析方法上的注解和解析参数上的注解
                    ServiceMethod serviceMethod = loadServiceMethod(method);
                    //3.封装OkHttp请求
                    OkHttpCall okHttpCall = new OkHttpCall(serviceMethod, args);
                    return okHttpCall;
                }
            });
        }
    
        /**
         * 2.解析方法上的注解和解析参数上的注解
         */
        private ServiceMethod loadServiceMethod(Method method) {
            ServiceMethod serviceMethod = serviceMethodMap.get(method);
            if (serviceMethod == null) {
                //创建ServiceMethod,把Retrofit和Method都传递进去进行解析
                serviceMethod = new ServiceMethod.Builder(this, method).build();
                serviceMethodMap.put(method, serviceMethod);
            }
            return serviceMethod;
        }
    
        public static class Builder {
            String baseUrl;
            okhttp3.Call.Factory callFactory;
    
            public Builder baseUrl(String baseUrl) {
                this.baseUrl = baseUrl;
                return this;
            }
    
            public Builder client(okhttp3.Call.Factory factory) {
                this.callFactory = factory;
                return this;
            }
    
            public Retrofit build() {
                if (callFactory == null) {
                    callFactory = new OkHttpClient();
                }
                return new Retrofit(this);
            }
        }
    }
    

    Retrofit类一共做了3件事情

    • 1.动态代理
    • 2.解析方法上的注解和解析参数上的注解
    • 3.封装OkHttp请求

    第1步,动态代理其目的就是为了解耦,每执行一个接口方法都会来到动态代理的invoke方法,代码如下

        /**
         * 1.动态代理
         */
        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 {
                    //每执行一个方法都会来到这里
                    // 判断是不是 Object 的方法
                    if (method.getDeclaringClass() == Object.class) {
                        return method.invoke(this, args);
                    }
                    //2.解析方法上的注解和解析参数上的注解
                    ServiceMethod serviceMethod = loadServiceMethod(method);
                    //3.封装OkHttp请求
                    OkHttpCall okHttpCall = new OkHttpCall(serviceMethod, args);
                    return okHttpCall;
                }
            });
        }
    

    第2步,解析方法上的注解和参数上的注解,具体代码如下

        private Map<Method, ServiceMethod> serviceMethodMap = new ConcurrentHashMap<>();
        /**
         * 2.解析方法上的注解和解析参数上的注解
         */
        private ServiceMethod loadServiceMethod(Method method) {
            ServiceMethod serviceMethod = serviceMethodMap.get(method);
            if (serviceMethod == null) {
                //创建ServiceMethod,把Retrofit和Method都传递进去进行解析
                serviceMethod = new ServiceMethod.Builder(this, method).build();
                serviceMethodMap.put(method, serviceMethod);
            }
    
            return serviceMethod;
        }
    

    第3步,就是封装成OkHttpCall请求

    //3.封装OkHttp请求
    OkHttpCall okHttpCall = new OkHttpCall(serviceMethod, args);
    

    我们来看下方法上的注解和参数上的注解是如何被解析的,来看ServiceMethod这个类

    2.ServiceMethod,解析注解

    /**
     * Author: 信仰年轻
     * Date: 2021-07-01 18:49
     * Email: hydznsqk@163.com
     * Des: 解析方法上的注解和参数上的注解
     */
    public class ServiceMethod {
    
        private final Retrofit mRetrofit;
        private final Method mMethod;
        private String mHttpMethod; //请求的方式 是GET还是POST
        private String mRelativeUrl;//相对路径
        private final ParameterHandler[] mParameterHandlers;
    
        public ServiceMethod(Builder builder) {
            this.mRetrofit = builder.mRetrofit;
            this.mMethod = builder.mMethod;
            this.mHttpMethod = builder.mHttpMethod;
            this.mRelativeUrl = builder.mRelativeUrl;
            this.mParameterHandlers = builder.mParameterHandlers;
        }
    
        /**
         * 创建一个新的Call
         */
        public okhttp3.Call createNewCall(Object[] args) {
            //把基础url,相对url,请求方式(get或post),mParameterHandler和真正的参数传递进去
            RequestBuilder requestBuilder = new RequestBuilder(mRetrofit.mBaseUrl, mRelativeUrl, mHttpMethod, mParameterHandlers, args);
            return mRetrofit.mCallFactory.newCall(requestBuilder.build());
        }
    
        /**
         * 解析ResponseBody
         */
        public <T> T parseBody(ResponseBody responseBody) {
            // 获取解析类型 T 获取方法返回值的类型
            Type returnType = mMethod.getGenericReturnType();// 返回值对象
            Class <T> dataClass = (Class <T>) ((ParameterizedType) returnType).getActualTypeArguments()[0];
            // 解析工厂去转换
            Gson gson = new Gson();
            T body = gson.fromJson(responseBody.charStream(),dataClass);
            return body;
        }
    
        public static class Builder {
    
            private final Retrofit mRetrofit;
            private final Method mMethod;
    
            private String mHttpMethod; //请求的方式 是GET还是POST
            private String mRelativeUrl;//相对路径
    
            private final Annotation[] mMethodAnnotations;
            private final Annotation[][] mParameterAnnotations;
            private final ParameterHandler[] mParameterHandlers;
    
            public Builder(Retrofit retrofit, Method method) {
                this.mRetrofit = retrofit;
                this.mMethod = method;
                //方法注解的数组
                mMethodAnnotations = method.getAnnotations();
                //参数注解的二维数组,为什么是二维数组呢,因为1个参数上有可能会有多个注解,所以每个参数对应一个数组,那多个参数就是二维数组了
                mParameterAnnotations = method.getParameterAnnotations();
                mParameterHandlers = new ParameterHandler[mParameterAnnotations.length];
            }
    
            public ServiceMethod build() {
                //1.解析方法上的注解
                for(Annotation annotation:mMethodAnnotations){
                    parseAnnotationMethod(annotation);
                }
                //2.解析参数注解
                for(int x=0;x<mParameterAnnotations.length;x++){
                    Annotation annotation = mParameterAnnotations[x][0];
                    //在这里只有Query这个注解
                    if(annotation instanceof Query){
                        // 一个一个封装成 ParameterHandler,不同的参数注解选择不同的策略
                        //传进去的就是 参数的 key = userName ,password
                        mParameterHandlers[x]=new ParameterHandler.Query(((Query) annotation).value());
                    }
                }
                return new ServiceMethod(this);
            }
    
            //1.解析方法上的注解
            private void parseAnnotationMethod(Annotation annotation) {
                if(annotation instanceof GET){
                    parseMethodAndPath("GET",((GET) annotation).value());
                }else if(annotation instanceof POST){
                    parseMethodAndPath("POST",((POST) annotation).value());
                }
                //还有一大堆其他解析...
            }
    
            private void parseMethodAndPath(String method, String value) {
                this.mHttpMethod =method;
                this.mRelativeUrl =value;
            }
        }
    }
    

    可以发现是在Builder内部类的build()方法去解析方法上的注解和参数注解的,解析完成后给赋值成成员变量
    到这里,注解上的value已经被解析出来了,包括是什么请求GET还是POST,相对路径,以及Querykeyvalue然后给赋值给成员变量
    在解析参数注解的时候我们需要用到ParameterHandler这个类,因为会有很多不同的注解,我们需要一个一个封装成 ParameterHandler,不同的参数注解选择不同的策略,在这里只是写了Query注解的处理策略

    /**
     * Author: 信仰年轻
     * Date: 2021-07-02 14:52
     * Email: hydznsqk@163.com
     * Des:
     */
    public interface ParameterHandler<T> {
        void apply(RequestBuilder requestBuilder, T value);
    
        class Query<T> implements ParameterHandler<T> {
            private String key; // 保存 就是参数的 key = userName ,password
            public Query(String key) {
                this.key = key;
            }
            @Override
            public void apply(RequestBuilder requestBuilder, T value) {
                requestBuilder.addQueryName(key, value.toString());
            }
        }
    }
    

    3.OkHttpCall

    /**
     * Author: 信仰年轻
     * Date: 2021-07-01 18:58
     * Email: hydznsqk@163.com
     * Des:
     */
    public class OkHttpCall<T> implements Call<T> {
    
        private ServiceMethod mServiceMethod;
        private Object[] mArgs;
    
        public OkHttpCall(ServiceMethod serviceMethod, Object[] args) {
            this.mServiceMethod = serviceMethod;
            this.mArgs = args;
        }
    
        @Override
        public void enqueue(final Callback<T> callback) {
            // 发起一个请求,给一个回调就完结了
            Log.e("TAG", "正式发起请求");
            //1.建一个新的Call
            okhttp3.Call call = mServiceMethod.createNewCall(mArgs);
            //2.发起请求
            call.enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(okhttp3.Call call, IOException e) {
                    if (callback != null) {
                        callback.onFailure(OkHttpCall.this, e);
                    }
                }
    
                @Override
                public void onResponse(okhttp3.Call call, Response response) throws IOException {
                    //3.解析数据
                    //涉及到解析,不能在这里写上,ConvertFactory
                    com.retrofit.write.retrofit.Response objectResponse = new com.retrofit.write.retrofit.Response();
                    objectResponse.body = mServiceMethod.parseBody(response.body());
                    if (callback != null) {
                        callback.onResponse(OkHttpCall.this, objectResponse);
                    }
                }
            });
        }
    }
    

    该类的enqueue方法也只是干了3件事

    • 1.建一个新的Call
    • 2.发起请求
    • 3.解析数据

    createNewCall方法中我们需要使用到RequestBuilder类来创建Request对象

    public class Retrofit {
       ......
        /**
         * 创建一个新的Call
         */
        public okhttp3.Call createNewCall(Object[] args) {
            //把基础url,相对url,请求方式(get或post),mParameterHandler和真正的参数传递进去
            RequestBuilder requestBuilder = new RequestBuilder(mRetrofit.mBaseUrl, mRelativeUrl, mHttpMethod, mParameterHandlers, args);
            return mRetrofit.mCallFactory.newCall(requestBuilder.build());
        }
       ......
    }
    
    /**
     * Author: 信仰年轻
     * Date: 2021-07-02 14:53
     * Email: hydznsqk@163.com
     * Des:
     */
    public class RequestBuilder {
        ParameterHandler<Object>[] mParameterHandlers;
        Object[] args;
        HttpUrl.Builder httpUrl;
    
        public RequestBuilder(String baseUrl, String relativeUrl, String httpMethod, ParameterHandler[] parameterHandlers, Object[] args) {
            this.mParameterHandlers = (ParameterHandler<Object>[]) parameterHandlers;
            this.args = args;
            this.httpUrl = HttpUrl.parse(baseUrl+relativeUrl).newBuilder();
        }
    
        public Request build() {
            int count = args.length;
            for (int i=0;i < count;i++) {
                // userName = yd
                mParameterHandlers[i].apply(this,args[i]);
            }
            // POST 等等
            Request request = new Request
                    .Builder()
                    .url(httpUrl.build())
                    .build();
            return request;
        }
    
        //https://www.fastmock.site/mock/b5b5b4f8bf5a7178e46771346c7940ca/YdHttpServer/login?userName=yd&password=123456
        public void addQueryName(String key, String value) {
            // userName = yd&password = 123456
            httpUrl.addQueryParameter(key,value);
        }
    
    }
    

    最后就是请求成功之后的解析数据了

    public class Retrofit {
       ......
        /**
         * 解析ResponseBody
         */
        public <T> T parseBody(ResponseBody responseBody) {
            // 获取解析类型 T 获取方法返回值的类型
            Type returnType = mMethod.getGenericReturnType();// 返回值对象
            Class <T> dataClass = (Class <T>) ((ParameterizedType) returnType).getActualTypeArguments()[0];
            // 解析工厂去转换
            Gson gson = new Gson();
            T body = gson.fromJson(responseBody.charStream(),dataClass);
            return body;
        }
       ......
    }
    

    最后通过回调接口把数据返回出去,整个流程就结束了,具体可以参考Demo

    相关文章

      网友评论

        本文标题:Android知名三方库Retrofit(二) - 手写核心代

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