okhttp源碼分析

一俘闯、okhttp簡(jiǎn)介

OKHttp是一個(gè)處理網(wǎng)絡(luò)請(qǐng)求的開(kāi)源項(xiàng)目潭苞,Android 當(dāng)前最火熱網(wǎng)絡(luò)框架,由移動(dòng)支付Square公司貢獻(xiàn)真朗,用于替代HttpUrlConnection和Apache HttpClient(android API23 6.0里已移除HttpClient)〈苏睿現(xiàn)在在Android開(kāi)發(fā)中最火的應(yīng)用最多的聯(lián)網(wǎng)框架毫無(wú)疑問(wèn)是okhttp,目前已經(jīng)大量替代了Volley蜜猾、HttpUrlConnection秀菱。

二、okhttp用法
1)導(dǎo)包:

    compile 'com.squareup.okhttp3:okhttp:3.9.1'

2)通過(guò)Builder類實(shí)例化客戶端類OkhttpClient蹭睡。

 private val client: OkHttpClient by lazy {
        OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .build()
    }

實(shí)例化OkhttpClient時(shí)可以通過(guò)Builder類添加一系列客戶端參數(shù)衍菱。
3)通過(guò)Builder類實(shí)例化請(qǐng)求類Request,通過(guò)OkHttpClient的newCall方法執(zhí)行請(qǐng)求肩豁。

 fun get(url: String, callBack: ResponseCallBack) {
        var request = Request.Builder()
            .url(Api.baseUrl + url)
            .build()
        client.newCall(request).enqueue(
            object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    callBack?.onFail(e.toString())
                }

                override fun onResponse(call: Call, response: Response) {
                    callBack?.onSuccess(response.body()!!.string())
                }

            }
        )
    }

    fun post(url: String, requestParams: RequestParams, callBack: ResponseCallBack) {
        var requestBody = FormBody.Builder()
        requestParams.forEach {
            requestBody.add(it.key, it.value.toString())
        }
        var request = Request.Builder()
            .url(Api.baseUrl + url)
            .post(requestBody.build())
            .build()
        client.newCall(request).enqueue(
            object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    callBack?.onFail(e.toString())
                }

                override fun onResponse(call: Call, response: Response) {
                    callBack?.onSuccess(response.body()!!.string())
                }
            }
        )
    }

實(shí)例化Request時(shí)可以通過(guò)Builder類添加一系列請(qǐng)求參數(shù)(例如Header信息)脊串,這里也不進(jìn)行詳細(xì)描述,在分析源碼時(shí)進(jìn)行具體分析清钥。
4)同步琼锋、異步請(qǐng)求。execute()執(zhí)行同步請(qǐng)求祟昭,enqueue()執(zhí)行異步請(qǐng)求缕坎,同步請(qǐng)求方法返回Response,異步請(qǐng)求通過(guò)CallBack返回Response篡悟。

三谜叹、源碼分析
1)OkHttpClient客戶端類,主要看一下各個(gè)成員變量搬葬。

final Dispatcher dispatcher;//請(qǐng)求分發(fā)器荷腊,分發(fā)執(zhí)行Request
  final @Nullable Proxy proxy;//代理
  final List<Protocol> protocols;//協(xié)議,管理協(xié)議版本
  final List<ConnectionSpec> connectionSpecs;//傳輸層版本和連接協(xié)議
  final List<Interceptor> interceptors;//攔截器
  final List<Interceptor> networkInterceptors;//網(wǎng)絡(luò)攔截器
  final EventListener.Factory eventListenerFactory;//請(qǐng)求監(jiān)聽(tīng)器
  final ProxySelector proxySelector;//代理選擇
  final CookieJar cookieJar;//cookie
  final @Nullable Cache cache;//緩存
  final @Nullable InternalCache internalCache;//內(nèi)部緩存
  final SocketFactory socketFactory;//socket工廠
  final @Nullable SSLSocketFactory sslSocketFactory;//HTTPS ssl安全套接層工廠
  final @Nullable CertificateChainCleaner certificateChainCleaner;//驗(yàn)證證書(shū)
  final HostnameVerifier hostnameVerifier;//確認(rèn)主機(jī)名
  final CertificatePinner certificatePinner;//證書(shū)鏈
  final Authenticator proxyAuthenticator;//代理身份驗(yàn)證
  final Authenticator authenticator;//本地身份驗(yàn)證
  final ConnectionPool connectionPool;//連接池
  final Dns dns;//DNS域名解析
  final boolean followSslRedirects;//安全套接層重定向
  final boolean followRedirects;//本地重定向
  final boolean retryOnConnectionFailure;//連接失敗重試
  final int connectTimeout;//連接超時(shí)
  final int readTimeout;//讀取超時(shí)
  final int writeTimeout;//寫(xiě)入超時(shí)
  final int pingInterval;//ping命令時(shí)間間隔

