Android Context完全解析渺鹦,你所不知道的Context的各種細(xì)節(jié)

Context相信所有的Android開發(fā)人員基本上每天都在接觸,因?yàn)樗R娏怂ㄆ薄5沁@并不代表Context沒有什么東西好講的,實(shí)際上Context有太多小的細(xì)節(jié)并不被大家所關(guān)注愕够,那么今天我們就來學(xué)習(xí)一下那些你所不知道的細(xì)節(jié)走贪。

Context類型

我們知道,Android應(yīng)用都是使用Java語言來編寫的惑芭,那么大家可以思考一下坠狡,一個Android程序和一個Java程序,他們最大的區(qū)別在哪里遂跟?劃分界限又是什么呢逃沿?其實(shí)簡單點(diǎn)分析婴渡,Android程序不像Java程序一樣,隨便創(chuàng)建一個類凯亮,寫個main()方法就能跑了边臼,而是要有一個完整的Android工程環(huán)境,在這個環(huán)境下触幼,我們有像Activity硼瓣、Service、BroadcastReceiver等系統(tǒng)組件置谦,而這些組件并不是像一個普通的Java對象new一下就能創(chuàng)建實(shí)例的了堂鲤,而是要有它們各自的上下文環(huán)境,也就是我們這里討論的Context媒峡∥疗埽可以這樣講,Context是維持Android程序中各組件能夠正常工作的一個核心功能類谅阿。

下面我們來看一下Context的繼承結(jié)構(gòu):

Context的繼承結(jié)構(gòu)還是稍微有點(diǎn)復(fù)雜的半哟,可以看到,直系子類有兩個签餐,一個是ContextWrapper寓涨,一個是ContextImpl。那么從名字上就可以看出氯檐,ContextWrapper是上下文功能的封裝類戒良,而ContextImpl則是上下文功能的實(shí)現(xiàn)類。而ContextWrapper又有三個直接的子類冠摄,ContextThemeWrapper糯崎、Service和Application。其中河泳,ContextThemeWrapper是一個帶主題的封裝類沃呢,而它有一個直接子類就是Activity。

那么在這里我們至少看到了幾個所比較熟悉的面孔拆挥,Activity薄霜、Service、還有Application纸兔。由此惰瓜,其實(shí)我們就已經(jīng)可以得出結(jié)論了,Context一共有三種類型食拜,分別是Application、Activity和Service副编。這三個類雖然分別各種承擔(dān)著不同的作用负甸,但它們都屬于Context的一種,而它們具體Context的功能則是由ContextImpl類去實(shí)現(xiàn)的。

那么Context到底可以實(shí)現(xiàn)哪些功能呢呻待?這個就實(shí)在是太多了打月,彈出Toast、啟動Activity蚕捉、啟動Service奏篙、發(fā)送廣播、操作數(shù)據(jù)庫等等等等都需要用到Context迫淹。由于Context的具體能力是由ContextImpl類去實(shí)現(xiàn)的秘通,因此在絕大多數(shù)場景下,Activity敛熬、Service和Application這三種類型的Context都是可以通用的肺稀。不過有幾種場景比較特殊,比如啟動Activity应民,還有彈出Dialog话原。出于安全原因的考慮,Android是不允許Activity或Dialog憑空出現(xiàn)的诲锹,一個Activity的啟動必須要建立在另一個Activity的基礎(chǔ)之上繁仁,也就是以此形成的返回棧。而Dialog則必須在一個Activity上面彈出(除非是System Alert類型的Dialog)归园,因此在這種場景下黄虱,我們只能使用Activity類型的Context,否則將會出錯蔓倍。

Context數(shù)量

那么一個應(yīng)用程序中到底有多少個Context呢悬钳?其實(shí)根據(jù)上面的Context類型我們就已經(jīng)可以得出答案了。Context一共有Application偶翅、Activity和Service三種類型默勾,因此一個應(yīng)用程序中Context數(shù)量的計(jì)算公式就可以這樣寫:

[plain]view plaincopy

Context數(shù)量?=?Activity數(shù)量?+?Service數(shù)量?+?1

上面的1代表著Application的數(shù)量,因?yàn)橐粋€應(yīng)用程序中可以有多個Activity和多個Service聚谁,但是只能有一個Application母剥。

Application Context的設(shè)計(jì)

