Retrofit 筆記

一開(kāi)始的目的是為了學(xué)習(xí)動(dòng)態(tài)代理的使用,后來(lái)干脆把整個(gè)流程梳理了下

這不是完整的Retrofit厅翔,是純Java環(huán)境下不涉及Android,Http的流程代碼,Retrofit的精簡(jiǎn)代碼凌那,把內(nèi)部一些設(shè)計(jì)和邏輯做了精簡(jiǎn)阻问,主要是為了學(xué)習(xí)整個(gè)代碼的大體設(shè)計(jì)和流程

Retrofit內(nèi)關(guān)于OkHttp、Rx部默、Android相關(guān)的部分都去除了侵蒙,其中okhttp3.call由一個(gè)RawCall來(lái)代替,內(nèi)部沒(méi)有做相關(guān)網(wǎng)絡(luò)請(qǐng)求傅蹂,只是做了下字符串拼接后println()纷闺,盡量還保持著Retrofit原有的類,有些地方份蝴,把注釋也給粘貼過(guò)來(lái)了

Demo完整代碼——>GitHub RetrofitDemo


1. Demo

使用也基本Retrofit的形式犁功,Demo本身并無(wú)任何意義

1.1 Service

/** 獲取幣種Coin Service 接口 */
public interface IBtcService {
    /** 獲取 BTC 信息 */
    Call<String> getBtcCoinInfo(@Content String content);
}

@Content是自己定義的一個(gè)注解掀宋,表明方法內(nèi)是Content,作用類比@Path等硼一,支持多個(gè)不同類型的Content,例如

getBtcCoinInfo(@Content String content1, @Content int content2)

返回類型必須是Call<>


1.2 使用

public class RetrofitDemo {
    public static void main(String[] args) {
        btcInfo();
    }

    /** BTC 信息 */
    private static void btcInfo() {
        Retrofit retrofit = new Retrofit.Builder().build();
        IBtcService iBtcService = retrofit.create(IBtcService.class);
        Call<String> call = iBtcService.getBtcCoinInfo("BTC $3512.6 ");
        try {
            Response<String> response = call.execute();
            System.out.println(response.body());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

結(jié)果就是Content前拼接當(dāng)前時(shí)間

2019-1-27 12:01:27
BTC $3512.6 

2. 流程

基于Retrofit2.5.0版本如失,整個(gè)流程的代碼都在core文件夾下

整個(gè)流程涉及有4個(gè)重要部分Retrofit,CallAdapter,Converter,OkHttpCall

Retrofit作為門面案糙,關(guān)鍵點(diǎn)之一是crete()方法限嫌,使用動(dòng)態(tài)代理通過(guò)CallAdatperRetrofit.Call轉(zhuǎn)換為一個(gè)Service

Converter轉(zhuǎn)換器,Request BodyResponse Body都要用

OkHttpCall侍筛,在實(shí)際的Retrofit中萤皂,持有著一個(gè)OkHttp3.Call,最終所有的有關(guān)網(wǎng)絡(luò)請(qǐng)求的操作匣椰,都會(huì)在這里執(zhí)行裆熙。由于我這里將網(wǎng)絡(luò)請(qǐng)求去掉做精簡(jiǎn),只是調(diào)用了RawCall.execute()來(lái)模擬網(wǎng)絡(luò)請(qǐng)求

注意:

還有一個(gè)重要的東西CallbackExecutor,給精簡(jiǎn)去掉了入录。在Android內(nèi)蛤奥,由于UI線程的原因,網(wǎng)絡(luò)請(qǐng)求時(shí)OkHttp會(huì)通過(guò)線程池運(yùn)行在單獨(dú)線程中僚稿,但本Demo為了簡(jiǎn)化流程凡桥,就網(wǎng)絡(luò)請(qǐng)求模擬時(shí),沒(méi)有新創(chuàng)建線程蚀同, 對(duì)整個(gè)流程學(xué)習(xí)影響不大

實(shí)際在Retrofit中缅刽,Android環(huán)境默認(rèn)情況下,會(huì)用一個(gè)Handler進(jìn)行線程間通信將Response回調(diào)到UI線程


2.1 build()構(gòu)建

Retrofit構(gòu)造方法并不是private的蠢络,但一般都是通過(guò)內(nèi)部的Builder來(lái)構(gòu)建

 @Nullable
 private Executor callbackExecutor;

 Builder(Platform platform) {
     this.platform = platform;
 }

 public Builder() {
     this(Platform.get());
 }

 public Retrofit build() {

     Executor callbackExecutor = this.callbackExecutor;
     if (callbackExecutor == null) {
         callbackExecutor = platform.defaultCallbackExecutor();
     }

     // Make a defensive copy of the adapters and add the default Call adapter.
     List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
     callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

     // Make a defensive copy of the converters.
     List<Converter.Factory> converterFactories = new ArrayList<>(
                    1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

     // Add the built-in converter factory first. This prevents overriding its behavior but also
     // ensures correct behavior when using converters that consume all types.
     converterFactories.add(new BuiltInConverters());
     converterFactories.addAll(this.converterFactories);
     converterFactories.addAll(platform.defaultConverterFactories());
     return new Retrofit(converterFactories, callAdapterFactories);
 }

Executor可以先不關(guān)注

CallAdapter作用就是將Call轉(zhuǎn)換為Service,默認(rèn)情況下使用DefaultCallAdapterFactory

Converter轉(zhuǎn)換器衰猛,默認(rèn)情況下,使用BuiltInConverters來(lái)構(gòu)建內(nèi)置的轉(zhuǎn)換器

在調(diào)用build()后刹孔,Retrofit就有了關(guān)聯(lián)的CallAdapterFactory啡省、ConveterFactory,但此時(shí)具體要使用的CallAdpater髓霞、Converter還不知道


2.2 動(dòng)態(tài)代理

Retrofit作為門面卦睹,關(guān)鍵點(diǎn)之一是crete()方法,使用動(dòng)態(tài)代理通過(guò)CallAdatperRetrofit.Call轉(zhuǎn)換為一個(gè)Service

/**
 * 利用 JDK 動(dòng)態(tài)代理方库,create 出一個(gè) T(Service)
 * <p>
 * 需要將 Call 通過(guò) CallAdapter 轉(zhuǎn)換成 T
 * <p>
 * Single-interface proxy creation guarded by parameter safety
 *
 * @param service 代理接口 Class
 * @param <T>     代理
 * @return T, 代理 Service
 */
@SuppressWarnings("unchecked")
public <T> T create(final Class<T> service) {
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service},
            new InvocationHandler() {
                private final Object[] emptyArgs = new Object[0];
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
                }
            });
}

loadServiceMethod()從已創(chuàng)建的mServiceCache中獲取當(dāng)前方法對(duì)應(yīng)的ServiceMethod结序,沒(méi)有就創(chuàng)建一個(gè)新的put進(jìn)去

private ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = mServiceCache.get(method);
    if (result != null) {
        return result;
    }
    synchronized (mServiceCache) {
        result = mServiceCache.get(method);
        if (result == null) {
            result = ServiceMethod.parseAnnotations(this, method);
            mServiceCache.put(method, result);
        }
    }
    return result;
}

