Android 多線(xiàn)程

當(dāng)某個(gè)應(yīng)用組件啟動(dòng)且該應(yīng)用沒(méi)有運(yùn)行其他任何組件時(shí)蒂培,Android 系統(tǒng)會(huì)使用單個(gè)執(zhí)行線(xiàn)程為應(yīng)用啟動(dòng)新的 Linux 進(jìn)程戏羽。默認(rèn)情況下,同一應(yīng)用的所有組件在相同的進(jìn)程和線(xiàn)程(稱(chēng)為“主”線(xiàn)程)中運(yùn)行绒极。 如果某個(gè)應(yīng)用組件啟動(dòng)且該應(yīng)用已存在進(jìn)程(因?yàn)榇嬖谠搼?yīng)用的其他組件)与柑,則該組件會(huì)在此進(jìn)程內(nèi)啟動(dòng)并使用相同的執(zhí)行線(xiàn)程。 但是芦缰,您可以安排應(yīng)用中的其他組件在單獨(dú)的進(jìn)程中運(yùn)行企巢,并為任何進(jìn)程創(chuàng)建額外的線(xiàn)程。

應(yīng)用啟動(dòng)時(shí)让蕾,系統(tǒng)會(huì)為應(yīng)用創(chuàng)建一個(gè)名為“主線(xiàn)程”的執(zhí)行線(xiàn)程浪规。 此線(xiàn)程非常重要,因?yàn)樗?fù)責(zé)將事件分派給相應(yīng)的用戶(hù)界面小部件探孝,其中包括繪圖事件罗丰。 此外,它也是應(yīng)用與 Android UI 工具包組件(來(lái)自 android.widgetandroid.view軟件包的組件)進(jìn)行交互的線(xiàn)程再姑。因此萌抵,主線(xiàn)程有時(shí)也稱(chēng)為 UI 線(xiàn)程。

系統(tǒng)不會(huì)為每個(gè)組件實(shí)例創(chuàng)建單獨(dú)的線(xiàn)程元镀。運(yùn)行于同一進(jìn)程的所有組件均在 UI 線(xiàn)程中實(shí)例化绍填,并且對(duì)每個(gè)組件的系統(tǒng)調(diào)用均由該線(xiàn)程進(jìn)行分派。 因此栖疑,響應(yīng)系統(tǒng)回調(diào)的方法始終在進(jìn)程的 UI 線(xiàn)程中運(yùn)行讨永。

在應(yīng)用執(zhí)行繁重的任務(wù)以響應(yīng)用戶(hù)交互時(shí),除非正確實(shí)現(xiàn)應(yīng)用遇革,否則這種單線(xiàn)程模式可能會(huì)導(dǎo)致性能低下卿闹。
具體地講,如果 UI 線(xiàn)程需要處理所有任務(wù)萝快,則執(zhí)行耗時(shí)很長(zhǎng)的操作(例如锻霎,網(wǎng)絡(luò)訪(fǎng)問(wèn)或數(shù)據(jù)庫(kù)查詢(xún))將會(huì)阻塞整個(gè) UI。 一旦線(xiàn)程被阻塞揪漩,將無(wú)法分派任何事件旋恼,包括繪圖事件。 從用戶(hù)的角度來(lái)看奄容,應(yīng)用顯示為掛起冰更。 更糟糕的是,如果 UI 線(xiàn)程被阻塞超過(guò)幾秒鐘時(shí)間(目前大約是 5 秒鐘)昂勒,用戶(hù)就會(huì)看到一個(gè)讓人厭煩的“應(yīng)用無(wú)響應(yīng)”(ANR) 對(duì)話(huà)框蜀细。如果引起用戶(hù)不滿(mǎn),他們可能就會(huì)決定退出并卸載此應(yīng)用戈盈。

此外奠衔,Android UI 工具包并非線(xiàn)程安全工具包。因此,您不得通過(guò)工作線(xiàn)程操縱 UI涣觉,而只能通過(guò) UI 線(xiàn)程操縱用戶(hù)界面痴荐。 因此,Android 的單線(xiàn)程模式必須遵守兩條規(guī)則:

  • 不要阻塞 UI 線(xiàn)程
  • 不要在 UI 線(xiàn)程之外訪(fǎng)問(wèn) Android UI 工具包