基本上每一個應(yīng)用程序都會有一個自己的Application,并讓它繼承自系統(tǒng)的Application類形导,然后在自己的Application類中去封裝一些通用的操作环疼。其實(shí)這并不是Google所推薦的一種做法,因?yàn)檫@樣我們只是把Application當(dāng)成了一個通用工具類來使用的朵耕,而實(shí)際上使用一個簡單的單例類也可以實(shí)現(xiàn)同樣的功能炫隶。但是根據(jù)我的觀察,有太多的項(xiàng)目都是這樣使用Application的阎曹。當(dāng)然這種做法也并沒有什么副作用伪阶,只是說明還是有不少人對于Application理解的還有些欠缺煞檩。那么這里我們先來對Application的設(shè)計(jì)進(jìn)行分析,講一些大家所不知道的細(xì)節(jié)栅贴,然后再看一下平時使用Application的問題斟湃。

首先新建一個MyApplication并讓它繼承自Application,然后在AndroidManifest.xml文件中對MyApplication進(jìn)行指定檐薯,如下所示:

[html]view plaincopy

android:name=".MyApplication"

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme">

......

指定完成后凝赛,當(dāng)我們的程序啟動時Android系統(tǒng)就會創(chuàng)建一個MyApplication的實(shí)例,如果這里不指定的話就會默認(rèn)創(chuàng)建一個Application的實(shí)例坛缕。

前面提到過墓猎,現(xiàn)在很多的Application都是被當(dāng)作通用工具類來使用的,那么既然作為一個通用工具類祷膳,我們要怎樣才能獲取到它的實(shí)例呢陶衅?如下所示:

[java]view plaincopy

publicclassMainActivityextendsActivity?{

@Override

protectedvoidonCreate(Bundle?savedInstanceState)?{

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

MyApplication?myApp?=?(MyApplication)?getApplication();

Log.d("TAG","getApplication?is?"+?myApp);

}

}

可以看到,代碼很簡單直晨,只需要調(diào)用getApplication()方法就能拿到我們自定義的Application的實(shí)例了搀军,打印結(jié)果如下所示:

那么除了getApplication()方法,其實(shí)還有一個getApplicationContext()方法勇皇,這兩個方法看上去好像有點(diǎn)關(guān)聯(lián)罩句,那么它們的區(qū)別是什么呢?我們將代碼修改一下:

[java]view plaincopy

publicclassMainActivityextendsActivity?{

@Override

protectedvoidonCreate(Bundle?savedInstanceState)?{

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

MyApplication?myApp?=?(MyApplication)?getApplication();

Log.d("TAG","getApplication?is?"+?myApp);

Context?appContext?=?getApplicationContext();

Log.d("TAG","getApplicationContext?is?"+?appContext);

}

}

同樣敛摘,我們把getApplicationContext()的結(jié)果打印了出來门烂,現(xiàn)在重新運(yùn)行代碼,結(jié)果如下圖所示:

咦兄淫?好像打印出的結(jié)果是一樣的呀屯远,連后面的內(nèi)存地址都是相同的,看來它們是同一個對象捕虽。其實(shí)這個結(jié)果也很好理解慨丐,因?yàn)榍懊嬉呀?jīng)說過了,Application本身就是一個Context泄私,所以這里獲取getApplicationContext()得到的結(jié)果就是MyApplication本身的實(shí)例房揭。

那么有的朋友可能就會問了,既然這兩個方法得到的結(jié)果都是相同的晌端,那么Android為什么要提供兩個功能重復(fù)的方法呢捅暴?實(shí)際上這兩個方法在作用域上有比較大的區(qū)別。getApplication()方法的語義性非常強(qiáng)咧纠,一看就知道是用來獲取Application實(shí)例的蓬痒,但是這個方法只有在Activity和Service中才能調(diào)用的到。那么也許在絕大多數(shù)情況下我們都是在Activity或者Service中使用Application的漆羔,但是如果在一些其它的場景梧奢,比如BroadcastReceiver中也想獲得Application的實(shí)例瞪讼,這時就可以借助getApplicationContext()方法了,如下所示:

[java]view plaincopy

publicclassMyReceiverextendsBroadcastReceiver?{

@Override

publicvoidonReceive(Context?context,?Intent?intent)?{

MyApplication?myApp?=?(MyApplication)?context.getApplicationContext();

Log.d("TAG","myApp?is?"+?myApp);

}

}

也就是說粹断,getApplicationContext()方法的作用域會更廣一些,任何一個Context的實(shí)例嫡霞,只要調(diào)用getApplicationContext()方法都可以拿到我們的Application對象瓶埋。

那么更加細(xì)心的朋友會發(fā)現(xiàn),除了這兩個方法之外诊沪,其實(shí)還有一個getBaseContext()方法养筒,這個baseContext又是什么東西呢?我們還是通過打印的方式來驗(yàn)證一下:

