Tasks and jobs

[TOC]

介紹

任務(wù)綁定到聯(lián)機(jī)或脫機(jī)數(shù)據(jù)或服務(wù)烦周,并提供使用這些資源執(zhí)行異步操作的方法帚稠。通過(guò)使用任務(wù)孵运,您可以:

  • 使用 GeodatabaseSyncTask 下載,收集和更新地理信息
  • 使用 ExportTileCacheTask 下載并顯示平鋪的底圖
  • 使用 LocatorTask查找地址并從地圖位置查找地址
  • 使用 RouteTask 計(jì)算點(diǎn)對(duì)點(diǎn)或多站路線并獲取行車(chē)路線
  • 通過(guò)使用 GeoprocessingTask 執(zhí)行地理處理模型來(lái)執(zhí)行復(fù)雜的GIS分析

任務(wù)要么直接從任務(wù)上的異步方法返回結(jié)果惠呼,要么使用作業(yè)(下文有介紹)來(lái)提供狀態(tài)更新和結(jié)果

Tasks

某些操作直接從任務(wù)上的異步方法返回其結(jié)果导俘,例如LocatorTask.geocodeAsyncRouteTask.solveRouteAsync。對(duì)于更復(fù)雜或更長(zhǎng)時(shí)間運(yùn)行的操作剔蹋,使用 jobs 代替 tasks旅薄。

要使用直接返回結(jié)果的任務(wù):

  1. 通過(guò)初始化任務(wù)來(lái)創(chuàng)建任務(wù)以使用所需的數(shù)據(jù)或服務(wù)。 某些操作可以在線和離線工作泣崩。
  2. 定義任務(wù)的輸入內(nèi)容少梁。
    2.1.某些操作僅需要輸入簡(jiǎn)單的值(例如,簡(jiǎn)單的地理編碼操作可能只需要地址字符串作為輸入)
    2.2.其他需要定義參數(shù)(例如矫付,將地理編碼操作限制到特定國(guó)家/地區(qū))凯沪。
  3. 調(diào)用異步操作方法,傳入您定義的輸入內(nèi)容买优。
  4. 根據(jù)需要使用請(qǐng)求的結(jié)果妨马,例如在地圖上顯示地理編碼結(jié)果。

下面的代碼使用默認(rèn)的Esri全局定位器創(chuàng)建LocatorTask而叼,并將地址傳遞給地理編碼身笤。操作完成后,將檢索結(jié)果位置并顯示在GraphicsOverlay中葵陵。

// Call geocodeAsync passing in an address
final LocatorTask onlineLocator = new LocatorTask("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer");
final ListenableFuture<List<GeocodeResult>> geocodeFuture = onlineLocator.geocodeAsync("380 New York Street, Redlands, CA");
geocodeFuture.addDoneListener(new Runnable() {
  @Override
  public void run() {
    try {
      // Get the results of the async operation
      List<GeocodeResult> geocodeResults = geocodeFuture.get();

      if (geocodeResults.size() > 0) {
        //顯示結(jié)果
        GeocodeResult topResult = geocodeResults.get(0);
        Graphic gecodedLocation = new Graphic(topResult.getDisplayLocation(), topResult.getAttributes(),
            new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.SQUARE, Color.rgb(255, 0, 0), 20.0f));
        mGraphicsOverlay.getGraphics().add(gecodedLocation);
      }
    }
    catch (InterruptedException | ExecutionException e) {
      dealWithException(e); // deal with exception appropriately...
    }
  }
});

某些任務(wù)是可加載的液荸,并且在調(diào)用需要任務(wù)處于加載狀態(tài)的異步方法(如上所示)時(shí)將自行加載。

Define input parameters(定義輸入?yún)?shù))

Tasts 提供了許多選項(xiàng)脱篙,使您可以根據(jù)需要定制操作娇钱。例如,在進(jìn)行地理編碼時(shí)绊困,您可以將搜索范圍限制在特定區(qū)域文搂,國(guó)家/地區(qū),地點(diǎn)類(lèi)別和/或結(jié)果數(shù)量秤朗。當(dāng)作者發(fā)布服務(wù)或打包資源時(shí)煤蹭,他們可以為發(fā)布的服務(wù)從選擇適合特定數(shù)據(jù)的選項(xiàng)或者最常見(jiàn)用例的選項(xiàng)選擇默認(rèn)值。

