關(guān)于Okhttp3(三)--請(qǐng)求流程

前兩天我們介紹了喇聊,基本使用和如何查看源碼牛柒,今天我們正式進(jìn)入源碼分析流程。

流程圖

首先我們先看一看它的請(qǐng)求流程棒旗,在Okhttp3中請(qǐng)求是基于攔截器原理喘批,源代碼如下:

源碼路徑:okhttp3/RealCall.java

// 開始執(zhí)行整個(gè)請(qǐng)求
Response getResponseWithInterceptorChain() throws IOException {
  // Build a full stack of interceptors.
  // 攔截器棧
  List<Interceptor> interceptors = new ArrayList<>();
  // 前文說過的 普通攔截器
  interceptors.addAll(client.interceptors());
  // 重試攔截器,網(wǎng)絡(luò)錯(cuò)誤铣揉、請(qǐng)求失敗等
  interceptors.add(retryAndFollowUpInterceptor);
  // 橋接攔截器饶深,主要是重構(gòu)請(qǐng)求頭即header
  interceptors.add(new BridgeInterceptor(client.cookieJar()));
  // 緩存攔截器
  interceptors.add(newCacheInterceptor(client.internalCache()));
  // 連接攔截器,連接服務(wù)器逛拱,https包裝
  interceptors.add(new ConnectInterceptor(client));
  // 網(wǎng)絡(luò)攔截器敌厘,websockt不支持,同樣是自定義
  if (!forWebSocket) {
    interceptors.addAll(client.networkInterceptors());
  }
  // 服務(wù)攔截器朽合,主要是發(fā)送(write俱两、input)、讀炔懿健(read宪彩、output)數(shù)據(jù)
  interceptors.add(new CallServerInterceptor(forWebSocket));

  // 開啟調(diào)用鏈
  Interceptor.Chain chain = new RealInterceptorChain(
      interceptors, null, null, null, 0, originalRequest);
  return chain.proceed(originalRequest);
}

研究Okhttp3的源碼,從此處開始讲婚,一個(gè)一個(gè)攔截器尿孔,讀懂即可。不得不說這種方式真的很棒筹麸,清晰明了活合。

整體框架圖(圖片來源網(wǎng)絡(luò),感謝作者):

通過上圖物赶,想必對(duì)Okhttp3的實(shí)現(xiàn)方式白指,已經(jīng)有了基本的認(rèn)識(shí)下面我們就一步一具體分析。

構(gòu)造HttpClient