哦端姚?這次得到的是不同的對象了晕粪,getBaseContext()方法得到的是一個ContextImpl對象。這個ContextImpl是不是感覺有點(diǎn)似曾相識渐裸?回去看一下Context的繼承結(jié)構(gòu)圖吧巫湘,ContextImpl正是上下文功能的實(shí)現(xiàn)類。也就是說像Application昏鹃、Activity這樣的類其實(shí)并不會去具體實(shí)現(xiàn)Context的功能尚氛,而僅僅是做了一層接口封裝而已,Context的具體功能都是由ContextImpl類去完成的洞渤。那么這樣的設(shè)計(jì)到底是怎么實(shí)現(xiàn)的呢阅嘶?我們還是來看一下源碼吧。因?yàn)锳pplication载迄、Activity讯柔、Service都是直接或間接繼承自ContextWrapper的,我們就直接看ContextWrapper的源碼护昧,如下所示:

[java]view plaincopy

/**

*?Proxying?implementation?of?Context?that?simply?delegates?all?of?its?calls?to

*?another?Context.??Can?be?subclassed?to?modify?behavior?without?changing

*?the?original?Context.

*/

publicclassContextWrapperextendsContext?{

Context?mBase;

/**

*?Set?the?base?context?for?this?ContextWrapper.??All?calls?will?then?be

*?delegated?to?the?base?context.??Throws

*?IllegalStateException?if?a?base?context?has?already?been?set.

*

*?@param?base?The?new?base?context?for?this?wrapper.

*/

protectedvoidattachBaseContext(Context?base)?{

if(mBase?!=null)?{

thrownewIllegalStateException("Base?context?already?set");

}

mBase?=?base;

}

/**

*?@return?the?base?context?as?set?by?the?constructor?or?setBaseContext

*/

publicContext?getBaseContext()?{

returnmBase;

}

@Override

publicAssetManager?getAssets()?{

returnmBase.getAssets();

}

@Override

publicResources?getResources()?{

returnmBase.getResources();

}

@Override

publicContentResolver?getContentResolver()?{

returnmBase.getContentResolver();

}

@Override

publicLooper?getMainLooper()?{

returnmBase.getMainLooper();

}

@Override

publicContext?getApplicationContext()?{

returnmBase.getApplicationContext();

}

@Override

publicString?getPackageName()?{

returnmBase.getPackageName();

}

@Override

publicvoidstartActivity(Intent?intent)?{

mBase.startActivity(intent);

}

@Override

publicvoidsendBroadcast(Intent?intent)?{

mBase.sendBroadcast(intent);

}

@Override

publicIntent?registerReceiver(

BroadcastReceiver?receiver,?IntentFilter?filter)?{

returnmBase.registerReceiver(receiver,?filter);

}

@Override

publicvoidunregisterReceiver(BroadcastReceiver?receiver)?{

mBase.unregisterReceiver(receiver);

}

@Override

publicComponentName?startService(Intent?service)?{

returnmBase.startService(service);

}

@Override

publicbooleanstopService(Intent?name)?{

returnmBase.stopService(name);

}

@Override

publicbooleanbindService(Intent?service,?ServiceConnection?conn,

intflags)?{

returnmBase.bindService(service,?conn,?flags);

}

@Override

publicvoidunbindService(ServiceConnection?conn)?{

mBase.unbindService(conn);

}

@Override

publicObject?getSystemService(String?name)?{

returnmBase.getSystemService(name);

}

......

}

由于ContextWrapper中的方法還是非常多的魂迄,我就進(jìn)行了一些篩選,只貼出來了部分方法捏卓。那么上面的這些方法相信大家都是非常熟悉的极祸,getResources()、getPackageName()怠晴、getSystemService()等等都是我們經(jīng)常要用到的方法遥金。那么所有這些方法的實(shí)現(xiàn)又是什么樣的呢?其實(shí)所有ContextWrapper中方法的實(shí)現(xiàn)都非常統(tǒng)一蒜田,就是調(diào)用了mBase對象中對應(yīng)當(dāng)前方法名的方法稿械。

那么這個mBase對象又是什么呢?我們來看第16行的attachBaseContext()方法冲粤,這個方法中傳入了一個base參數(shù)美莫,并把這個參數(shù)賦值給了mBase對象页眯。而attachBaseContext()方法其實(shí)是由系統(tǒng)來調(diào)用的,它會把ContextImpl對象作為參數(shù)傳遞到attachBaseContext()方法當(dāng)中厢呵,從而賦值給mBase對象窝撵,之后ContextWrapper中的所有方法其實(shí)都是通過這種委托的機(jī)制交由ContextImpl去具體實(shí)現(xiàn)的,所以說ContextImpl是上下文功能的實(shí)現(xiàn)類是非常準(zhǔn)確的襟铭。