可以看到所有成員變量都是final的急凰,這樣所有的變量賦值就全部交給Builder類女仰,Builder類是OkHttpClient的靜態(tài)內(nèi)部類。可以通過(guò)builder()來(lái)完成設(shè)置疾忍,在不進(jìn)行設(shè)置情況下會(huì)使用Builder的默認(rèn)設(shè)置乔外。

  public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool();
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }

2)Request請(qǐng)求信息類。成員變量如下:

  final HttpUrl url;//請(qǐng)求url信息
  final String method;//請(qǐng)求方式(post get)
  final Headers headers;//請(qǐng)求header
  final @Nullable RequestBody body;//請(qǐng)求Body
  final Object tag;//請(qǐng)求tag

3)執(zhí)行newCall()方法創(chuàng)建Call對(duì)象锭碳,Call接口唯一實(shí)現(xiàn)類是RealCall袁稽,是執(zhí)行請(qǐng)求的真正對(duì)象。

  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }

RealCall請(qǐng)求類的源碼如下:

final class RealCall implements Call {
  final OkHttpClient client;
  final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;//請(qǐng)求重定向攔截器
  private EventListener eventListener;
  final Request originalRequest;//request信息
  final boolean forWebSocket;
  // Guarded by this.
  private boolean executed;
  private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

  static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }
  //同步請(qǐng)求
  @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }
.....
//異步請(qǐng)求
  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    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 RealCall.newRealCall(client, originalRequest, forWebSocket);
  }

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

  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 {
        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 {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

//鏈?zhǔn)秸?qǐng)求方法
  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, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

先來(lái)分析同步請(qǐng)求擒抛,從源碼中可以看到同步請(qǐng)求中會(huì)先通過(guò)

client.dispatcher().executed(this);

完成對(duì)請(qǐng)求的監(jiān)聽(tīng)推汽,本質(zhì)上是將當(dāng)前請(qǐng)求放入一個(gè)隊(duì)列中,當(dāng)請(qǐng)求結(jié)束之后再移除隊(duì)列歧沪。

/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

對(duì)于異步請(qǐng)求來(lái)說(shuō)同樣會(huì)進(jìn)行請(qǐng)求的監(jiān)聽(tīng)歹撒,唯一的區(qū)別在于異步請(qǐng)求是用兩個(gè)隊(duì)列完成,一個(gè)是等待請(qǐng)求的隊(duì)列诊胞,一個(gè)是正在請(qǐng)求的隊(duì)列暖夭。

/** Ready async calls in the order they'll be run. */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

最終進(jìn)行網(wǎng)絡(luò)請(qǐng)求是通過(guò)鏈?zhǔn)椒椒╣etResponseWithInterceptorChain來(lái)完成的。這個(gè)方法也是網(wǎng)絡(luò)請(qǐng)求的精髓所在撵孤。這個(gè)方法里通過(guò)獲取設(shè)置的一系列攔截器來(lái)完成網(wǎng)絡(luò)請(qǐng)求工作迈着,每個(gè)攔截器有不同的分工。最后通過(guò)構(gòu)建的攔截器鏈調(diào)用proceed()方法完成網(wǎng)絡(luò)請(qǐng)求邪码。由于不同的攔截器分別負(fù)責(zé)網(wǎng)絡(luò)請(qǐng)求中的不同工作裕菠。分析起來(lái)內(nèi)容比較多,這里先不進(jìn)行具體各個(gè)攔截器的源碼分析闭专。

4)Dispatcher請(qǐng)求分發(fā)器奴潘,在okhttp中網(wǎng)絡(luò)請(qǐng)求分發(fā)是通過(guò)Dispatcher分發(fā)器來(lái)維護(hù)的,Dispatcher控制著當(dāng)前網(wǎng)絡(luò)請(qǐng)求是否執(zhí)行還是進(jìn)入等待狀態(tài)影钉,這里涉及一些網(wǎng)絡(luò)請(qǐng)求的域名等判斷信息画髓。Dispatcher類源碼如下:

public final class Dispatcher {
  private int maxRequests = 64;//最大請(qǐng)求數(shù)
  private int maxRequestsPerHost = 5;//最大主機(jī)名
  private @Nullable Runnable idleCallback;
  private @Nullable ExecutorService executorService;//請(qǐng)求線程池,核心線程0平委,非核心線程最大值Integer.MAX_VALUE
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();//異步等待隊(duì)列
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();//異步請(qǐng)求隊(duì)列
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//同步請(qǐng)求隊(duì)列
.......
  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

  public synchronized void setMaxRequests(int maxRequests) {
    if (maxRequests < 1) {
      throw new IllegalArgumentException("max < 1: " + maxRequests);
    }
    this.maxRequests = maxRequests;
    promoteCalls();
  }

  public synchronized int getMaxRequests() {
    return maxRequests;
  }
  public synchronized void setMaxRequestsPerHost(int maxRequestsPerHost) {
    if (maxRequestsPerHost < 1) {
      throw new IllegalArgumentException("max < 1: " + maxRequestsPerHost);
    }
    this.maxRequestsPerHost = maxRequestsPerHost;
    promoteCalls();
  }

  public synchronized int getMaxRequestsPerHost() {
    return maxRequestsPerHost;
  }

  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();
      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }
      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

  private int runningCallsForHost(AsyncCall call) {
    int result = 0;
    for (AsyncCall c : runningAsyncCalls) {
      if (c.host().equals(call.host())) result++;
    }
    return result;
  }

  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

  void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
  }

  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }
  public synchronized List<Call> runningCalls() {
    List<Call> result = new ArrayList<>();
    result.addAll(runningSyncCalls);
    for (AsyncCall asyncCall : runningAsyncCalls) {
      result.add(asyncCall.get());
    }
    return Collections.unmodifiableList(result);
  }
  public synchronized int queuedCallsCount() {
    return readyAsyncCalls.size();
  }
  public synchronized int runningCallsCount() {
    return runningAsyncCalls.size() + runningSyncCalls.size();
  }
}

在同步請(qǐng)求時(shí)直接加入同步請(qǐng)求隊(duì)列中奈虾,持有同步請(qǐng)求的請(qǐng)求引用。對(duì)于異步請(qǐng)求來(lái)講廉赔,會(huì)判斷當(dāng)前正在執(zhí)行的請(qǐng)求是否達(dá)到最大請(qǐng)求限制愚墓,并且正在執(zhí)行請(qǐng)求的主機(jī)名是否達(dá)到個(gè)數(shù)限制,如果都沒(méi)有達(dá)到限制昂勉,則立即通過(guò)線程池進(jìn)行網(wǎng)絡(luò)請(qǐng)求,否則放入異步等待隊(duì)列扫腺。那么什么時(shí)候進(jìn)行執(zhí)行等待隊(duì)列中的請(qǐng)求呢岗照?
在一個(gè)網(wǎng)絡(luò)請(qǐng)求結(jié)束之后會(huì)調(diào)用Dispatcher的finished方法,在finished方法中會(huì)調(diào)用promoteCalls()方法來(lái)判斷時(shí)候可以執(zhí)行等待隊(duì)列中的網(wǎng)絡(luò)請(qǐng)求。

文中demo代碼:https://github.com/24KWYL/okhttp.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末攒至,一起剝皮案震驚了整個(gè)濱河市厚者,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌迫吐,老刑警劉巖库菲,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異志膀,居然都是意外死亡烫止,警方通過(guò)查閱死者的電腦和手機(jī)戳稽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門惊奇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)吼渡,“玉大人,你說(shuō)我怎么就攤上這事房维。” “怎么了阿趁?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵命黔,是天一觀的道長(zhǎng)蘑辑。 經(jīng)常有香客問(wèn)我,道長(zhǎng)副砍,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任硼控,我火速辦了婚禮匙隔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己喂柒,他們只是感情好熙参,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上神僵,一...
    開(kāi)封第一講書(shū)人閱讀 51,182評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音胁赢,去河邊找鬼。 笑死白筹,一個(gè)胖子當(dāng)著我的面吹牛智末,可吹牛的內(nèi)容都是我干的系馆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起启涯,我...
    開(kāi)封第一講書(shū)人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎恃轩,沒(méi)想到半個(gè)月后松忍,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡筷厘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年鸣峭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敞掘。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡叽掘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出玖雁,到底是詐尸還是另有隱情更扁,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布赫冬,位于F島的核電站浓镜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏劲厌。R本人自食惡果不足惜膛薛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望补鼻。 院中可真熱鬧哄啄,春花似錦、人聲如沸风范。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)硼婿。三九已至锌半,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寇漫,已是汗流浹背刊殉。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工殉摔, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人记焊。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓逸月,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親遍膜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子彻采,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353