所以官册,如果要執(zhí)行耗時(shí)的操作生兆,我們需要另起線(xiàn)程執(zhí)行。


1. new Thread()

new Thread(new Runnable() {
            @Override
            public void run() {
 
            }
        }).start();

這是Android系統(tǒng)里開(kāi)線(xiàn)程最簡(jiǎn)單的方式膝宁,也只能應(yīng)用于最簡(jiǎn)單的場(chǎng)景鸦难,簡(jiǎn)單的好處卻伴隨不少的隱患。

這種方式僅僅是起動(dòng)了一個(gè)新的線(xiàn)程员淫,沒(méi)有任務(wù)的概念合蔽,不能做狀態(tài)的管理。start之后介返,run當(dāng)中的代碼就一定會(huì)執(zhí)行到底拴事,無(wú)法中途取消。Runnable作為匿名內(nèi)部類(lèi)還持有了外部類(lèi)的引用圣蝎,在線(xiàn)程退出之前刃宵,該引用會(huì)一直存在,阻礙外部類(lèi)對(duì)象被GC回收徘公,在一段時(shí)間內(nèi)造成內(nèi)存泄漏牲证。沒(méi)有線(xiàn)程切換的接口,要傳遞處理結(jié)果到UI線(xiàn)程的話(huà)关面,需要寫(xiě)額外的線(xiàn)程切換代碼坦袍。

如果從UI線(xiàn)程啟動(dòng),則該線(xiàn)程優(yōu)先級(jí)默認(rèn)為Default等太,歸于default cgroup捂齐,會(huì)平等的和UI線(xiàn)程爭(zhēng)奪CPU資源。在對(duì)UI性能要求高的場(chǎng)景下要記得Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);澈驼。雖說(shuō)處于background group的線(xiàn)程總共只能爭(zhēng)取到5~10%的CPU資源辛燥,但這對(duì)絕大部分的后臺(tái)任務(wù)處理都綽綽有余了筛武,1ms和10ms對(duì)用戶(hù)來(lái)說(shuō)缝其,都是快到無(wú)法感知,所以我們一般都偏向于在background group當(dāng)中執(zhí)行工作線(xiàn)程任務(wù)徘六。


2. HandlerThread

Android主線(xiàn)程包含一個(gè)消息隊(duì)列(MessageQueue)内边,該消息隊(duì)列里面可以存入一系列的Message或Runnable對(duì)象。通過(guò)一個(gè)Handler你可以往這個(gè)消息隊(duì)列發(fā)送Message或者Runnable對(duì)象待锈,并且處理這些對(duì)象漠其。每次你新創(chuàng)建一個(gè)Handle對(duì)象,它會(huì)綁定于創(chuàng)建它的線(xiàn)程(也就是UI線(xiàn)程)以及該線(xiàn)程的消息隊(duì)列,從這時(shí)起和屎,這個(gè)handler就會(huì)開(kāi)始把Message或Runnable對(duì)象傳遞到消息隊(duì)列中拴驮,并在它們出隊(duì)列的時(shí)候執(zhí)行它們。

  • HandlerThread本質(zhì)是一個(gè)線(xiàn)程類(lèi)柴信,它繼承了Thread套啤;
  • HandlerThread有自己的內(nèi)部Looper對(duì)象,可以進(jìn)行l(wèi)ooper循環(huán)随常;
  • 通過(guò)獲取HandlerThread的looper對(duì)象傳遞給Handler對(duì)象潜沦,可以在handlerMessage方法中執(zhí)行異步任務(wù);
  • 優(yōu)點(diǎn)是不會(huì)有堵塞绪氛,減少對(duì)性能的消耗唆鸡,缺點(diǎn)是不能同時(shí)進(jìn)行多任務(wù)的處理,需要等待進(jìn)行處理枣察,處理效率較低争占;
  • HandlerThread是一個(gè)串行隊(duì)列,背后只有一個(gè)線(xiàn)程序目。