要查找這些默認(rèn)參數(shù)值取视,任務(wù)會(huì)提供創(chuàng)建參數(shù)對(duì)象的異步方法硝皂,并使用這些特定于服務(wù)的值進(jìn)行初始化。然后作谭,您可以在執(zhí)行操作之前對(duì)參數(shù)值進(jìn)行任意更改稽物。以這種方式創(chuàng)建參數(shù)對(duì)象對(duì)于具有許多選項(xiàng)的操作尤其有用,例如計(jì)算路徑折欠。

下面的代碼獲取 RouteTask的默認(rèn)參數(shù)贝或,然后確保使用這些參數(shù)的結(jié)果將返回路徑和方向吼过,并且輸出空間參考也匹配MapView的結(jié)果。

// use async method on RouteTask to get default parameters (task can be loaded or not loaded)
final ListenableFuture<RouteParameters> defaultParametersFuture = routeTask.createDefaultParametersAsync();
defaultParametersFuture.addDoneListener(new Runnable() {
  @Override
  public void run() {
    try {
      // get the parameters from the future
      RouteParameters routeParameters = defaultParametersFuture.get();

      // update properties of route parameters
      routeParameters.setReturnDirections(true);
      routeParameters.setReturnRoutes(true);
      if (routeParameters.getOutputSpatialReference() != mapView.getSpatialReference()) {
        routeParameters.setOutputSpatialReference(mapView.getSpatialReference());
      }
      // Use the updated parameters to solve a route...
    }
    catch (InterruptedException | ExecutionException e) {
      dealWithException(e); // deal with exception appropriately...
    }
  }
});

如果您知道所有輸入?yún)?shù)的值咪奖,則可以使用你想使用的具有構(gòu)造函數(shù)參數(shù)對(duì)象盗忱。在參數(shù)設(shè)置簡(jiǎn)單的情況下,這可以更有效羊赵。

例如售淡,下面的代碼會(huì)創(chuàng)建地理編碼參數(shù),以限制要進(jìn)行地理編碼的國(guó)家/地區(qū)慷垮,并限制返回的最大結(jié)果揖闸。

GeocodeParameters geocodeParams = new GeocodeParameters();
geocodeParams.setCountryCode("France");
geocodeParams.setMaxResults(5);

Work online or offline

許多任務(wù)可以通過(guò)使用服務(wù)在線工作,也可以使用本地?cái)?shù)據(jù)和資源離線工作料身。
例如汤纸,您可以使用默認(rèn)的Esri地理編碼服務(wù),您自己的地理編碼服務(wù)芹血,定位器文件(.loc)或移動(dòng)地圖包(.mmpk)對(duì)地址進(jìn)行地理編碼贮泞。

//使用在線服務(wù)
final LocatorTask onlineLocator = new LocatorTask("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer");

//使用離線定位器文件
final LocatorTask offlineLocator = new LocatorTask(localLocatorPath + ".loc");

// 使用移動(dòng)地圖包
final MobileMapPackage mmpk = new MobileMapPackage(localMmpkPath + ".mmpk");
    mmpk.addDoneLoadingListener(new Runnable() {
      @Override
      public void run() {
        if (mmpk.getLoadStatus() == LoadStatus.LOADED) {
          LocatorTask mmpkLocator = mmpk.getLocatorTask();
         
        }
      }
    });
mmpk.loadAsync();

Tasks and jobs

某些任務(wù)會(huì)公開(kāi)具有多個(gè)階段的操作(例如準(zhǔn)備和下載地理數(shù)據(jù)庫(kù)),并可以生成多個(gè)進(jìn)度消息(例如完成百分比)。這些類(lèi)型的任務(wù)始終綁定到ArcGIS 服務(wù)或本地服務(wù)器幔烛。一個(gè)例子是GeodatabaseSyncTask上的generateGeodatabaseAsync啃擦。

這些任務(wù)不是直接返回結(jié)果,而是使用作業(yè)來(lái)監(jiān)視狀態(tài)饿悬,返回進(jìn)度更新并返回結(jié)果令蛉。每個(gè)Job代表一個(gè)任務(wù)的特定操作。作業(yè)對(duì)于長(zhǎng)時(shí)間運(yùn)行的操作很有用狡恬,因?yàn)樗鼈円部梢詴和V槭澹謴?fù)和取消。您的應(yīng)用程序可以支持用戶或主機(jī)操作系統(tǒng)終止正在運(yùn)行的作業(yè)對(duì)象弟劲,然后在以后重新創(chuàng)建并恢復(fù)該作業(yè)祷安。

