android intent以及Dart&Henson

一、顯式Intent和隱式Intent

參考
Android理解:顯式和隱式Intent
Android中Intent概述及使用

顯式的Intent:如果Intent中明確包含了要啟動(dòng)的組件的完整類名(包名及類名),那么這個(gè)Intent就是explict的,即顯式的。使用顯式Intent最典型的情形是在你自己的App中啟動(dòng)一個(gè)組件,因?yàn)槟阕约嚎隙ㄖ雷约旱囊獑?dòng)的組件的類名。比如,為了響應(yīng)用戶操作通過顯式的Intent在你的App中啟動(dòng)一個(gè)Activity或啟動(dòng)一個(gè)Service下載文件。
隱式的Intent:如果Intent沒有包含要啟動(dòng)的組件的完整類名,那么這個(gè)Intent就是implict的,即隱式的。雖然隱式的Intent沒有指定要啟動(dòng)的組件的類名,但是一般情況下,隱式的Intent都要指定需要執(zhí)行的action。一般,隱式的Intent只用在當(dāng)我們想在自己的App中通過Intent啟動(dòng)另一個(gè)App的組件的時(shí)候,讓另一個(gè)App的組件接收并處理該Intent。例如,你想在地圖上給用戶顯示一個(gè)位置,但是你的App又不支持地圖展示,這時(shí)候你可以將位置信息放入到一個(gè)Intent中,然后給它指定相應(yīng)的action,通過這樣隱式的Intent請(qǐng)求其他的地圖型的App(例如Google Map、百度地圖等)來在地圖中展示一個(gè)指定的位置。隱式的Intent也體現(xiàn)了Android的一種設(shè)計(jì)哲學(xué):我自己的App無需包羅萬象所有功能,可以通過與其他App組合起來,給用戶提供很好的用戶體驗(yàn)。而連接自己的App與其他App的紐帶就是隱式Intent。

  • 顯式Intent
    第一個(gè)參數(shù)是活動(dòng)上下文,第二個(gè)是目標(biāo)活動(dòng)的class
    FirstActivity:
button.setOnClickListener(new OnClickListener(){
   public void onClick(View v){
      Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
      startActivity(intent);
   }
});
  • 隱式Intent
    由系統(tǒng)分析Intent,并找出合適的activity去啟動(dòng)。
    FirstActivity
button.setOnClickListener(new OnClickListener(){
   public void onClick(View v){
     Intent intent = new Intent("com.example.activitytest.ACTION_START");
     //setAction方式是一樣的
     //Intent intent = new Intent();
     //intent.setAction("com.example.activitytest.ACTION_START");
     intent.addCategory("com.example.activitytest.MY_CATEGORY");
     startActivity(intent);
   }
});

SecondActivity

<activity android:name="SecondActivity">
   <intent-filter>
     <!-- 意圖過濾器, 只要滿足了action(動(dòng)作), 和 category(分類),那么就啟動(dòng)這個(gè)界面 -->
     <action android:name="com.example.activitytest.ACTION_START"/>
     <category android:name="com.example.activitytest.MY_CATEGORY"/>
     <category android:name="android.intent.category.DEFAULT"/>
     //default category一定要有,這是默認(rèn)的
</intent-filter>

通過設(shè)置Action字符串,表明自己的意圖,即我想干嘛,需要由系統(tǒng)解析,找到能夠處理這個(gè)Intent的Activity并啟動(dòng)。比如我想打電話,則可以設(shè)置Action為"android.intent.action.DIAL"字符串,表示打電話的意圖,系統(tǒng)會(huì)找到能處理這個(gè)意圖的Activity,例如調(diào)出撥號(hào)面板。

有幾點(diǎn)需要注意:
1、這個(gè)Activity其他應(yīng)用程序也可以調(diào)用,只要使用這個(gè)Action字符串。這樣應(yīng)用程序之間交互就很容易了,例如手機(jī)QQ可以調(diào)用QQ空間,可以調(diào)用騰訊微博等。因?yàn)槿绱耍瑸榱朔乐箲?yīng)用程序之間互相影響,一般命名方式是包名+Action名,例如命名"abcdefg"就很不合理了,就應(yīng)該改成"com.example.app016.MyTest"。
2、當(dāng)然,你可以在自己的程序中調(diào)用其他程序的Action。
例如可以在自己的應(yīng)用程序中調(diào)用撥號(hào)面板:

Intent intent = new Intent(Intent.ACTION_DIAL);  
// 或者Intent intent = new Intent("android.intent.action.DIAL");  
// Intent.ACTION_DIAL是內(nèi)置常量,值為"android.intent.action.DIAL"  
startActivity(intent);

3、一個(gè)Activity可以處理多種Action
只要你的應(yīng)用程序夠牛逼,一個(gè)Activity可以看網(wǎng)頁,打電話,發(fā)短信,發(fā)郵件。。。當(dāng)然可以。
Intent的Action只要是其中之一,就可以打開這個(gè)Activity。

<activity  
    android:name="com.example.app016.SecondActivity">  
    <intent-filter>  
        <!-- 可以處理下面三種Intent -->  
        <action android:name="com.example.app016.SEND_EMAIL"/>  
        <action android:name="com.example.app016.SEND_MESSAGE"/>  
        <action android:name="com.example.app016.DAIL"/>  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</activity>  

對(duì)于一個(gè)Action字符串,系統(tǒng)有可能會(huì)找到一個(gè)Activity能處理這個(gè)Action,也有可能找到多個(gè)Activity,也可能一個(gè)都找不到。
1、找到一個(gè)Activity
很簡(jiǎn)單,直接打開這個(gè)Activity。這個(gè)不需要解釋。
2、找到多個(gè)Acyivity
系統(tǒng)會(huì)提示從多個(gè)activity中選擇一個(gè)打開。
例如我們自己開發(fā)一個(gè)撥號(hào)面板應(yīng)用程序,可以設(shè)置activity的<intent-filter>中Action name為"android.intent.action.DIAL",這樣別的程序調(diào)用撥號(hào)器時(shí),用戶可以從Android自帶的撥號(hào)器和我們自己開發(fā)的撥號(hào)器中選擇。

<activity  
    android:name="com.example.app016.SecondActivity">  
    <intent-filter>  
        <action android:name="android.intent.action.DIAL"/>  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</activity>  

這也就是當(dāng)Android手機(jī)裝上UC瀏覽器后,打開網(wǎng)頁時(shí)會(huì)彈出選擇Android自帶瀏覽器還是UC瀏覽器,可能都會(huì)遇到過。
3、一個(gè)Activity都沒找到
一個(gè)都沒找到的話,程序就會(huì)出錯(cuò),會(huì)拋出ActivityNotFoundException。比如隨便寫一個(gè)action字符串:

Intent intent = new Intent("asasasas");  
startActivity(intent);  

所以應(yīng)該注意try catch異常。

Intent intent = new Intent("asasasas");  
try  
{  
    startActivity(intent);  
}  
catch(ActivityNotFoundException e)  
{  
    Toast.makeText(this, "找不到對(duì)應(yīng)的Activity", Toast.LENGTH_SHORT).show();  
}  

或者也可以使用Intent的resolveActivity方法判斷這個(gè)Intent是否能找到合適的Activity,如果沒有,則不再startActivity,或者可以直接禁用用戶操作的控件。

Intent intent = new Intent(Intent.ACTION_DIAL);  
if(intent.resolveActivity(getPackageManager()) == null)  
{  
    // 設(shè)置控件不可用  
}  

注意resolveActivity方法返回值就是顯式Intent上面講到的ComponentName對(duì)象,一般情況下也就是系統(tǒng)找到的那個(gè)Activity。但是如果有多個(gè)Activity可供選擇的話,則返回的Component是com.android.internal.app.ResolverActivity,也就是用戶選擇Activity的那個(gè)界面對(duì)應(yīng)的Activity,這里不再深究。