Handler可以把一個(gè)Message對(duì)象或者Runnable對(duì)象壓入到消息隊(duì)列中燃乍,進(jìn)而在UI線(xiàn)程中獲取Message或者執(zhí)行Runnable對(duì)象,Handler把壓入消息隊(duì)列有兩類(lèi)方式宛琅,Post和sendMessage刻蟹。

  • 對(duì)于Handler的Post方式來(lái)說(shuō),它會(huì)傳遞一個(gè)Runnable對(duì)象到消息隊(duì)列中嘿辟,在這個(gè)Runnable對(duì)象中舆瘪,重寫(xiě)run()方法。一般在這個(gè)run()方法中寫(xiě)入需要在UI線(xiàn)程上的操作红伦。
  • Handler如果使用sendMessage的方式把消息入隊(duì)到消息隊(duì)列中英古,需要傳遞一個(gè)Message對(duì)象,而在Handler中昙读,需要重寫(xiě)handleMessage()方法召调,用于獲取工作線(xiàn)程傳遞過(guò)來(lái)的消息,此方法運(yùn)行在UI線(xiàn)程上蛮浑。Message是一個(gè)final類(lèi)唠叛,所以不可被繼承。

HandlerThread背后只有一個(gè)線(xiàn)程沮稚,所以任務(wù)是串行執(zhí)行的艺沼。串行相對(duì)于并行來(lái)說(shuō)更安全,各任務(wù)之間不會(huì)存在多線(xiàn)程安全問(wèn)題蕴掏。

HandlerThread在實(shí)用性障般,靈活度调鲸,安全性上都有更好的表現(xiàn),但是需要寫(xiě)的代碼較多挽荡。


3. AsyncTask

public class MyAsyncTask extends AsyncTask {
 
        @Override
        protected Object doInBackground(Object[] params) {
            return null;
        }
 
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }
 
        @Override
        protected void onPostExecute(Object o) {
            super.onPostExecute(o);
        }
    }

AsyncTask是android提供的輕量級(jí)的異步類(lèi),可以直接繼承AsyncTask藐石,在類(lèi)中實(shí)現(xiàn)異步操作,并提供接口反饋當(dāng)前異步執(zhí)行的程度(可以通過(guò)接口實(shí)現(xiàn)UI進(jìn)度更新)定拟,最后反饋執(zhí)行的結(jié)果給UI主線(xiàn)程贯钩。

AsyncTask的本質(zhì)是一個(gè)靜態(tài)的線(xiàn)程池,AsyncTask派生出的子類(lèi)可以實(shí)現(xiàn)不同的異步任務(wù)办素,這些任務(wù)都是提交到靜態(tài)的線(xiàn)程池中執(zhí)行的角雷。線(xiàn)程池中的工作線(xiàn)程執(zhí)行doInBackground(mParams)方法執(zhí)行異步任務(wù)。當(dāng)任務(wù)狀態(tài)改變之后性穿,工作線(xiàn)程會(huì)向UI線(xiàn)程發(fā)送消息勺三,AsyncTask的內(nèi)部的IntentHandler響應(yīng)這些消息,并調(diào)用相關(guān)的回調(diào)函數(shù)需曾。

AsyncTask通過(guò)一個(gè)阻塞隊(duì)列BlockingQuery<Runnable>存儲(chǔ)待執(zhí)行的任務(wù)吗坚,利用靜態(tài)線(xiàn)程池THREAD_POOL_EXECUTOR提供一定數(shù)量的線(xiàn)程,默認(rèn)128個(gè)呆万。在Android 3.0以前商源,默認(rèn)采取的是并行任務(wù)執(zhí)行器,3.0以后改成了默認(rèn)采用串行任務(wù)執(zhí)行器谋减,通過(guò)靜態(tài)串行任務(wù)執(zhí)行器SERIAL_EXECUTOR控制任務(wù)串行執(zhí)行牡彻,循環(huán)取出任務(wù)交給THREAD_POOL_EXECUTOR中的線(xiàn)程執(zhí)行,執(zhí)行完一個(gè)出爹,再執(zhí)行下一個(gè)庄吼。

AsyncTask的幾處回調(diào)都給了我們機(jī)會(huì)去中斷任務(wù),在任務(wù)狀態(tài)的管理上較之Thread()方式更為靈活严就。值得注意的是AsyncTask的cancel()方法并不會(huì)終止任務(wù)的執(zhí)行总寻,開(kāi)發(fā)者需要自己去檢查cancel的狀態(tài)值來(lái)決定是否中止任務(wù)。

