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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 229,836评论 6 540
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,275评论 3 428
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 177,904评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,633评论 1 317
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,368评论 6 410
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,736评论 1 328
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,740评论 3 446
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,919评论 0 289
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,481评论 1 335
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,235评论 3 358
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,427评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,968评论 5 363
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,656评论 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,055评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,348评论 1 294
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,160评论 3 398
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,380评论 2 379

推荐阅读更多精彩内容