2.2.1 ServiceMethod

ServiceMethod是一個(gè)抽象類,有一個(gè)HttpSeeviceMethod子類薪捍,負(fù)責(zé)將好的Request,CallAdapter,Converter組合起來(lái)

abstract class ServiceMethod<T> {
    static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
        RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

        Type returnType = method.getGenericReturnType();
        if (Utils.hasUnresolvableType(returnType)) {
            throw methodError(method,
                    "Method return type must not include a type variable or wildcard: %s", returnType);
        }
        if (returnType == void.class) {
            throw methodError(method, "Service methods cannot return void.");
        }

        return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
    }

    abstract T invoke(Object[] args);
}

parseAnnotations()內(nèi)笼痹,首先會(huì)通過(guò)RequestFactory.parseAnnotations()進(jìn)行解析注解配喳,然后構(gòu)建出一個(gè)RequestFactory

再判斷returnType返回類型酪穿,若為void,就會(huì)拋出異常晴裹,也就是創(chuàng)建的Service接口返回值不能為void

返回值為子類HttpServiceMethodparseAnnotations()方法返回值


HttpServiceMethod

內(nèi)部主要有兩個(gè)方法:

 /** 構(gòu)建具體的 CallAdapter被济,Response Converter */
 static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
         Retrofit retrofit, Method method, RequestFactory requestFactory) {
     // 創(chuàng)建 CallAdapter
     CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
     Type responseType = callAdapter.responseType();
     // Response Converter
     Converter<ResponseBody, ResponseT> responseConverter =
             createResponseConverter(retrofit, method, responseType);
     return new HttpServiceMethod<>(requestFactory, callAdapter, responseConverter);
 }
 
 /** ServiceMehod 抽象方法,將OkHttpCall進(jìn)行適配*/
 @Override
 ReturnT invoke(Object[] args) {
     return mCallAdapter.adapt(new OkHttpCall<>(mRequestFactory, args, mResponseConverter));
 }