HttpClient采用了建造者設(shè)計(jì)模式來實(shí)例化酵紫。本身有多個(gè)字段用于全局對(duì)象侵续,比去Cache倔丈、Dns等

  1. 靜態(tài)代碼塊構(gòu)造全局緩存

    static {
      Internal.instance = new Internal() {
        // 略
        @Override public void setCache(OkHttpClient.Builder builder, InternalCache internalCache) {
          // 寫入緩存,指的是響應(yīng)數(shù)據(jù)緩存
          builder.setInternalCache(internalCache);
        }
    
        // 從緩存中獲取有效的連接状蜗,僅支持Http/2需五, 本質(zhì)就是從內(nèi)存的ConnectiongPol中的Deque讀取
        @Override public RealConnection get(
            ConnectionPool pool, Address address, StreamAllocation streamAllocation) {
          return pool.get(address, streamAllocation);
        }
    // 將連接緩存到連接池中
        @Override public void put(ConnectionPool pool, RealConnection connection) {
          pool.put(connection);
        }
    
        // 線路的緩存,多host多ip的青款
        @Override public RouteDatabase routeDatabase(ConnectionPool connectionPool) {
          return connectionPool.routeDatabase;
        }
    
        // 略
    }
      
      // 此處有很多種數(shù)據(jù)緩存處理轧坎,不了解并不影響代碼分析宏邮,如有興趣可自行研究
    
  2. Dispatcher, 分發(fā)請(qǐng)求缸血,內(nèi)部是有一個(gè)ThreadPoolExecutor

  3. Proxy蜜氨, 代理連接,分三種類型直接(DIRECT)捎泻、Http(http)飒炎、SOCKS。

  4. ProxySelector笆豁,線路選擇器郎汪,對(duì)應(yīng)Okhttp的一大特點(diǎn),自行線路選擇闯狱,找到合適的連接

  5. Cache煞赢, 真正的緩存實(shí)現(xiàn)

  6. SSLSocketFactory, Https的支持

  7. ConnectionPool哄孤, 連接池

  8. Dns照筑,dns解析(Java實(shí)現(xiàn))

  9. 其他,如超時(shí)時(shí)間等

發(fā)起請(qǐng)求

我們都知道一個(gè)請(qǐng)求有多部分組成瘦陈,同樣Okhttp3創(chuàng)建一個(gè)請(qǐng)求也要多部分凝危。

  1. 構(gòu)造請(qǐng)求頭
  2. 構(gòu)造請(qǐng)求體
  3. 發(fā)送一個(gè)請(qǐng)求

通過以上三步我們就可以完成一次請(qǐng)求。

請(qǐng)求頭

header并非每個(gè)請(qǐng)求都需要晨逝,要看與服務(wù)端是如何定義的媒抠,通常一個(gè)請(qǐng)求會(huì)默認(rèn)一些頭,比如Content-Type咏花,Accept-Encoding,Connection等對(duì)應(yīng)http協(xié)議

Header本質(zhì)上就是一個(gè)Map阀趴,只是在封裝了一層而已昏翰,但是Okhttp3的實(shí)現(xiàn)不是這樣,而是一個(gè)String數(shù)據(jù)刘急,key + value的形式棚菊,即一個(gè)頭占用數(shù)組的兩位:

每一個(gè)色塊對(duì)應(yīng)一個(gè)header,代碼如下:

 // 源碼路徑 okhttp3/Headers.java

public final class Headers {
  private final String[] namesAndValues;
  // 略
  Builder addLenient(String name, String value) {
    namesAndValues.add(name);
    namesAndValues.add(value.trim());
    return this;
  }
  // 略
 }
請(qǐng)求體

請(qǐng)求體有多種形式叔汁,對(duì)應(yīng)的父類是RequestBody统求,有文件形式检碗、Json等,MediaType決定了是何種形式码邻,通常我們用的是FromBody和MultipartBody

  1. FromBody

    public final class FormBody extends RequestBody {
      private static final MediaType CONTENT_TYPE =
          MediaType.parse("application/x-www-form-urlencoded");
    
      private final List<String> encodedNames; // 參數(shù)名稱
      private final List<String> encodedValues; // 參數(shù)值
     
     // 構(gòu)造者
       public static final class Builder {
        private final List<String> names = new ArrayList<>();
        private final List<String> values = new ArrayList<>();
    
        public Builder add(String name, String value) {
          names.add(HttpUrl.canonicalize(name, FORM_ENCODE_SET, false, false, true, true));
          values.add(HttpUrl.canonicalize(value, FORM_ENCODE_SET, false, false, true, true));
          return this;
        }
        
         //構(gòu)造一個(gè)實(shí)例
        public FormBody build() {
          return new FormBody(names, values);
        }
      }
     }
    
  2. MultipartBody

    MultipartBody原理基本一致折剃,區(qū)別在于他可以發(fā)送表單的同時(shí)也可以發(fā)送文件數(shù)據(jù),再次不在贅述像屋。

構(gòu)造一個(gè)Request

有了上面兩個(gè)步驟怕犁,接下了就自然而讓產(chǎn)生一個(gè)Request,顧名思義它就是對(duì)請(qǐng)求的封裝己莺,包括請(qǐng)求方式奏甫,請(qǐng)求頭,請(qǐng)求體凌受,請(qǐng)求路徑等,源代碼碼也是比較簡(jiǎn)單阵子,一看即明白。

public final class Request {
  final HttpUrl url;
  final String method;
  final Headers headers;
  final RequestBody body;
  final Object tag;
 }
Call

有了準(zhǔn)備工作胜蛉,現(xiàn)在我們就要發(fā)射了挠进,Call是一個(gè)概念的封裝,就像一列火車腾么,蓄勢(shì)待發(fā)奈梳,在鐵軌停靠準(zhǔn)備出發(fā)一樣

Call是一個(gè)接口解虱,實(shí)現(xiàn)類只有一個(gè)RealCall攘须,上面我們提到的流程就是在RealCall中。

public interface Call extends Cloneable {
  Request request(); // 獲取請(qǐng)求封裝的數(shù)據(jù)
  Response execute() throws IOException; // 同步執(zhí)行
  void enqueue(Callback responseCallback); // 異步執(zhí)行
  void cancel(); // 取消請(qǐng)求
  boolean isExecuted();
  boolean isCanceled();
  Call clone();
  interface Factory {
    Call newCall(Request request);
  }
}

// 重點(diǎn)看看中文注釋處即可
final class RealCall implements Call {
  
  final OkHttpClient client;
  // 重試攔截器
  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;

  /** The application's original request unadulterated by redirects or auth headers. */
  final Request originalRequest;
  final boolean forWebSocket;

  // Guarded by this.
  private boolean executed;

  RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

  @Override public Request request() {
    return originalRequest;
  }

  // 同步執(zhí)行的實(shí)現(xiàn)
  @Override public Response execute() throws IOException {
    // 一個(gè)call只能執(zhí)行一次
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    try {
      // 將請(qǐng)求放在殴泰,請(qǐng)求池中執(zhí)行于宙,此處會(huì)立即執(zhí)行
      client.dispatcher().executed(this);
      // 獲取結(jié)果,即執(zhí)行多個(gè)鏈接器的調(diào)用鏈
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }

  private void captureCallStackTrace() {
    Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
    retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
  }

  //  異步執(zhí)行悍汛,不管行返回結(jié)果
  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    // AsyncCall是一個(gè)Runnable的實(shí)現(xiàn)類捞魁,同時(shí)一個(gè)是RealCall的內(nèi)部類
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

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

  @Override public synchronized boolean isExecuted() {
    return executed;
  }

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

  @SuppressWarnings("CloneDoesntCallSuperClone") // We are a final type & this saves clearing state.
  @Override public RealCall clone() {
    return new RealCall(client, originalRequest, forWebSocket);
  }

  StreamAllocation streamAllocation() {
    return retryAndFollowUpInterceptor.streamAllocation();
  }

  // 異步執(zhí)行的線程封裝,Android基本就是這里了
  final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }

    String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }

    RealCall get() {
      return RealCall.this;
    }

    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        // 執(zhí)行調(diào)用鏈离咐,是不是很重要谱俭,哈哈
        Response response = getResponseWithInterceptorChain();
        // 處理回掉
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

  /**
   * Returns a string that describes this call. Doesn't include a full URL as that might contain
   * sensitive information.
   */
  String toLoggableString() {
    return (isCanceled() ? "canceled " : "")
        + (forWebSocket ? "web socket" : "call")
        + " to " + redactedUrl();
  }

  String redactedUrl() {
    return originalRequest.url().redact();
  }
    // 最重要的入口了
    // 最重要的入口了
    // 最重要的入口了
  // 重要事情說三遍
  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    // 脊髓代碼開始處
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }
}