那么另外再看一下我們剛剛打印的getBaseContext()方法碌奉,在第26行。這個方法只有一行代碼寒砖,就是返回了mBase對象而已赐劣,而mBase對象其實(shí)就是ContextImpl對象,因此剛才的打印結(jié)果也得到了印證哩都。

使用Application的問題

雖說Application的用法確實(shí)非常簡單魁兼,但是我們平時的開發(fā)工作當(dāng)中也著實(shí)存在著不少Application誤用的場景,那么今天就來看一看有哪些比較容易犯錯的地方是我們應(yīng)該注意的漠嵌。

Application是Context的其中一種類型咐汞,那么是否就意味著,只要是Application的實(shí)例儒鹿,就能隨時使用Context的各種方法呢碉考?我們來做個實(shí)驗(yàn)試一下就知道了:

[java]view plaincopy

publicclassMyApplicationextendsApplication?{

publicMyApplication()?{

String?packageName?=?getPackageName();

Log.d("TAG","package?name?is?"+?packageName);

}

}

這是一個非常簡單的自定義Application,我們在MyApplication的構(gòu)造方法當(dāng)中獲取了當(dāng)前應(yīng)用程序的包名挺身,并打印出來侯谁。獲取包名使用了getPackageName()方法,這個方法就是由Context提供的章钾。那么上面的代碼能正常運(yùn)行嗎墙贱?跑一下就知道了,你將會看到如下所示的結(jié)果:

應(yīng)用程序一啟動就立刻崩潰了贱傀,報(bào)的是一個空指針異常惨撇。看起來好像挺簡單的一段代碼府寒,怎么就會成空指針了呢魁衙?但是如果你嘗試把代碼改成下面的寫法,就會發(fā)現(xiàn)一切正常了:

[java]view plaincopy

publicclassMyApplicationextendsApplication?{

@Override

publicvoidonCreate()?{

super.onCreate();

String?packageName?=?getPackageName();

Log.d("TAG","package?name?is?"+?packageName);

}

}

運(yùn)行結(jié)果如下所示:

在構(gòu)造方法中調(diào)用Context的方法就會崩潰株搔,在onCreate()方法中調(diào)用Context的方法就一切正常剖淀,那么這兩個方法之間到底發(fā)生了什么事情呢?我們重新回顧一下ContextWrapper類的源碼纤房,ContextWrapper中有一個attachBaseContext()方法纵隔,這個方法會將傳入的一個Context參數(shù)賦值給mBase對象,之后mBase對象就有值了。而我們又知道捌刮,所有Context的方法都是調(diào)用這個mBase對象的同名方法碰煌,那么也就是說如果在mBase對象還沒賦值的情況下就去調(diào)用Context中的任何一個方法時,就會出現(xiàn)空指針異常绅作,上面的代碼就是這種情況芦圾。Application中方法的執(zhí)行順序如下圖所示:

Application中在onCreate()方法里去初始化各種全局的變量數(shù)據(jù)是一種比較推薦的做法,但是如果你想把初始化的時間點(diǎn)提前到極致俄认,也可以去重寫attachBaseContext()方法堕扶,如下所示:

[java]view plaincopy

publicclassMyApplicationextendsApplication?{

@Override

protectedvoidattachBaseContext(Context?base)?{

//?在這里調(diào)用Context的方法會崩潰

super.attachBaseContext(base);

//?在這里可以正常調(diào)用Context的方法

}

}

以上是我們平時在使用Application時需要注意的一個點(diǎn),下面再來介紹另外一種非常普遍的Application誤用情況梭依。

其實(shí)Android官方并不太推薦我們使用自定義的Application,基本上只有需要做一些全局初始化的時候可能才需要用到自定義Application典尾,官方文檔描述如下:

但是就我的觀察而言役拴,現(xiàn)在自定義Application的使用情況基本上可以達(dá)到100%了,也就是我們平時自己寫測試demo的時候可能不會使用钾埂,正式的項(xiàng)目幾乎全部都會使用自定義Application河闰。可是使用歸使用褥紫,有不少項(xiàng)目對自定義Application的用法并不到位姜性,正如官方文檔中所表述的一樣,多數(shù)項(xiàng)目只是把自定義Application當(dāng)成了一個通用工具類髓考,而這個功能并不需要借助Application來實(shí)現(xiàn)部念,使用單例可能是一種更加標(biāo)準(zhǔn)的方式。

