手寫簡易retrofit

首先我們知道retrofit底層是基于OkHttp的:
接著我們通過一個測試類,看一下retrofit的一般使用方法:

public class RetrofitTest {
    interface Weather{
        @GET("/v3/weather/weatherInfo")
        Call get(@Query("city") String city, @Query("key") String key);
    }
    @Test
    public void test(){
        Retrofit retrofit = new Retrofit.Builder().baseUrl("http://restapi.amap.com/").build();
        Weather weather = retrofit.creat(Weather.class);
        Call call = weather.get("北京", "13cb58f5884f9749287abbead9c658f2");
        try {
            Response response = call.execute();
            System.out.println(response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

我們可以看出Retrofit是通過Builder模式生成的,這樣的優(yōu)點應(yīng)該都知道,不僅能幫我們初始化一系列的參數(shù),還可以讓調(diào)用者不用過于關(guān)心內(nèi)部構(gòu)造。
接著我們看Retrofit的creat傳入的是一個接口類,所以這里應(yīng)該是會用到代理模式,通過代理模式拿到Weather的方法,再通過反射的方法獲取到我們需要的地址的后綴和我們需要傳遞的參數(shù)。

public class Retrofit {
    private HttpUrl baseUrl;
    private Call.Factory callFactory;

   private ConcurrentHashMap<Method,ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();

    public Retrofit(Builder builder) {
        this.baseUrl = builder.baseUrl;
        this.callFactory = builder.callFactory;
    }

    public HttpUrl baseUrl() {
        return baseUrl;
    }

    public Call.Factory callFactory() {
        return callFactory;
    }

    public <T> T creat(Class<T> clazz){
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                       
                        //采集數(shù)據(jù)
                        ServiceMethod serviceMethod = loadServiceMethod(method);

                        return serviceMethod.toCall(args);
                    }
                });
    }

    private ServiceMethod loadServiceMethod(Method method){
        ServiceMethod serviceMethod = serviceMethodCache.get(method);

        if(null == serviceMethod){
            //采集
            serviceMethod = new ServiceMethod.Builder(this,method).bulid();
            serviceMethodCache.putIfAbsent(method,serviceMethod);
        }
        return serviceMethod;
    }

    /*
        構(gòu)建者模式
         */
    public static final class Builder{

        private HttpUrl baseUrl;
        private Call.Factory callFactory;

        public Builder baseUrl(String baseUrl){
            this.baseUrl = HttpUrl.parse(baseUrl);
            return this;
        }
        public Builder callFactory(Call.Factory callFactory){
            this.callFactory = callFactory;
            return this;
        }


        public Retrofit build(){
            if(baseUrl == null){
                throw new IllegalArgumentException("baseUrl not set");
            }
            if(callFactory == null){
                callFactory = new OkHttpClient();
            }
            return new Retrofit(this);
        }


    }

}

這里有幾點需要說一下:
1、creat方法:其實creat方法主要的功能就是通過動態(tài)代理獲取到接口類的實例,然后通過實例獲取到請求方法的各個參數(shù)(包括注解上面的地址后綴和每個接口需要傳遞的參數(shù));我在這里把這些放到了一個緩存中,這樣也可以加快效率。在
2、callFactory方法:其實retrofit中這種工廠設(shè)計模式使我們想要使用它的理由之一,它的這種插拔式的使用讓我們可以把數(shù)據(jù)轉(zhuǎn)為Gson或者Xml等類型的數(shù)據(jù),并且還可以和RxJava組合使用,這里沒有在向外延伸。
3、因為調(diào)用weather的get方法返回的是Call對象,所以需要在creat方法中的invoke中返回Call對象。這里的Call是okhttp3包下的。

public Call toCall(Object[] args) {
    //1.創(chuàng)建Request
    Request.Builder requestBuilder = new Request.Builder();
    //1.1地址
    if (urlBuilder == null) {
        urlBuilder = baseUrl.newBuilder(relativeUrl);
    }
    //1.2如果是get請求,將參數(shù)放到地址中

    for (int i = 0; i < parameterHandlers.length; i++) {
        parameterHandlers[i].apply(this, String.valueOf(args[i]));

    }

    if (formBuilder != null) {
        formBody = formBuilder.build();
    }
    requestBuilder.url(urlBuilder.build());
    Request request = requestBuilder.method(httpMethod, formBody).build();
    //2.創(chuàng)建call
    return  callFactory.newCall(request);
}

4、因為獲取到方法上面的參數(shù)需要拼接和判斷(這里只做了GET和POST)。所以用了一個serviceMethod來處理這一系列的問題;

public ServiceMethod bulid() {
    //處理方法的注解
    for (Annotation annotation : methodAnnotation) {
        //GET
        paseMethodAnnotation(annotation);
    }
    //處理參數(shù)的注解
    parameterHandlers = new ParameterHandler[parameterAnnotations.length];
    //遍歷參數(shù)注解
    for (int i = 0; i < parameterAnnotations.length; i++) {
        Annotation[] parameterAnnotation = parameterAnnotations[i];
        //遍歷一個參數(shù)上的左右注解
        for (Annotation annotation : parameterAnnotation) {
            if (annotation instanceof Query) {
                Query query = (Query) annotation;
                String value = query.value();
                parameterHandlers[i] = new ParameterHandler.Query(value);
            } else if (annotation instanceof Field) {
                Field field = (Field) annotation;
                String value = field.value();
                parameterHandlers[i] = new ParameterHandler.Filed(value);
            }
        }
    }
    return new ServiceMethod(this);
}

這樣的話我們把地址和參數(shù)都拼裝好了,而且也獲取到Call對象了,然后執(zhí)行call.execute()就可以獲取到Response了。

try {
    Response response = call.execute();
    System.out.println(response.body().string());
} catch (IOException e) {
    e.printStackTrace();
}

總結(jié):對于不理解retrofit的人來說,剛開始對于retrofit的用法和原理都是一頭霧水,還有一些人都是停止在會用retrofit的方法之上就沒有深入研究。其實可以簡單的對retrofit理解為:它就是對okhttp的一個封裝,只不過可擴展性更強。內(nèi)部的根本原理就是OkHttp。因為內(nèi)部對于請求方法的類使用了代理模式獲取請求地址和想要傳遞的參數(shù),所以請求方法類必須使用接口,并且在Retrofit的內(nèi)部使用 ConverterFactory工廠類來讓用戶選擇返回數(shù)據(jù)的格式(可以是Gson,也可以是XML等各種數(shù)據(jù)格式),更加的方便靈活。甚至還可以通過CallAdapterFactory的適配器模式來與RxJava組合使用,這就是靈活之處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,063評論 25 709
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 14,007評論 2 59
  • 目錄介紹 1.首先回顧Retrofit簡單使用方法 2.Retrofit的創(chuàng)建流程源碼分析2.1 Retrofit...
    楊充211閱讀 1,177評論 0 16
  • 旅行,其實就是離開熟悉的城市去陌生地體驗新鮮。因為新鮮我們總是向往,也因為陌生我們總是防范。青島的天價大...
    玫苑姑娘閱讀 399評論 1 1
  • 假期說完就完,又到了開工的日子,很多人開始不適應(yīng),整天呆在辦公室,是不是覺得腿部和臀部都特別僵硬呢? 今天來學學這...
    Rebecca小零閱讀 356評論 0 0

友情鏈接更多精彩內(nèi)容