看來上面的代碼,是不是被人家的想法折服宵蛀。更精妙的還在后面呢昆著,不要急。到此术陶,一個(gè)請(qǐng)求已經(jīng)完成凑懂,發(fā)送和接收,(不關(guān)心內(nèi)部實(shí)現(xiàn)的話)梧宫。接下來我們?cè)賮砜纯唇咏鳎B接器是如何工作的摆碉。

攔截器工作原理

Interceptor是一個(gè)接口,主要是對(duì)請(qǐng)求和響應(yīng)的處理脓豪,而實(shí)現(xiàn)攔截器調(diào)用鏈的是其內(nèi)部接口Chain

public interface Interceptor {
// 管攔截
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    // 管分發(fā),前行
    Response proceed(Request request) throws IOException;

    Connection connection();
  }
}

Interceptor.Chain的實(shí)現(xiàn)類只有一個(gè)RealInterceptorChain巷帝,也是處理調(diào)用過程的實(shí)現(xiàn),其內(nèi)部有個(gè)List裝載了所有攔截器跑揉,大家想必也猜到了锅睛,對(duì),就是迭代历谍,當(dāng)然也不是簡(jiǎn)簡(jiǎn)單單的接待了事现拒。讓我們看看源碼實(shí)現(xiàn)。

// RealCall.getResponseWithInterceptorChain()中創(chuàng)建了一個(gè)實(shí)例
public final class RealInterceptorChain implements Interceptor.Chain {
  // RealCall.getResponseWithInterceptorChain()中已經(jīng)賦值
   private final Request request;
  private final List<Interceptor> interceptors;
  
  // 下面屬性會(huì)在執(zhí)行攔截器的過程中一步一步賦值
  private final StreamAllocation streamAllocation;
  private final HttpCodec httpCodec;
  private final Connection connection;
  
  private final int index;
  private int calls;