不過自定義Application也并沒有什么副作用氨菇,它和單例模式二選一都可以實(shí)現(xiàn)同樣的功能儡炼,但是我見過有一些項(xiàng)目,會把自定義Application和單例模式混合到一起使用查蓉,這就讓人大跌眼鏡了乌询。一個非常典型的例子如下所示:

[java]view plaincopy

publicclassMyApplicationextendsApplication?{

privatestaticMyApplication?app;

publicstaticMyApplication?getInstance()?{

if(app?==null)?{

app?=newMyApplication();

}

returnapp;

}

}

就像單例模式一樣,這里提供了一個getInstance()方法豌研,用于獲取MyApplication的實(shí)例妹田,有了這個實(shí)例之后,就可以調(diào)用MyApplication中的各種工具方法了鹃共。

但是這種寫法對嗎鬼佣?這種寫法是大錯特錯!因?yàn)槲覀冎繟pplication是屬于系統(tǒng)組件霜浴,系統(tǒng)組件的實(shí)例是要由系統(tǒng)來去創(chuàng)建的沮趣,如果這里我們自己去new一個MyApplication的實(shí)例,它就只是一個普通的Java對象而已坷随,而不具備任何Context的能力房铭。有很多人向我反饋使用LitePal時發(fā)生了空指針錯誤其實(shí)都是由于這個原因驻龟,因?yàn)槟闾峁┙oLitePal的只是一個普通的Java對象,它無法通過這個對象來進(jìn)行Context操作缸匪。

那么如果真的想要提供一個獲取MyApplication實(shí)例的方法翁狐,比較標(biāo)準(zhǔn)的寫法又是什么樣的呢?其實(shí)這里我們只需謹(jǐn)記一點(diǎn)凌蔬,Application全局只有一個露懒,它本身就已經(jīng)是單例了,無需再用單例模式去為它做多重實(shí)例保護(hù)了砂心,代碼如下所示:

[java]view plaincopy

publicclassMyApplicationextendsApplication?{

privatestaticMyApplication?app;

publicstaticMyApplication?getInstance()?{

returnapp;

}

@Override

publicvoidonCreate()?{

super.onCreate();

app?=this;

}

}

getInstance()方法可以照常提供懈词,但是里面不要做任何邏輯判斷,直接返回app對象就可以了辩诞,而app對象又是什么呢坎弯?在onCreate()方法中我們將app對象賦值成this,this就是當(dāng)前Application的實(shí)例译暂,那么app也就是當(dāng)前Application的實(shí)例了抠忘。

好了,關(guān)于Context的介紹就到這里吧外永,內(nèi)容還是比較簡單易懂的崎脉,希望大家通過這篇文章可以理解Context更多的細(xì)節(jié),并且不要去犯使用Context時的一些低級錯誤伯顶。

喜歡的點(diǎn)個贊吧囚灼。


轉(zhuǎn)載請注明出處:http://blog.csdn.net/guolin_blog/article/details/47028975

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市祭衩,隨后出現(xiàn)的幾起案子啦撮,更是在濱河造成了極大的恐慌,老刑警劉巖汪厨,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赃春,死亡現(xiàn)場離奇詭異,居然都是意外死亡劫乱,警方通過查閱死者的電腦和手機(jī)织中,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衷戈,“玉大人狭吼,你說我怎么就攤上這事≈掣荆” “怎么了刁笙?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我疲吸,道長座每,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任摘悴,我火速辦了婚禮峭梳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蹂喻。我一直安慰自己葱椭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布口四。 她就那樣靜靜地躺著孵运,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蔓彩。 梳的紋絲不亂的頭發(fā)上治笨,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機(jī)與錄音粪小,去河邊找鬼。 笑死抡句,一個胖子當(dāng)著我的面吹牛探膊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播待榔,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼逞壁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了锐锣?” 一聲冷哼從身側(cè)響起腌闯,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎雕憔,沒想到半個月后姿骏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡斤彼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年分瘦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片琉苇。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡嘲玫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出并扇,到底是詐尸還是另有隱情去团,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站土陪,受9級特大地震影響昼汗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜旺坠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一乔遮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧取刃,春花似錦蹋肮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至崩侠,卻和暖如春漆魔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背却音。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工改抡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人系瓢。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓阿纤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親夷陋。 傳聞我的和親對象是個殘疾皇子欠拾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

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