Retrofit源碼分析(一)

金三銀四速速臨近,準(zhǔn)備分析一下用了一段時(shí)日的Retrofit,本文分析Retrofit2.1.0版本,2.2.0最近新發(fā)布,據(jù)說支持RxJava2
(°?°)?
Ps:對所用的框架分析也是面試重要一環(huán)哦,知其然而知其所然是考察程序員潛力的重要一環(huán)(??????)??

Retrofit源碼分析(二)

介紹

Retrofit:大名鼎鼎的Square公司旗下的開源框架,是OkHttp的封裝版,完成了高度的解耦,非常的好用,尤其配合是RESTful風(fēng)格的服務(wù)端。

基本用法

//定義一個(gè)全局單例(推薦)
Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("yourAPI/")
                .client(new OkHttpClient())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
//定義一個(gè)接口,里面存放方法
public interface API{
//注意這個(gè)Call不是OkHttp3的Call
@GET("users/search/{keyword}")
Call<List<UserBean>> searchUserList(@Path("keyword") String keyword);
}
API api= retrofit.create(API.class);
api.searchUserList("張三").省略

入口

在我們構(gòu)建了一個(gè)Retrofit實(shí)例后,調(diào)用了create方法,調(diào)用API接口中定義的方法后直接可以發(fā)起網(wǎng)絡(luò)請求,這是怎么實(shí)現(xiàn)的呢?那么,請聽下回分解。讓我們進(jìn)去瞧瞧。

public <T> T create(final Class<T> service) {
        //判斷了傳入的service必須為interface,且不能實(shí)現(xiàn)其他接口
        Utils.validateServiceInterface(service);
        //有需要的話做方法緩存?
        if (validateEagerly) {
            eagerlyValidateMethods(service);
        }
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service},
                new InvocationHandler() {
                    private final Platform platform = Platform.get();

                    @Override
                    public Object invoke(Object proxy, Method method, Object... args)
                            throws Throwable {
                        //如果調(diào)用的方法是Object的
                        if (method.getDeclaringClass() == Object.class) {
                            return method.invoke(this, args);
                        }
                        //默認(rèn)進(jìn)不來這個(gè)if
                        if (platform.isDefaultMethod(method)) {
                            return platform.invokeDefaultMethod(method, service, proxy, args);
                        }
                        //重點(diǎn)*具體執(zhí)行細(xì)節(jié)
                        ServiceMethod serviceMethod = loadServiceMethod(method);
                        OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
                        return serviceMethod.callAdapter.adapt(okHttpCall);
                    }
                });
    }

可以看到這里運(yùn)用了Java的動態(tài)代理技術(shù),關(guān)于動態(tài)代理的詳細(xì)介紹,這篇寫的很詳細(xì)。
我們先來看執(zhí)行細(xì)節(jié)的第一句

第一句

ServiceMethod serviceMethod = loadServiceMethod(method);

看起來像是把我們在API中定義的方法searchUserList封裝成了一個(gè)對象

ServiceMethod loadServiceMethod(Method method) {
        ServiceMethod result;
        synchronized (serviceMethodCache) {
            result = serviceMethodCache.get(method);
            if (result == null) {
                result = new ServiceMethod.Builder(this, method).build();
                serviceMethodCache.put(method, result);
            }
        }
        return result;
    }

果然,這里還運(yùn)用了建造者模式和緩存來設(shè)計(jì),API中的同一個(gè)方法只會構(gòu)建一次,值得一提的是ServiceMethod的原注釋

Adapts an invocation of an interface method into an HTTP call

意思是一個(gè)接口方法對應(yīng)一個(gè)Http請求,及接下來讓我們來看看ServiceMethod的構(gòu)造器,都帶上了注釋,看官暫時(shí)不明白也沒關(guān)系,后面會一一講解清楚。反正這博文也沒人看(;?_?)