要使用以下操作

  1. 通過(guò)初始化任務(wù)來(lái)創(chuàng)建任務(wù)以使用所需的數(shù)據(jù)或服務(wù)
  2. 定義任務(wù)的輸入?yún)?shù)。
  3. 調(diào)用異步操作方法來(lái)獲取作業(yè)兔乞,傳入您定義的輸入?yún)?shù)汇鞭。
  4. 開(kāi)始工作
  5. (可選)偵聽(tīng)作業(yè)狀態(tài)的更改并檢查作業(yè)消息,例如更新UI并向用戶報(bào)告進(jìn)度庸追。
  6. 聽(tīng)取工作完成情況并從操作中獲取結(jié)果霍骄。檢查作業(yè)中的錯(cuò)誤,如果成功锚国,請(qǐng)使用結(jié)果腕巡。

例:

// 創(chuàng)建在線平鋪服務(wù)任務(wù)
ExportTileCacheTask exportTilesTask = new ExportTileCacheTask("http://sampleserver6.arcgisonline.com/arcgis/rest/services/World_Street_Map/MapServer");

// 參數(shù)對(duì)象
ExportTileCacheParameters exportTilesParameters = new ExportTileCacheParameters();
exportTilesParameters.getLevelIDs().addAll(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
exportTilesParameters.setAreaOfInterest(
    new Envelope(-1652366.0, 2939253.0, 2537014.0, 8897484.0, SpatialReferences.getWebMercator()));

// 創(chuàng)建Job
final ExportTileCacheJob exportJob = exportTilesTask.exportTileCacheAsync(exportTilesParameters, exportedTpkPath);

// 監(jiān)聽(tīng) Job 進(jìn)度玄坦,修改進(jìn)度條
exportJob.addJobChangedListener(new Runnable() {
  @Override
  public void run() {
    List<Job.Message> messages = exportJob.getMessages();
    updateUiWithProgress(messages.get(messages.size() - 1).getMessage());
  }
});

// 監(jiān)聽(tīng) Job 結(jié)果 血筑,完成后使用結(jié)果
exportJob.addJobDoneListener(new Runnable() {
  @Override
  public void run() {
    // 發(fā)生錯(cuò)誤
    if (exportJob.getError() != null) {
      dealWithException(exportJob.getError()); 
      return;
    }
// 執(zhí)行成功
    if (exportJob.getStatus() == Job.Status.SUCCEEDED) {
      // 在地圖中顯示結(jié)果
      final TileCache exportedTileCache = exportJob.getResult();
      ArcGISTiledLayer tiledLayer = new ArcGISTiledLayer(exportedTileCache);
      mapView.getMap().getOperationalLayers().add(tiledLayer);
    }
  }
});

// 執(zhí)行 Job
exportJob.start();

調(diào)用Job.getStatus將檢索作業(yè)工作流中的當(dāng)前點(diǎn).開(kāi)始工作定期接收工作變更事件;隨著工作的進(jìn)展绘沉,這種情況會(huì)隨著頻率的降低而發(fā)生;每個(gè)呼叫可能已為作業(yè)添加了多個(gè)JobMessage。對(duì)于成功和失敗豺总,一旦作業(yè)完成车伞,就會(huì)調(diào)用作業(yè)監(jiān)聽(tīng)器。已完成的作業(yè)喻喳,無(wú)論是成功還是失敗另玖,都無(wú)法重新啟動(dòng)。

Report job progress

job表示可能需要一些時(shí)間才能完成的異步運(yùn)行操作表伦。如前所述谦去,您可以在作業(yè)完成,失敗或被取消時(shí)監(jiān)視作業(yè)狀態(tài)的更改蹦哼,但是中間的時(shí)間如何呢鳄哭?用戶可能會(huì)因等待長(zhǎng)時(shí)間工作而無(wú)法獲得有關(guān)其進(jìn)度的反饋而感到不耐煩,jobs 提供了一種機(jī)制纲熏,用于報(bào)告它們所代表的運(yùn)行操作的當(dāng)前進(jìn)度(完成百分比)妆丘。

當(dāng)作業(yè)運(yùn)行時(shí),調(diào)用作業(yè)更改的偵聽(tīng)器(使用Job.addJobChangedListener方法偵聽(tīng))局劲。您可以從作業(yè)的Progress屬性中獲取作業(yè)的當(dāng)前進(jìn)度勺拣,該屬性是一個(gè)整數(shù),表示已完成操作的百分比鱼填。這允許您的應(yīng)用程序使用UI元素(例如進(jìn)度條)提供有關(guān)正在運(yùn)行的作業(yè)狀態(tài)的更多特定信息药有。

exportJob.start();
// Add a listener to display the % of the job done
exportJob.addProgressChangedListener(new Runnable() {
  @Override public void run() {
    updateUiWithProgress("Progress: " + String.valueOf(exportJob.getProgress()) +"%");
  }
});
// Add a listener to get the result when job is done
exportJob.addJobDoneListener(new Runnable() {
  @Override public void run() {
    TileCache exportedTileCacheResult = exportJob.getResult();
  }
});

Pause, resume, or cancel job

作業(yè)旨在有效地處理在作業(yè)運(yùn)行時(shí)退出應(yīng)用程序的用戶,并處理由主機(jī)操作系統(tǒng)終止的應(yīng)用程序苹丸。Jobs 還處理明確的暫停和取消操作塑猖。

Cancel a job

有時(shí)候不再需要工作的結(jié)果。例如谈跛,用戶可以改變他們想要下載的圖塊緩存區(qū)域的想法羊苟,并且想要取消作業(yè)并重新開(kāi)始。

在作業(yè)上調(diào)用Job.cancel會(huì)立即將其狀態(tài)更改為Job.Status.FAILED感憾,并將其他信息添加到作業(yè)錯(cuò)誤對(duì)象蜡励。將調(diào)用作業(yè)完成偵聽(tīng)器。錯(cuò)誤對(duì)象指示錯(cuò)誤域和代碼阻桅,允許您識(shí)別何時(shí)發(fā)生取消凉倚。

下面的代碼顯示,對(duì)于正在運(yùn)行的ExportTileCacheJob嫂沉,添加了JobDoneListener稽寒。在偵聽(tīng)器中,代碼檢查相應(yīng)的錯(cuò)誤代碼和域趟章,指示作業(yè)已被取消杏糙。此時(shí)慎王,代碼檢查以防已創(chuàng)建切片緩存文件,如果已創(chuàng)建宏侍,則刪除它赖淤。

// 添加監(jiān)聽(tīng)
runningJob.addJobDoneListener(new Runnable() {
  @Override
  public void run() {
    // 經(jīng)檢測(cè)是否 error
    ArcGISRuntimeException jobError = runningJob.getError();
    if (jobError != null) {

      //檢測(cè)是否是 failed
      if (runningJob.getStatus() == Job.Status.FAILED) {

        // 是否是用戶取消
        if ((jobError.getErrorCode() == 18) &&
            (jobError.getErrorDomain() == ArcGISRuntimeException.ErrorDomain.ARCGIS_RUNTIME)) {

          // 取消后的UI刷行
          updateUiWithProgress("Export has been cancelled");

          // 檢測(cè)是否已經(jīng)緩存
          File tileCacheFile = new File(exportedTpkPath);
          if (tileCacheFile.exists()) {
            tileCacheFile.delete();
          }
          return;
        }
      }

      dealWithException(jobError); // 處理其他原因造船的錯(cuò)誤
      return;
    }

    dealWithJobDoneSuccess(); // 成功后的處理
  }
});

雖然您可以通過(guò)重復(fù)使用相同的參數(shù)來(lái)啟動(dòng)新作業(yè),但無(wú)法重新啟動(dòng)已取消的作業(yè)谅河。取消作業(yè)不一定會(huì)停止任何服務(wù)器端進(jìn)程咱旱,因?yàn)椴⒎撬蟹?wù)都支持服務(wù)器端的取消。