AsyncTask也有隱式的持有外部類(lèi)對(duì)象引用的問(wèn)題梢为,需要特別注意防止出現(xiàn)意外的內(nèi)存泄漏渐行。AsyncTask由于在不同的系統(tǒng)版本上串行與并行的執(zhí)行行為不一致,被不少開(kāi)發(fā)者所詬病铸董,這確實(shí)是硬傷祟印,絕大部分的多線(xiàn)程場(chǎng)景都需要明確任務(wù)是串行還是并行。

AsyncTask適合單個(gè)異步任務(wù)的處理袒炉。


4. ThreadPoolExecutor

線(xiàn)程池是指在初始化一個(gè)多線(xiàn)程應(yīng)用程序過(guò)程中創(chuàng)建的一個(gè)線(xiàn)程集合旁理。線(xiàn)程池在任務(wù)未到達(dá)前,會(huì)創(chuàng)建一定數(shù)量的線(xiàn)程放在空閑隊(duì)列中我磁。這些線(xiàn)程都是處于睡眠狀態(tài)的孽文,即均為啟動(dòng)但不消耗CPU,只占用較小的內(nèi)存空間夺艰。當(dāng)請(qǐng)求到來(lái)時(shí)芋哭,線(xiàn)程池給這次請(qǐng)求分配一個(gè)空閑線(xiàn)程,把請(qǐng)求傳入此線(xiàn)程中運(yùn)行郁副,進(jìn)行處理减牺。當(dāng)預(yù)制線(xiàn)程不夠時(shí),線(xiàn)程池可以自由創(chuàng)建一定數(shù)量的新線(xiàn)程用于處理更多的請(qǐng)求存谎。如果線(xiàn)程池中的最大線(xiàn)程數(shù)使用滿(mǎn)了拔疚,則會(huì)拋出異常,拒絕請(qǐng)求既荚。 當(dāng)系統(tǒng)較為空閑時(shí)稚失,也可以通過(guò)移除一部分一直處于停用狀態(tài)的線(xiàn)程。線(xiàn)程池中的每個(gè)線(xiàn)程都有可能被分配為多個(gè)任務(wù)恰聘,一旦任務(wù)完成句各,線(xiàn)程回到線(xiàn)程池等待下一次任務(wù)分配。

ThreadPoolExecutor提供了一組線(xiàn)程池晴叨,可以管理多個(gè)線(xiàn)程并行執(zhí)行凿宾。這樣一方面減少了每個(gè)并行任務(wù)獨(dú)自建立線(xiàn)程的開(kāi)銷(xiāo),另一方面可以管理多個(gè)并發(fā)線(xiàn)程的公共資源兼蕊,從而提高了多線(xiàn)程的效率初厚。所以ThreadPoolExecutor比較適合一組任務(wù)的執(zhí)行。Executors利用工廠(chǎng)模式對(duì)ThreadPoolExecutor進(jìn)行了封裝孙技,使用起來(lái)更加方便惧所。

1. Executors.newFixedThreadPool()
創(chuàng)建一個(gè)定長(zhǎng)的線(xiàn)程池,每提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線(xiàn)程绪杏,直到達(dá)到池的最大長(zhǎng)度下愈,這時(shí)線(xiàn)程池會(huì)保持長(zhǎng)度不再變化
2. Executors.newCachedThreadPool()
創(chuàng)建一個(gè)可緩存的線(xiàn)程池,如果當(dāng)前線(xiàn)程池的長(zhǎng)度超過(guò)了處理的需要時(shí)蕾久,它可以靈活的回收空閑的線(xiàn)程势似,當(dāng)需要增加時(shí),它可以靈活的添加新的線(xiàn)程僧著,而不會(huì)對(duì)池的長(zhǎng)度作任何限制
3. Executors.newScheduledThreadPool()
創(chuàng)建一個(gè)定長(zhǎng)的線(xiàn)程池履因,而且支持定時(shí)的以及周期性的任務(wù)執(zhí)行,類(lèi)似于Timer
4. Executors.newSingleThreadExecutor()
創(chuàng)建一個(gè)單線(xiàn)程化的executor盹愚,它只創(chuàng)建唯一的worker線(xiàn)程來(lái)執(zhí)行任務(wù)

線(xiàn)程池可以避免線(xiàn)程的頻繁創(chuàng)建和銷(xiāo)毀栅迄,顯然性能更好,但線(xiàn)程池并發(fā)的特性往往也是疑難雜癥的源頭皆怕,是代碼降級(jí)和失控的開(kāi)始毅舆。多線(xiàn)程并行導(dǎo)致的bug往往是偶現(xiàn)的西篓,不方便調(diào)試,一旦出現(xiàn)就會(huì)耗掉大量的開(kāi)發(fā)精力憋活。