  public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, Connection connection, int index, Request request) {
    this.interceptors = interceptors;
    this.connection = connection;
    this.streamAllocation = streamAllocation;
    this.httpCodec = httpCodec;
    this.index = index;
    this.request = request;
  }

  // 實(shí)現(xiàn)了父類proceed方法
  @Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }

  // 處理調(diào)用
  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      Connection connection) throws IOException {
    // 1望侈、迭代攔截器集合
    if (index >= interceptors.size()) throw new AssertionError();
    calls++;
    // 略印蔬。。脱衙。
    // 2侥猬、創(chuàng)建行的實(shí)例, 并將計(jì)數(shù)器+1
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    // 3捐韩、 取出下一個(gè)攔截器
    Interceptor interceptor = interceptors.get(index);
    // 4退唠、執(zhí)行攔截器方法,攔截器中又會(huì)調(diào)用proceed()方法
    Response response = interceptor.intercept(next);
  // 略荤胁。瞧预。。
    return response;
  }
}

看了上面的代碼是不是還不明白仅政,到底怎么實(shí)現(xiàn)的垢油,實(shí)際上就是迭代+遞歸。

每一個(gè)RealInterceptorChain會(huì)對(duì)應(yīng)一個(gè)Interceptor圆丹,然后Interceptor在產(chǎn)生下一個(gè)RealInterceptorChain滩愁,知道List迭代完成。

總結(jié)

Okhttp3的調(diào)用流程基本原理就是這樣辫封,重要的是思想硝枉,整個(gè)流程一氣呵成,完全解耦倦微。

系列文章

1妻味、關(guān)于Okhttp(一)-基本使用

2、關(guān)于Okhttp(二)-如何下載查看源碼

3璃诀、關(guān)于Okhttp3(三)-請(qǐng)求流程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蔑匣,隨后出現(xiàn)的幾起案子劣欢,更是在濱河造成了極大的恐慌棕诵,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凿将,死亡現(xiàn)場(chǎng)離奇詭異校套,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)牧抵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門笛匙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人犀变,你說我怎么就攤上這事妹孙。” “怎么了获枝?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵蠢正,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我省店,道長(zhǎng)嚣崭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任懦傍,我火速辦了婚禮雹舀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘粗俱。我一直安慰自己说榆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布源梭。 她就那樣靜靜地躺著娱俺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪废麻。 梳的紋絲不亂的頭發(fā)上荠卷,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音烛愧,去河邊找鬼油宜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛怜姿,可吹牛的內(nèi)容都是我干的慎冤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼沧卢,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蚁堤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起但狭,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤披诗,失蹤者是張志新(化名)和其女友劉穎撬即,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呈队,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剥槐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宪摧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片粒竖。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖几于,靈堂內(nèi)的尸體忽然破棺而出蕊苗,到底是詐尸還是另有隱情,我是刑警寧澤孩革,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布岁歉,位于F島的核電站,受9級(jí)特大地震影響膝蜈,放射性物質(zhì)發(fā)生泄漏锅移。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一饱搏、第九天 我趴在偏房一處隱蔽的房頂上張望非剃。 院中可真熱鬧,春花似錦推沸、人聲如沸备绽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肺素。三九已至,卻和暖如春宇驾,著一層夾襖步出監(jiān)牢的瞬間倍靡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工课舍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留塌西,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓筝尾,卻偏偏與公主長(zhǎng)得像捡需,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子筹淫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理站辉,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,651評(píng)論 18 139
  • 參考資源 官網(wǎng) 國(guó)內(nèi)博客 GitHub官網(wǎng) 鑒于一些關(guān)于OKHttp3源碼的解析文檔過于碎片化饰剥,本文系統(tǒng)的狸相,由淺入...
    風(fēng)骨依存閱讀 12,503評(píng)論 11 82
  • 1 介紹 在我們所處的互聯(lián)網(wǎng)世界中,HTTP協(xié)議算得上是使用最廣泛的網(wǎng)絡(luò)協(xié)議捐川。OKHttp是一款高效的HTTP客戶...
    天才木木閱讀 5,726評(píng)論 7 53
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法逸尖,內(nèi)部類的語法古沥,繼承相關(guān)的語法,異常的語法娇跟,線程的語...
    子非魚_t_閱讀 31,623評(píng)論 18 399
  • 葉離這輩子經(jīng)過的人情冷暖岩齿,生離死別太多,從不相信也不敢相信任何人苞俘,于是盹沈,她在孤獨(dú)中越走越遠(yuǎn),當(dāng)她歷盡艱辛終于殺了林...
    冰蝶摘星閱讀 464評(píng)論 2 9