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

源代码
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

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

推荐阅读更多精彩内容