Intent intent = new Intent(Intent.ACTION_DIAL);  
ComponentName componentName = intent.resolveActivity(getPackageManager());  
if(componentName != null)  
{  
    String className = componentName.getClassName();  
    Toast.makeText(this, className, Toast.LENGTH_SHORT).show();  
}  

當(dāng)創(chuàng)建了一個(gè)顯式Intent去啟動(dòng)Activity或Service的時(shí)候,系統(tǒng)會(huì)立即啟動(dòng)Intent中所指定的組件。

當(dāng)創(chuàng)建了一個(gè)隱式Intent去使用的時(shí)候,Android系統(tǒng)會(huì)將該隱式Intent所包含的信息與設(shè)備上其他所有App中manifest文件中注冊(cè)的組件的Intent Filters進(jìn)行對(duì)比過濾,從中找出滿足能夠接收處理該隱式Intent的App和對(duì)應(yīng)的組件。如果有多個(gè)App中的某個(gè)組件都符合條件,那么Android會(huì)彈出一個(gè)對(duì)話框讓用戶選擇需要啟動(dòng)哪個(gè)App。

Intent Filter,即Intent過濾器,一個(gè)組件可以包含0個(gè)或多個(gè)Intent Filter。Intent Filter是寫在App的manifest文件中的,其通過設(shè)置action或uri數(shù)據(jù)類型等指明了組件能夠處理接收的Intent的類型。如果你給你的Activity設(shè)置了Intent Filter,那么這就使得其他的App有可能通過隱式Intent啟動(dòng)你的這個(gè)Activity。反之,如果你的Activity不包含任何Intent Filter,那么該Activity只能通過顯式Intent啟動(dòng),由于我們一般不會(huì)暴露出我們組件的完整類名,所以這種情況下,其他的App基本就不可能通過Intent啟動(dòng)我們的Activity了(因?yàn)樗麄儾恢涝揂ctivity的完整類名),只能由我們自己的App通過顯式Intent啟動(dòng)。

需要注意的是,為了確保App的安全性,我們應(yīng)該總是使用顯式Intent去啟動(dòng)Service并且不要為該Service設(shè)置任何的Intent Filter。通過隱式的Intent啟動(dòng)Service是有風(fēng)險(xiǎn)的,因?yàn)槟悴淮_定最終哪個(gè)App中的哪個(gè)Service會(huì)啟動(dòng)起來以響應(yīng)你的隱式Intent,更悲催的是,由于Service沒有UI的在后臺(tái)運(yùn)行,所以用戶也不知道哪個(gè)Service運(yùn)行了。

二、傳遞數(shù)據(jù)
  • 向下一個(gè)活動(dòng)傳遞數(shù)據(jù)
    FirstActivity:
button.setOnClickListener(new OnClickListener(){
   public void onClick(View v){
     Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
     ***intent.putExtra("extra_data","Hello");***
     startActivity(intent);
   }
});

SecondActivity

protected void onCreate(Bundle savedInstanceState){
   super.onCreate(savedInstanceState);
   Intent intent = getIntent();
   String data = intent.getStringExtra("extra_data");
   //getIntExtra  getBooleanExtra 取值方式與傳遞的數(shù)據(jù)要一致
}
  • 返回?cái)?shù)據(jù)給上一個(gè)活動(dòng)
    startActivityForResult方法也是用于啟動(dòng)活動(dòng)的,但這個(gè)方法期望在活動(dòng)銷毀時(shí)能夠返回結(jié)果給上一個(gè)活動(dòng)。
    FirstActivity:
button.setOnClickListener(new OnClickListener(){
   public void onClick(View v){
     Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
     startActivityForResult(intent,1);//請(qǐng)求碼
   }
});
//使用startActivityForResult啟動(dòng)活動(dòng),會(huì)在銷毀后回調(diào)onActivityResult方法
protected void onActivityResult(int requestCode,int resultCode,Intent data){
   switch(requestCode){
   case 1:
     if(resultCode == RESULT_OK){ 
       String returnData = data.getStringExtra("data_return");
       ....
   }
   }
}