到了此時(shí)涧团,具體要使用的CallApdater只磷、Converter都已經(jīng)創(chuàng)建好,就等著調(diào)用Service內(nèi)的方法進(jìn)行網(wǎng)絡(luò)請(qǐng)求

mCallAdapter.adapt()泌绣,就是適配器模式钮追,將Service適配成Call


2.3 Converter 轉(zhuǎn)換器

Converter<F,T>是一個(gè)接口,將一個(gè)F -> T

兩個(gè)地方要用阿迈,Request Body以及Response Body

Request Body需要解析注解元媚,拿到注解的信息拼裝用于網(wǎng)絡(luò)請(qǐng)求的Request

Response Body就是將Json轉(zhuǎn)換成為Bean,但Demo里這里簡(jiǎn)化了,只是直接將字符串進(jìn)行返回


2.3.1 解析 Request Body 注解

ServiceMethod.parseAnnotations()內(nèi)刊棕,RequestFactory.parseAnnotations()時(shí)會(huì)進(jìn)行解析注解炭晒,組裝Request

static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
    return new Builder(retrofit, method).build();
}

進(jìn)行 build 構(gòu)建RequestFactory

RequestFactory build() {
    // 創(chuàng)建的 Service 接口方法中的參數(shù)注解
    int parameterCount = parameterAnnotationsArray.length;
    parameterHandlers = new ParameterHandler<?>[parameterCount];
    for (int p = 0; p < parameterCount; p++) {
        parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p]);
    }
    return new RequestFactory(this);
}

parseParameter()就是根據(jù)Service接口內(nèi)方法參數(shù),使用parseParameterAnnotation()進(jìn)行遍歷對(duì)每個(gè)注解進(jìn)行解析

/** 解析具體的參數(shù)注解 */
@Nullable
private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
    if (annotation instanceof Content) {
        validateResolvableType(p, type);

        Converter<?, String> converter =
                mRetrofit.stringConverter(type, annotations);
        return new ParameterHandler.Content<>(converter);
    }
    return null;
}

@Content是一個(gè)自己定義的注解

/** 內(nèi)容注解 */
@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Content {
}

ParameterHandler一個(gè)抽象類甥角,根據(jù)注解网严,實(shí)現(xiàn)了一個(gè)Content子類,負(fù)責(zé)對(duì)注解進(jìn)行解析拼接嗤无。注解是RUNTIME型的震束,為了加快讀取,使用ParameterHandler

static final class Content<T> extends ParameterHandler<T> {

    private final Converter<T, String> mValueConverter;

    Content(Converter<T, String> valueConverter) {
        this.mValueConverter = valueConverter;
    }

    @Override
    void apply(StringBuilder builder, T value) throws IOException {
        builder.append(mValueConverter.convert(value));
    }
}

apply()將注解標(biāo)記的參數(shù)拼接到StirngBuilder当犯,在OkHttpCall內(nèi)發(fā)起網(wǎng)路請(qǐng)求時(shí)驴一,會(huì)通過(guò)這個(gè)StirngBuilder拿到Request Body


2.3.2 Response Body 轉(zhuǎn)換

Retrofit.Builderbuild()方法內(nèi),添加了一個(gè)BuiltInConverters內(nèi)置的轉(zhuǎn)換器

converterFactories.add(new BuiltInConverters());

BuiltInConverters 內(nèi)置轉(zhuǎn)換器

/** 內(nèi)置的轉(zhuǎn)換器 */
final class BuiltInConverters extends Converter.Factory {
    @Nullable
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(
            Type type, Annotation[] annotations, Retrofit retrofit) {
        // 由于簡(jiǎn)化過(guò)程沒(méi)有進(jìn)行 http 請(qǐng)求灶壶,最終都轉(zhuǎn)換成了 String
        if (type == String.class) {
            return ResponseBodyToStringConverter.INSTANCE;
        }

        // 返回 Void
        if (type == Void.class) {
            return VoidResponseBodyConverter.INSTANCE;
        }
        return null;
    }

    /** Response Body 轉(zhuǎn)換成 String */
    static final class ResponseBodyToStringConverter implements Converter<ResponseBody, String> {
        static final ResponseBodyToStringConverter INSTANCE = new ResponseBodyToStringConverter();

        @Override
        public String convert(ResponseBody value) throws IOException {
            return value.body();
        }
    }
}

ResponseBodyToStringConverter內(nèi)直接返回了ResponseBody.body()肝断,也就是Stirng本身


2.4 OkHttpCall