ThreadPool較之HandlerThread在處理多任務(wù)上有更高的靈活性岂津,但也帶來(lái)了更大的復(fù)雜度和不確定性。


5. IntentService

IntentService又是另一種開(kāi)工作線(xiàn)程的方式悦即,從名字就可以看出這個(gè)工作線(xiàn)程會(huì)帶有service的屬性吮成。IntentService是繼承并處理異步請(qǐng)求的一個(gè)類(lèi)。和AsyncTask不同辜梳,沒(méi)有和UI線(xiàn)程的交互粱甫,也不像HandlerThread的工作線(xiàn)程會(huì)一直存活。IntentService背后其實(shí)也有一個(gè)HandlerThread來(lái)串行的處理Message Queue作瞄。只不過(guò)在所有的Message處理完畢之后茶宵,工作線(xiàn)程會(huì)自動(dòng)結(jié)束。所以可以把IntentService看做是Service和HandlerThread的結(jié)合體粉洼,適合需要在工作線(xiàn)程處理UI無(wú)關(guān)任務(wù)的場(chǎng)景节预。

在IntentService內(nèi)有一個(gè)工作線(xiàn)程來(lái)處理耗時(shí)操作,啟動(dòng)IntentService的方式和啟動(dòng)傳統(tǒng)的Service一樣属韧,同時(shí)安拟,當(dāng)任務(wù)執(zhí)行完后,IntentService會(huì)自動(dòng)停止宵喂,而不需要我們手動(dòng)去控制糠赦。

另外,可以啟動(dòng)IntentService多次锅棕,而每一個(gè)耗時(shí)操作會(huì)以工作隊(duì)列的方式在IntentService的onHandlerIntent回調(diào)方法中執(zhí)行拙泽,并且,每次只會(huì)執(zhí)行一個(gè)工作線(xiàn)程裸燎,執(zhí)行完后第一個(gè)再執(zhí)行第二個(gè)顾瞻。

  • 其本質(zhì)是一個(gè)特殊的Service,繼承自Service并且本身就是一個(gè)抽象類(lèi)德绿;
  • 它內(nèi)部通過(guò)HandlerThread和Handlershi'xian

載入自:

  1. 進(jìn)程和線(xiàn)程
  2. Android多線(xiàn)程的四種方式
  3. Android 線(xiàn)程的正確使用姿勢(shì)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末荷荤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子移稳,更是在濱河造成了極大的恐慌蕴纳,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件个粱,死亡現(xiàn)場(chǎng)離奇詭異古毛,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)都许,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)稻薇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嫂冻,“玉大人,你說(shuō)我怎么就攤上這事颖低⌒醭常” “怎么了弧烤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵忱屑,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我暇昂,道長(zhǎng)莺戒,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任急波,我火速辦了婚禮从铲,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘澄暮。我一直安慰自己名段,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布泣懊。 她就那樣靜靜地躺著伸辟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪馍刮。 梳的紋絲不亂的頭發(fā)上信夫,一...
    開(kāi)封第一講書(shū)人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音卡啰,去河邊找鬼静稻。 笑死,一個(gè)胖子當(dāng)著我的面吹牛匈辱,可吹牛的內(nèi)容都是我干的振湾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼亡脸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼押搪!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起梗掰,我...
    開(kāi)封第一講書(shū)人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤嵌言,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后及穗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體摧茴,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年埂陆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了苛白。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娃豹。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖购裙,靈堂內(nèi)的尸體忽然破棺而出懂版,到底是詐尸還是另有隱情,我是刑警寧澤躏率,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布躯畴,位于F島的核電站,受9級(jí)特大地震影響薇芝,放射性物質(zhì)發(fā)生泄漏蓬抄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一夯到、第九天 我趴在偏房一處隱蔽的房頂上張望嚷缭。 院中可真熱鬧,春花似錦耍贾、人聲如沸趟章。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至绷耍,卻和暖如春荠诬,著一層夾襖步出監(jiān)牢的瞬間妒潭,已是汗流浹背诺祸。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留杂伟,地道東北人移层。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像赫粥,于是被迫代替她去往敵國(guó)和親观话。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

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