Pause and resume a job

job 可以是長(zhǎng)時(shí)間運(yùn)行的操作绷耍,因此無(wú)法保證在應(yīng)用程序運(yùn)行時(shí)它們將完成吐限。您可以使用Job.pause顯式暫停作業(yè)。例如褂始,當(dāng)應(yīng)用程序切換到后臺(tái)并且沒(méi)有后臺(tái)操作權(quán)限時(shí)毯盈。如果用戶希望出于任何原因暫時(shí)停止網(wǎng)絡(luò)訪問(wèn),則暫停也可能是有用的病袄。

暫停 job 將不會(huì)收到作業(yè)更改的消息搂赋。暫停作業(yè)不會(huì)阻止任何服務(wù)器端進(jìn)程執(zhí)行。當(dāng)作業(yè)暫停時(shí)益缠,未完成的請(qǐng)求可以完成脑奠,因此恢復(fù)作業(yè)可能會(huì)導(dǎo)致它與暫停時(shí)的狀態(tài)不同。

您可以將 job 序列化為JSON幅慌,以便在您的應(yīng)用程序處于后臺(tái)運(yùn)行時(shí)保持該作業(yè)宋欺,否則該過(guò)程將被終止。再次反序列化時(shí)胰伍,job 將處于Job.Status.PAUSED狀態(tài)齿诞,無(wú)論其序列化時(shí)的狀態(tài)如何,應(yīng)重新啟動(dòng)以繼續(xù)偵聽(tīng)完成骂租。Job 更改的偵聽(tīng)器可以是您的應(yīng)用程序更新作業(yè)JSON以存儲(chǔ)的好地方祷杈。