SecondActivity

   Intent intent = new Intent();
   ***intent.putExtra("data_return","Hello");***
   setResult(RESULT_OK,intent);//專門用于向上一個(gè)活動(dòng)返回?cái)?shù)據(jù)
}

利用Intent的Extra部分來存儲(chǔ)我們想要傳遞的數(shù)據(jù),可以傳送int, long, char等一些基礎(chǔ)類型,對(duì)復(fù)雜的對(duì)象就無能為力了。

傳復(fù)雜對(duì)象參考
Android Bundle總結(jié)
Android五種數(shù)據(jù)傳遞方法匯總
Intent傳遞對(duì)象——Serializable和Parcelable區(qū)別
Android系統(tǒng)中Parcelable和Serializable的區(qū)別

1.使用Serializable很簡(jiǎn)單,指定Person類implements Serializable即可。注意,要指定serialVersionUID,否則反序列化會(huì)出問題。
可參考如何讓Android Studio 自動(dòng)生成 serialVersionUID

Person person = new Person();
person.setName("tom");
person.setAge(20);
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("person_data",person);
startActivity(intent);

SecondActivity:
Person person = (Person)getIntent().getSerializableExtra("person_data");

2.Parcelable稍微復(fù)雜一些,要實(shí)現(xiàn)幾個(gè)方法。

public class Person implements Parcelable{
   private String name;
   private int age;
   
   public int describeContents(){
      return 0;
   }
   
   public void writeToParcel(Parcel dest,int flags){
      dest.writeString(name);
      dest.writeInt(age);
   }
   
   public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>(){
      public Person createFromParcel(Parcel source){
         Person person = new Person();
         person.name = source.readString();
         person.age = source.readInt();
         return person;
      }
      
      public Person[] newArray(int size){
         return new Person[size];
      }
   };
SecondActivity:
Person person = (Person)getIntent().getPacelableExtra("person_data");
三、Dart&Henson

參考
【譯】使用 Dart & Henson 改進(jìn) Android Intents
用Dart&Henson玩轉(zhuǎn)Activity跳轉(zhuǎn)
上面?zhèn)鬟f數(shù)據(jù)的方式很好地處理了組件的創(chuàng)建與通信,但仍然有一些問題需要注意:

  • 目標(biāo)組件作為一個(gè)實(shí)體,對(duì)輸入沒有任何控制權(quán)。在我們的例子里,itemId 是必需的,但如果沒有傳遞它,DetailActivity 最好的處理方式也只能是拋出異常。
  • Intent 的創(chuàng)建(完全)不夠健壯,并沒有對(duì) extra 中的 key 或 value 做任何檢查。

解決這些問題的一個(gè)可能的方案是 Intent 工廠模式。它主要由一些工廠方法組成,包含了應(yīng)用程序里用到的各種 Intent。比如像下面這樣的 Intent 工廠:

public class IntentFactory {
  public Intent newDetailActivityIntent(Context context, String itemId, boolean showMap) {
    Intent intent = new Intent(context, DetailActivity.class);
    intent.putExtra(EXTRA_ITEM_ID, itemId);
    intent.putExtra(EXTRA_SHOW_MAP, showMap);
    return intent;
  }
  ...
}

然而,這種解決方案有一些局限,并不是一個(gè)很好的辦法。

  • Intent 工廠是一個(gè)集中類,這個(gè)類可能會(huì)變得很大且復(fù)雜。
  • 它違背了開放/閉合原則。對(duì)修改并沒有關(guān)閉,我們將總是需要給每個(gè)新的 Activity 添加一個(gè)新方法。
  • 目標(biāo)組件應(yīng)該是唯一知曉參數(shù)細(xì)節(jié)與邏輯的地方。
  • 可選參數(shù)處理。同一個(gè)組件有不同的需求,是否應(yīng)該寫不同的方法?還是寫一個(gè)方法并使用默認(rèn)值?
    它會(huì)誘使后續(xù)的開發(fā)人員模仿,進(jìn)而產(chǎn)生其他的 Intent 工廠,最終演變成大泥球模式,使得代碼越來越糟。