ServiceMethod(Builder<T> builder) {
        //負(fù)責(zé)創(chuàng)建請求
        this.callFactory = builder.retrofit.callFactory();
        //負(fù)責(zé)將請求適配成API中的Call或者RxJava的Observable
        this.callAdapter = builder.callAdapter;
        //請求的baseUrl
        this.baseUrl = builder.retrofit.baseUrl();
        //響應(yīng)體的解析器,上文設(shè)置了Gson來解析成
        this.responseConverter = builder.responseConverter;
        //請求的方式,POST,GET。。。
        this.httpMethod = builder.httpMethod;
        //請求的相對路徑,API中定義的路徑
        this.relativeUrl = builder.relativeUrl;
        //請求報(bào)文頭
        this.headers = builder.headers;
        //請求報(bào)文類型
        this.contentType = builder.contentType;
        //是否帶有請求體
        this.hasBody = builder.hasBody;
        //是否表單請求
        this.isFormEncoded = builder.isFormEncoded;
        //是否是Multipart請求,一般用于傳輸文件
        this.isMultipart = builder.isMultipart;
        //數(shù)組,記錄請求中方法的參數(shù),@Path,@Query等等
        this.parameterHandlers = builder.parameterHandlers;
  }

我們前文在API中定義的一個(gè)方法searchUserList的生辰八字(包括方法的注解信息)就被封裝到了ServiceMethod中了。

第二句

OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);