下面的代碼顯示,對(duì)于現(xiàn)有正在運(yùn)行的Job渗饮,將作業(yè)序列化為JSON但汞。

mRunningJob.addJobChangedListener(new Runnable() {
  @Override
  public void run() {
    // Every time the job changes, update the stored JSON that represents the Job.
    mJobJson = mRunningJob.toJson();
  }
});

然后可以在適當(dāng)時(shí)保存此JSON,例如互站,通過(guò)覆蓋Activity中的onSaveInstanceState并將JSON String保存到Bundle參數(shù)私蕾。可以在適當(dāng)?shù)臅r(shí)候類(lèi)似地檢索JSON胡桃,例如在Activity的onRestoreInstanceState或onCreate方法中踩叭,具體取決于作業(yè)是應(yīng)該被視為持久數(shù)據(jù)還是臨時(shí)數(shù)據(jù)。

反序列化保存的 JSON數(shù)據(jù)

private void deserializeFromJson() {
  if ( (mJobJson != null) && (!mJobJson.isEmpty())) {
    mRunningJob = Job.fromJson(mJobJson);

    if (mRunningJob != null) {
      // 反序列化Job.Status.PAUSED狀態(tài)的 job,
      mRunningJob.start();

    
      mRunningJob.addJobChangedListener(new Runnable() {
        @Override
        public void run() {
          dealWithJobChanged();
        }
      });

      mRunningJob.addJobDoneListener(new Runnable() {
        @Override
        public void run() {
          dealWithJobDone(); 
        }
      });
    }
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市容贝,隨后出現(xiàn)的幾起案子自脯,更是在濱河造成了極大的恐慌,老刑警劉巖嗤疯,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異闺兢,居然都是意外死亡茂缚,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)屋谭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)脚囊,“玉大人,你說(shuō)我怎么就攤上這事桐磁』谠牛” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵我擂,是天一觀的道長(zhǎng)衬以。 經(jīng)常有香客問(wèn)我,道長(zhǎng)校摩,這世上最難降的妖魔是什么看峻? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮衙吩,結(jié)果婚禮上互妓,老公的妹妹穿的比我還像新娘。我一直安慰自己坤塞,他們只是感情好冯勉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著摹芙,像睡著了一般灼狰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上浮禾,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天伏嗜,我揣著相機(jī)與錄音,去河邊找鬼伐厌。 笑死承绸,一個(gè)胖子當(dāng)著我的面吹牛挣轨,可吹牛的內(nèi)容都是我干的军熏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼卷扮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼荡澎!你這毒婦竟也來(lái)了均践?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤摩幔,失蹤者是張志新(化名)和其女友劉穎彤委,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體或衡,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡焦影,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了封断。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斯辰。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖坡疼,靈堂內(nèi)的尸體忽然破棺而出彬呻,到底是詐尸還是另有隱情,我是刑警寧澤柄瑰,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布闸氮,位于F島的核電站,受9級(jí)特大地震影響教沾,放射性物質(zhì)發(fā)生泄漏湖苞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一详囤、第九天 我趴在偏房一處隱蔽的房頂上張望财骨。 院中可真熱鬧,春花似錦藏姐、人聲如沸隆箩。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)捌臊。三九已至,卻和暖如春兜材,著一層夾襖步出監(jiān)牢的瞬間理澎,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工曙寡, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留糠爬,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓举庶,卻偏偏與公主長(zhǎng)得像执隧,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355