有一個(gè)類似的策略可以分散這些工廠方法到各自的目標(biāo)組件。也就是指,每個(gè)組件可以包含一個(gè)(或多個(gè))靜態(tài)方法,用于生成這些啟動(dòng)它自身的 Intent。這個(gè)辦法可以解決開放/閉合原則的問題,分解 Intent 工廠,也許還可以避免大泥球模式。盡管如此,關(guān)于可選參數(shù)的問題依然存在。有人說 builder 模式可以?我們自己實(shí)現(xiàn)它?...

Dart 是一個(gè) Android 開源庫(kù)(Henson實(shí)際是Dart項(xiàng)目的子項(xiàng)目)。它綁定 Activity 字段到 Intent extra,Butter Knife 也是用類似的方案,關(guān)聯(lián) Activity 與 XML 布局中的View。

這里我們假設(shè)要從MainActivity跳轉(zhuǎn)到DetailActivity,DetailActivity中要接受三個(gè)參數(shù)分別是String name,int age和User user。DetailActivity需要接受上述三個(gè)參數(shù),僅僅通過@InjectExtra注解即可,然后在onCreate
中執(zhí)行Dart.inject(this),詳細(xì)的代碼為:

public class DetailActivity extends AppCompatActivity {
    @InjectExtra
    String name = "default name";

    @InjectExtra
    int age = 0;

    @Nullable
    @InjectExtra
    User user;

    @BindView(R.id.tvName)
    TextView tvName;

    @BindView(R.id.tvAge)
    TextView tvAge;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);
        ButterKnife.bind(this);
        Dart.inject(this);
        initView();
    }

    private void initView() {
        // 使用name,age,user
    }
}

String name = "default name";這句話給了name一個(gè)默認(rèn)值,但是當(dāng)Dart.inject執(zhí)行后會(huì)被傳遞過來的數(shù)據(jù)覆蓋。
@Nullable加在了user上說明這個(gè)數(shù)據(jù)可以不用傳遞。接受端就這么多代碼,下面讓我們看看發(fā)送端如果發(fā)送數(shù)據(jù),如何跳轉(zhuǎn)到DetailActivity。

注意編寫上述代碼后,我們要先編譯下項(xiàng)目,編譯好后Henson會(huì)通過注解處理器生成可以跳轉(zhuǎn)到DetailActivity的DSL(領(lǐng)域特定語言)段,方便其他組件對(duì)DetailActivity的跳轉(zhuǎn),我們?cè)贛ainActivity上只要寫上下列代碼,即可完成界面跳轉(zhuǎn)和數(shù)據(jù)傳遞:

User user = new User();
user.setAge(Integer.parseInt(age.getText().toString()));
user.setName(name.getText().toString());
startActivity(
        Henson.with(this)
                .gotoDetailActivity()
                .age(27)
                .name("jason")
                .user(user)
                .build()
);

當(dāng)你寫完Henson.with(this)后代碼提示會(huì)自動(dòng)彈出.gotoDetailActivity(),Henson幫助你提示你DetailActivity是可以被跳轉(zhuǎn)的;隨后你繼續(xù)寫下.gotoDetailActivity()后,又自動(dòng)彈出.age()方法,提示你傳入一個(gè)int類型給age,寫好后,又自動(dòng)彈出.name()方法,以此類推,最后以一個(gè)build()收?qǐng)觥?br> 注意,這里你可以不寫.user(),因?yàn)樵贒etailActivity中我們指定它是nullable的,可以不傳。但是.age()和.name()都是會(huì)強(qiáng)制彈出讓你填寫數(shù)據(jù)的。如果我偏向要對(duì)調(diào)name和age的傳入順序呢?發(fā)現(xiàn)不行~如果你寫完.gotoDetailActivity()后發(fā)現(xiàn)只有.age()的提示,卻沒有.name()的,即強(qiáng)制要求你先傳遞age。這里作者說明后才知道他們是按照字幕順序來排數(shù)據(jù)傳入的,目前沒有更好的方法。

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

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