當(dāng)call.execute()時(shí),最終會(huì)調(diào)用OkHttpCall內(nèi)部邏輯驰凛,發(fā)起網(wǎng)絡(luò)請(qǐng)求

@Override
public Response<T> execute() throws IOException {
    RawCall call;
    synchronized (this) {
        if (mExecuted) {
            throw new IllegalStateException("Already executed.");
        }
        mExecuted = true;
        call = mRawCall;
        if (call == null) {
            try {
                call = mRawCall = createRawCall();
            } catch (IOException | RuntimeException | Error e) {
                throw e;
            }
        }
    }
    return parseResponse(call.execute());
}

在方法內(nèi)胸懈,會(huì)先創(chuàng)建RawCall

private RawCall createRawCall() throws IOException {
        return new RawCall(mRequestFactory.create(mArgs));
}

mRequestFactory.create()內(nèi),會(huì)將ParameterHandler存的解析的注解拼裝的Request Body轉(zhuǎn)成String

parseResponse()解析結(jié)果

private Response<T> parseResponse(String s) throws IOException {
    T body = mResponseConverter.convert(new ResponseBody(s));
    return Response.success(body);
}

根據(jù)創(chuàng)建的Response Body Converter來(lái)解析恰响,之后構(gòu)建一個(gè)Response類包裝下趣钱,返回


3. 最后

整個(gè)精簡(jiǎn)后的Demo流程就這些,可以看看具體的代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末胚宦,一起剝皮案震驚了整個(gè)濱河市首有,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌枢劝,老刑警劉巖井联,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異您旁,居然都是意外死亡烙常,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門鹤盒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蚕脏,“玉大人,你說(shuō)我怎么就攤上這事侦锯⊥毡蓿” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵尺碰,是天一觀的道長(zhǎng)挣棕。 經(jīng)常有香客問(wèn)我汇竭,道長(zhǎng),這世上最難降的妖魔是什么穴张? 我笑而不...
    開(kāi)封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任细燎,我火速辦了婚禮,結(jié)果婚禮上皂甘,老公的妹妹穿的比我還像新娘玻驻。我一直安慰自己,他們只是感情好偿枕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布璧瞬。 她就那樣靜靜地躺著,像睡著了一般渐夸。 火紅的嫁衣襯著肌膚如雪嗤锉。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天墓塌,我揣著相機(jī)與錄音瘟忱,去河邊找鬼。 笑死苫幢,一個(gè)胖子當(dāng)著我的面吹牛访诱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播韩肝,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼触菜,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了哀峻?” 一聲冷哼從身側(cè)響起涡相,我...
    開(kāi)封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎剩蟀,沒(méi)想到半個(gè)月后催蝗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喻旷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年生逸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了牢屋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片且预。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖烙无,靈堂內(nèi)的尸體忽然破棺而出锋谐,到底是詐尸還是另有隱情,我是刑警寧澤截酷,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布涮拗,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏三热。R本人自食惡果不足惜鼓择,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望就漾。 院中可真熱鬧呐能,春花似錦、人聲如沸抑堡。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)首妖。三九已至偎漫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間有缆,已是汗流浹背象踊。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留棚壁,地道東北人通危。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像灌曙,于是被迫代替她去往敵國(guó)和親菊碟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 目錄介紹 1.首先回顧Retrofit簡(jiǎn)單使用方法 2.Retrofit的創(chuàng)建流程源碼分析2.1 Retrofit...
    楊充211閱讀 1,058評(píng)論 0 16
  • Retrofit這個(gè)開(kāi)源庫(kù)出來(lái)也有一定年頭了在刺,記得之前還是在V1.0的版本的時(shí)候逆害,之前在三月份也寫過(guò)一個(gè)Retro...
    lovejjfg閱讀 1,448評(píng)論 0 5
  • Retrofit 源碼解析 簡(jiǎn)單用法 Retrofit最簡(jiǎn)單的用法就是定義一個(gè)接口,創(chuàng)建Retrofit對(duì)象蚣驼,調(diào)用...
    Kingty閱讀 793評(píng)論 3 14
  • # 結(jié)構(gòu) HTTP請(qǐng)求包括三部分:請(qǐng)求行(Request Line)魄幕,頭部(Headers)和數(shù)據(jù)體(Body)。...
    okmnhyxx閱讀 238評(píng)論 0 0
  • 現(xiàn)如今 一切安穩(wěn) 所有遺憾都按部就班地填滿 時(shí)間真是奇妙 分秒必爭(zhēng)地帶走一切 又從容不迫地等一切發(fā)生 漂泊的游子在...
    丹三醬閱讀 238評(píng)論 0 0