如果說第一句是乘客準(zhǔn)備好了身份證,那么第二句代碼就是購票上車的具體操作。
我們可以看到OkHttpCall的內(nèi)部是實(shí)現(xiàn)了retrofit2包下的Call<T>,主要是一個(gè)同步方法execute,一個(gè)異步方法enqueue

    @Override
    public void enqueue(final Callback<T> callback) {
        if (callback == null) throw new NullPointerException("callback == null");

        okhttp3.Call call;
        Throwable failure;

        synchronized (this) {
            if (executed) throw new IllegalStateException("Already executed.");
            executed = true;

            call = rawCall;
            failure = creationFailure;
            if (call == null && failure == null) {
                try {
                    //創(chuàng)建一個(gè)Call
                    call = rawCall = createRawCall();
                } catch (Throwable t) {
                    failure = creationFailure = t;
                }
            }
        }

        if (failure != null) {
            callback.onFailure(this, failure);
            return;
        }

        if (canceled) {
            call.cancel();
        }

        call.enqueue(new okhttp3.Callback() {
            @Override
            public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
                    throws IOException {
                Response<T> response;
                try {
                    //將Response轉(zhuǎn)換
                    response = parseResponse(rawResponse);
                } catch (Throwable e) {
                    callFailure(e);
                    return;
                }
                callSuccess(response);
            }

            @Override
            public void onFailure(okhttp3.Call call, IOException e) {
                try {
                    callback.onFailure(OkHttpCall.this, e);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
            }

            private void callFailure(Throwable e) {
                try {
                    callback.onFailure(OkHttpCall.this, e);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
            }

            private void callSuccess(Response<T> response) {
                try {
                    callback.onResponse(OkHttpCall.this, response);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
            }
        });
    }
    @Override 
    public Response<T> execute() throws IOException {
        okhttp3.Call call;

        synchronized (this) {
            if (executed) throw new IllegalStateException("Already executed.");
            executed = true;

            if (creationFailure != null) {
                if (creationFailure instanceof IOException) {
                    throw (IOException) creationFailure;
                } else {
                    throw (RuntimeException) creationFailure;
                }
            }

            call = rawCall;
            if (call == null) {
                try {
                    //創(chuàng)建一個(gè)Call
                    call = rawCall = createRawCall();
                } catch (IOException | RuntimeException e) {
                    creationFailure = e;
                    throw e;
                }
            }
        }

        if (canceled) {
            call.cancel();
        }
        //將Response轉(zhuǎn)換
        return parseResponse(call.execute());
    }

細(xì)心地讀者可能都發(fā)現(xiàn)了,在這2個(gè)方法中都調(diào)用了2個(gè)方法
一個(gè)是

private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

這個(gè)方法完成了如下幾步操作
1.將serviceMethod(乘客身份信息)+args(乘客選擇的列車座位)生成了一個(gè)okhttp3.Request(列車票)
2.serviceMethod.callFactory.newCall(request)上車
3.將call返回,通知上級乘客已經(jīng)就位
toRequest(args)無非就是將參數(shù)構(gòu)建成一個(gè)okhttp3請求,略過~~這里注意,將request轉(zhuǎn)換成okhttp3.Call是通過callFactory來完成的,callFactoryokhttp3.Call下的Factory接口,默認(rèn)也就是自家的OkHttpClient,也可以通過new Retrofit.Builder().client(yourClient)來改變。
還有一個(gè)是

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    // Remove the body's source (the only stateful object) so we can pass the response along.
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();

    int code = rawResponse.code();
    if (code < 200 || code >= 300) {
    //Http錯(cuò)誤碼的情況
      try {
        // Buffer the entire body to avoid future I/O.
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
      } finally {
        rawBody.close();
      }
    }

    if (code == 204 || code == 205) {
    //204-205時(shí)服務(wù)端不返回實(shí)體內(nèi)容
      return Response.success(null, rawResponse);
    }

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
      //將從okhttp3來的response通過轉(zhuǎn)換再解析成數(shù)據(jù)實(shí)體T
      T body = serviceMethod.toResponse(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      // If the underlying source threw an exception, propagate that rather than indicating it was
      // a runtime exception.
      catchingBody.throwIfCaught();
      throw e;
    }
  }

在這里我們又見到了熟悉的serviceMethod.toResponse


這個(gè)方法內(nèi)部也是調(diào)用接口來實(shí)現(xiàn)的

T toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

接口的初始化這里

new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.build();

也就是說看官不喜歡Gson也可以選用Jackson以及自定義,在項(xiàng)目中一般都要自定義,這里先挖個(gè)坑,(`?ω?′)
那么我們花了不少的篇幅來講解乘客購票上車這個(gè)動作,畢竟上車了不是還會有驗(yàn)票嗎 手動滑稽

第三句

return serviceMethod.callAdapter.adapt(okHttpCall);

接下來我們來看動態(tài)代理的最后一句,也就是司機(jī)開車,哦不,發(fā)起請求
這里我們又見到了老熟人serviceMethod,這里也是通過callAdapter這個(gè)接口來發(fā)起請求的。

//我們也可以添加對RxJava的支持,安利一波
Retrofit retrofit = new Retrofit.Builder()
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();

由于在Retrofit中支持的adapterFactories是一個(gè)List類型,并且默認(rèn)會在List中添加一個(gè)默認(rèn)的ExecutorCallAdapterFactory,這里我們先分析它的源碼

final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
  final Executor callbackExecutor;

  ExecutorCallAdapterFactory(Executor callbackExecutor) {
    //傳入一個(gè)回調(diào) 調(diào)度器
    this.callbackExecutor = callbackExecutor;
  }

  @Override
  public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public <R> Call<R> adapt(Call<R> call) {
        //返回調(diào)度器的回調(diào)結(jié)果
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

  static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }

    @Override public void enqueue(final Callback<T> callback) {
      if (callback == null) throw new NullPointerException("callback == null");

      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              if (delegate.isCanceled()) {
                // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
              } else {
                callback.onResponse(ExecutorCallbackCall.this, response);
              }
            }
          });
        }

        @Override public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
    }

    @Override public boolean isExecuted() {
      return delegate.isExecuted();
    }

    @Override public Response<T> execute() throws IOException {
      return delegate.execute();
    }

    @Override public void cancel() {
      delegate.cancel();
    }

    @Override public boolean isCanceled() {
      return delegate.isCanceled();
    }

    @SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
    @Override public Call<T> clone() {
      return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
    }

    @Override public Request request() {
      return delegate.request();
    }
  }
}

短短100行代碼看官可能看的有點(diǎn)找不到北


Retrofit.Builder中我們執(zhí)行最后的build()時(shí),Retrofit已經(jīng)為我們準(zhǔn)備了默認(rèn)的調(diào)度器,也就是Handler,也就是說callbackExecutor.execute(Runnable)這個(gè)方法就相當(dāng)于Handler post一個(gè)任務(wù)到主線程,那么剩下的也就是紙老虎了,細(xì)心的同學(xué)可能注意到了,ExecutorCallbackCall其實(shí)就是一個(gè)裝飾者模式。那么我們就簡單的分析完了Retrofit的一部分源碼。

總結(jié)


筆者總結(jié)了一張精簡的本文閹割版,如有錯(cuò)誤,敬請指正。
對RxJavaAdapter的分析和其他的補(bǔ)充就放到下一篇了(??ω??)
Retrofit源碼分析(二)

“你們是世上的鹽。鹽若失了味,怎能叫它再咸呢?以后無用,不過丟在外面,被人踐踏了。你們是世上的光。城造在山上是不能隱藏的。人點(diǎn)燈,不放在斗底下,是放在燈臺上,就照亮一家的人。你們的光也當(dāng)這樣照在人前,叫他們看見你們的好行為,便將榮耀歸給你們在天上的父。” (馬太福音 5:13-16 和合本)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,157評論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,157評論 2 375

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