如何實(shí)現(xiàn) Flutter 與原生的混合開(kāi)發(fā)方式?詳解Flutter混合開(kāi)發(fā)的路由棧管理(深度好文)

為了把 Flutter 引入到原生工程炒辉,我們需要把 Flutter 工程改造為原生工程的一個(gè)組件依賴豪墅,并以組件化的方式管理不同平臺(tái)的 Flutter 構(gòu)建產(chǎn)物,即 Android 平臺(tái)使用 aar黔寇、iOS 平臺(tái)使用 pod 進(jìn)行依賴管理偶器。這樣,我們就可以在 Android 工程中通過(guò) FlutterView缝裤,iOS 工程中通過(guò) FlutterViewController屏轰,為 Flutter 搭建應(yīng)用入口,實(shí)現(xiàn) Flutter 與原生的混合開(kāi)發(fā)方式倘是。

對(duì)于混合開(kāi)發(fā)的應(yīng)用而言亭枷,通常我們只會(huì)將應(yīng)用的部分模塊修改成 Flutter 開(kāi)發(fā),其他模塊繼續(xù)保留原生開(kāi)發(fā)搀崭,因此應(yīng)用內(nèi)除了 Flutter 的頁(yè)面之外叨粘,還會(huì)有原生 Android、iOS 的頁(yè)面瘤睹。在這種情況下升敲,F(xiàn)lutter 頁(yè)面有可能會(huì)需要跳轉(zhuǎn)到原生頁(yè)面,而原生頁(yè)面也可能會(huì)需要跳轉(zhuǎn)到 Flutter 頁(yè)面轰传。這就涉及到了一個(gè)新的問(wèn)題:如何統(tǒng)一管理原生頁(yè)面和 Flutter 頁(yè)面跳轉(zhuǎn)交互的混合導(dǎo)航棧驴党。

混合導(dǎo)航棧

混合導(dǎo)航棧,指的是在混合開(kāi)發(fā)中原生頁(yè)面和Flutter頁(yè)面相互摻雜获茬,存在于用戶視角的頁(yè)面導(dǎo)航棧視圖港庄,如圖11-12所示。在混合開(kāi)發(fā)的應(yīng)用中恕曲,原生Android鹏氧、iOS與Flutter各自實(shí)現(xiàn)了一套互不相同的頁(yè)面映射機(jī)制,原生平臺(tái)采用的是單容器單頁(yè)面佩谣,即一個(gè)ViewController或Activity對(duì)應(yīng)一個(gè)原生頁(yè)面把还;而Flutter采用單容器多頁(yè)面的機(jī)制,即一個(gè)ViewController或Activity對(duì)應(yīng)多個(gè)Flutter頁(yè)面茸俭。Flutter在原生的導(dǎo)航棧之上又自建了一套Flutter導(dǎo)航棧吊履,這使得原生頁(yè)面與Flutter頁(yè)面與之間進(jìn)行頁(yè)面切換時(shí),需要處理跨引擎的頁(yè)面切換問(wèn)題调鬓。

image

接下來(lái)艇炎,我們就分別從原生頁(yè)面跳轉(zhuǎn)至 Flutter 頁(yè)面,以及從 Flutter 頁(yè)面跳轉(zhuǎn)至原生頁(yè)面來(lái)看看混合開(kāi)發(fā)的路由管理腾窝。

原生頁(yè)面跳轉(zhuǎn)Flutter頁(yè)面

從原生頁(yè)面跳轉(zhuǎn)至 Flutter 頁(yè)面冕臭,實(shí)現(xiàn)起來(lái)比較簡(jiǎn)單腺晾。因?yàn)?Flutter 本身依托于原生提供的容器,即iOS 使用的是FlutterViewController辜贵,Android 使用的是Activity 中的 FlutterView悯蝉。所以我們通過(guò)初始化 Flutter 容器,為其設(shè)置初始路由頁(yè)面之后托慨,就可以以原生的方式跳轉(zhuǎn)至 Flutter 頁(yè)面了鼻由。

對(duì)于iOS混合工程來(lái)說(shuō),可以先初始化一個(gè)FlutterViewController實(shí)例厚棵,然后設(shè)置初始化頁(yè)面路由蕉世,將其加入原生的視圖導(dǎo)航棧中即可完成跳轉(zhuǎn),如下所示婆硬。

//iOS 跳轉(zhuǎn)至Flutter頁(yè)面
FlutterViewController *vc = [[FlutterViewController alloc] init];
//設(shè)置Flutter初始化路由頁(yè)面
[vc setInitialRoute:@"defaultPage"];
//完成頁(yè)面跳轉(zhuǎn)
[self.navigationController pushViewController:vc animated:YES];

對(duì)于Android混合工程而言狠轻,則需要多加一步。因?yàn)镕lutter頁(yè)面的入口并不是原生視圖導(dǎo)航棧的最小單位Activity彬犯,而是一個(gè)FlutterView向楼,所以我們需要把這個(gè)View包裝到Activity的contentView中,然后才能實(shí)現(xiàn)跳轉(zhuǎn)谐区。在Activity內(nèi)部設(shè)置頁(yè)面初始化路由之后湖蜕,在外部就可以采用打開(kāi)一個(gè)普通的原生視圖的方式來(lái)打開(kāi)Flutter頁(yè)面了,如下所示宋列。

//Android 跳轉(zhuǎn)至Flutter頁(yè)面
//創(chuàng)建一個(gè)作為Flutter頁(yè)面容器的Activity
public class FlutterHomeActivity extends AppCompatActivity {
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //設(shè)置Flutter初始化路由頁(yè)面昭抒,傳入路由標(biāo)識(shí)符
    View FlutterView = Flutter.createView(this, getLifecycle(), "defaultRoute"); 
    //用FlutterView替代Activity的ContentView
    setContentView(FlutterView);
  }
}
//用FlutterPageActivity完成頁(yè)面跳轉(zhuǎn)
Intent intent = new Intent(MainActivity.this, FlutterHomeActivity.class);
startActivity(intent);

運(yùn)行項(xiàng)目代碼,最終的效果下圖所示炼杖。

image

對(duì)于Android混合工程來(lái)說(shuō)灭返,F(xiàn)lutter的原生容器就是一個(gè)Activity,只需要?jiǎng)?chuàng)建一個(gè)FlutterView坤邪,然后利用addContentView()方法將當(dāng)前頁(yè)面的layout頁(yè)面布局添加進(jìn)去即可熙含。如果Flutter的原生容器是一個(gè)Fragment,那么只需要?jiǎng)?chuàng)建一個(gè)FlutterFragment罩扇,然后在指定的容器中添加Flutter頁(yè)面即可婆芦。同樣怕磨,對(duì)于iOS混合工程來(lái)說(shuō)喂饥,F(xiàn)lutter的原生容器是一個(gè)FlutterViewController。

Flutter 頁(yè)面跳轉(zhuǎn)至原生頁(yè)面

相比原生頁(yè)面跳轉(zhuǎn)Flutter頁(yè)面肠鲫,從Flutter頁(yè)面跳轉(zhuǎn)至原生頁(yè)面則會(huì)相對(duì)麻煩些员帮。因?yàn)槲覀冃枰紤]以下兩種場(chǎng)景,即從Flutter頁(yè)面打開(kāi)新的原生頁(yè)面和從Flutter頁(yè)面回退到舊的原生頁(yè)面导饲。
由于Flutter并沒(méi)有提供對(duì)原生頁(yè)面的操作方法捞高,所以不能通過(guò)直接調(diào)用原生平臺(tái)的方法來(lái)實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)氯材,不過(guò)可以使用Flutter提供的方法通道來(lái)間接實(shí)現(xiàn),即打開(kāi)原生頁(yè)面使用的是openNativePage()方法硝岗,需要關(guān)閉Flutter頁(yè)面時(shí)則調(diào)用closeFlutterPage()方法氢哮。
具體來(lái)說(shuō),在Flutter和原生兩端各自初始化方法通道型檀,并提供Flutter操作原生頁(yè)面的方法冗尤,并在原生代碼中注冊(cè)方法通道,當(dāng)原生端收到Flutter的方法調(diào)用時(shí)就可以打開(kāi)新的原生頁(yè)面胀溺。
在混合開(kāi)發(fā)的應(yīng)用中裂七,F(xiàn)lutterView與FlutterViewController是Flutter模塊的入口,也是Flutter模塊初始化的地方仓坞”沉悖可以看到,在混合開(kāi)發(fā)的應(yīng)用中接入Flutter與開(kāi)發(fā)一個(gè)純Flutter應(yīng)用在運(yùn)行機(jī)制上并無(wú)任何區(qū)別无埃,因?yàn)閷?duì)于混合工程來(lái)說(shuō)徙瓶,原生工程只不過(guò)是為Flutter提供了一個(gè)容器而已,即Android使用的是FlutterView录语,iOS使用的是FlutterViewController倍啥。接下來(lái),F(xiàn)lutter模塊就可以使用自己的導(dǎo)航棧來(lái)管理Flutter頁(yè)面澎埠,并且可以實(shí)現(xiàn)多個(gè)復(fù)雜頁(yè)面的渲染和切換虽缕。
因?yàn)镕lutter容器本身屬于原生導(dǎo)航棧的一部分,所以當(dāng)Flutter容器內(nèi)的根頁(yè)面需要返回時(shí)蒲稳,開(kāi)發(fā)者需要處理Flutter容器的關(guān)閉問(wèn)題氮趋,從而實(shí)現(xiàn)Flutter根頁(yè)面的關(guān)閉。由于Flutter并沒(méi)有提供操作Flutter容器的方法江耀,因此我們依然需要通過(guò)方法通道剩胁,在原生代碼宿主為Flutter提供操作Flutter容器的方法,在頁(yè)面返回時(shí)關(guān)閉Flutter頁(yè)面祥国。如圖下圖所示昵观,是Flutter跳轉(zhuǎn)原生頁(yè)面的兩種場(chǎng)景的示意圖。

image

使用方法通道實(shí)現(xiàn)Flutter頁(yè)面至原生頁(yè)面的跳轉(zhuǎn)舌稀,注冊(cè)方法通道最合適的地方是Flutter應(yīng)用的入口啊犬,即在iOS端的FlutterViewController和Android端的是FlutterView初始化Flutter頁(yè)面之前。因此壁查,在混合開(kāi)發(fā)的應(yīng)用中觉至,需要分別繼承iOS的FlutterViewController和Android的AppCompatActivity,然后在iOS的viewDidLoad和Android的onCreate生命周期函數(shù)中初始化Flutter容器時(shí)睡腿,注冊(cè)openNativePage和closeFlutterPage兩個(gè)方法语御。
下面是使用方法通道實(shí)現(xiàn)Flutter跳轉(zhuǎn)原生頁(yè)面的原生Android端的代碼峻贮,如下所示。

public class FlutterModuleActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//初始化Flutter容器
        FlutterView fv = Flutter.createView(this, getLifecycle(), "defaultPage");  
        //注冊(cè)方法通道  
        new MethodChannel(fv, "com.xzh/navigation").setMethodCallHandler(
            new MethodChannel.MethodCallHandler() {
               @Override
               public void onMethodCall(MethodCall call, Result result) {
               if (call.method.equals("openNativePage")) {
                 Intent intent = new Intent(this, AndroidNativeActivity.class);
                 tartActivity(intent);
                 result.success(0);
               } else if (call.method.equals("closeFlutterPage")) {
                 finish();
                 result.success(0);
           } else {
                result.notImplemented();
              }
           }
      });
    setContentView(fv);
   }
}

可以發(fā)現(xiàn)应闯,在上面的代碼中纤控,首先使用FlutterView初始化一個(gè)Flutter容器,然后在原生代碼中注冊(cè)openNativePage和closeFlutterPage兩個(gè)方法碉纺,當(dāng)Flutter頁(yè)面通過(guò)方法通道調(diào)用原生方法時(shí)即可打開(kāi)原生頁(yè)面嚼黔。
與原生Android端的實(shí)現(xiàn)原理類(lèi)似,使用方法通道實(shí)現(xiàn)頁(yè)面的跳轉(zhuǎn)頁(yè)需要在原生iOS端中注冊(cè)openNativePage和closeFlutterPage兩個(gè)方法惜辑,代碼如下唬涧。

@interface FlutterHomeViewController : FlutterViewController
@end

@implementation FlutterHomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
//聲明方法通道
    FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"com.xzh/navigation" binaryMessenger:self];
    [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
        if([call.method isEqualToString:@"openNativePage"]) {
//打開(kāi)一個(gè)新的原生頁(yè)面
            iOSNativeViewController *vc = [[iOSNativeViewController alloc] init];
            [self.navigationController pushViewController:vc animated:YES];
            result(@0);
        }else if([call.method isEqualToString:@"closeFlutterPage"]) {
            //關(guān)閉Flutter頁(yè)面
            [self.navigationController popViewControllerAnimated:YES];
            result(@0);
        }else {
            result(FlutterMethodNotImplemented);
        }
    }];
}
@end

經(jīng)過(guò)上面的方法注冊(cè)后,接下來(lái)就可以在Flutter中使用openNativePage()方法來(lái)打開(kāi)原生頁(yè)面了盛撑,如下所示碎节。

void main() => runApp(_widgetForRoute(window.defaultRouteName));
//獲取方法通道
const platform = MethodChannel('com.xzh/navigation'); 

//根據(jù)路由標(biāo)識(shí)符返回應(yīng)用入口視圖
Widget _widgetForRoute(String route) {
  switch (route) {
    default://返回默認(rèn)視圖
      return MaterialApp(home:DefaultPage());
  }
}

class PageA extends StatelessWidget {
  ...
  @override
  Widget build(BuildContext context) {
    return Scaffold(
            body: RaisedButton(
                    child: Text("Go PageB"),
                    onPressed: ()=>platform.invokeMethod('openNativePage')//打開(kāi)原生頁(yè)面
            ));
  }
}

class DefaultPage extends StatelessWidget {
  ...
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
            title: Text("DefaultPage Page"),
            leading: IconButton(icon:Icon(Icons.arrow_back), onPressed:() => platform.invokeMethod('closeFlutterPage')//關(guān)閉Flutter頁(yè)面
        )),
        body: RaisedButton(
                  child: Text("Go PageA"),
                  onPressed: ()=>Navigator.push(context, MaterialPageRoute(builder: (context) => PageA())),//打開(kāi)Flutter頁(yè)面 PageA
        ));
  }
}

在上面的例子中,F(xiàn)lutter 容器的根視圖 DefaultPage 包含有兩個(gè)按鈕抵卫。點(diǎn)擊左上角的按鈕后狮荔,可以通過(guò) closeFlutterPage 返回原生頁(yè)面;點(diǎn)擊中間的按鈕后介粘,會(huì)打開(kāi)一個(gè)新的 Flutter 頁(yè)面 PageA殖氏。PageA 中也有一個(gè)按鈕,點(diǎn)擊這個(gè)按鈕之后會(huì)調(diào)用 openNativePage 來(lái)打開(kāi)一個(gè)新的原生頁(yè)面姻采。
整個(gè)混合導(dǎo)航棧示例的代碼流程雅采,如下圖所示。通過(guò)這張圖慨亲,你就可以把這個(gè)示例的整個(gè)代碼流程串起來(lái)了婚瓜。

image

在混合應(yīng)用工程中,RootViewController 與 MainActivity 分別是 iOS 和 Android 應(yīng)用的原生頁(yè)面入口刑棵,可以初始化為 Flutter 容器的 FlutterHomeViewController(iOS 端)與 FlutterHomeActivity(Android 端)巴刻。

在為其設(shè)置初始路由頁(yè)面 DefaultPage 之后,就可以以原生的方式跳轉(zhuǎn)至 Flutter 頁(yè)面蛉签。但是胡陪,F(xiàn)lutter 并未提供接口,來(lái)支持從 Flutter 的 DefaultPage 頁(yè)面返回到原生頁(yè)面碍舍,因此我們需要利用方法通道來(lái)注冊(cè)關(guān)閉 Flutter 容器的方法柠座,即 closeFlutterPage,讓 Flutter 容器接收到這個(gè)方法調(diào)用時(shí)關(guān)閉自身乒验。

在 Flutter 容器內(nèi)部愚隧,我們可以使用 Flutter 內(nèi)部的頁(yè)面路由機(jī)制蒂阱,通過(guò) Navigator.push 方法锻全,完成從 DefaultPage 到 PageA 的頁(yè)面跳轉(zhuǎn)狂塘;而當(dāng)我們想從 Flutter 的 PageA 頁(yè)面跳轉(zhuǎn)到原生頁(yè)面時(shí),因?yàn)樯婕暗娇缫娴捻?yè)面路由鳄厌,所以我們?nèi)匀恍枰梅椒ㄍǖ纴?lái)注冊(cè)打開(kāi)原生頁(yè)面的方法荞胡,即 openNativePage,讓 Flutter 容器接收到這個(gè)方法調(diào)用時(shí)了嚎,在原生代碼宿主完成原生頁(yè)面 SomeOtherNativeViewController(iOS 端)與 SomeNativePageActivity(Android 端)的初始化泪漂,并最終完成頁(yè)面跳轉(zhuǎn)。

總結(jié)

對(duì)于原生 Android歪泳、iOS 工程混編 Flutter 開(kāi)發(fā)萝勤,由于應(yīng)用中會(huì)同時(shí)存在 Android、iOS 和 Flutter 頁(yè)面呐伞,所以我們需要妥善處理跨渲染引擎的頁(yè)面跳轉(zhuǎn)敌卓,解決原生頁(yè)面如何切換 Flutter 頁(yè)面,以及 Flutter 頁(yè)面如何切換到原生頁(yè)面的問(wèn)題伶氢。

在原生頁(yè)面切換到 Flutter 頁(yè)面時(shí)趟径,我們通常會(huì)將 Flutter 容器封裝成一個(gè)獨(dú)立的 ViewController(iOS 端)或 Activity(Android 端),在為其設(shè)置好 Flutter 容器的頁(yè)面初始化路由(即根視圖)后癣防,原生的代碼就可以按照打開(kāi)一個(gè)普通的原生頁(yè)面的方式來(lái)打開(kāi) Flutter 頁(yè)面了蜗巧。

而如果我們想在 Flutter 頁(yè)面跳轉(zhuǎn)到原生頁(yè)面,則需要同時(shí)處理好打開(kāi)新的原生頁(yè)面蕾盯,以及關(guān)閉自身回退到老的原生頁(yè)面兩種場(chǎng)景幕屹。在這兩種場(chǎng)景下,我們都需要利用方法通道來(lái)注冊(cè)相應(yīng)的處理方法级遭,從而在原生代碼宿主實(shí)現(xiàn)新頁(yè)面的打開(kāi)和 Flutter 容器的關(guān)閉香嗓。

需要注意的是,與純 Flutter 應(yīng)用不同装畅,原生應(yīng)用混編 Flutter 由于涉及到原生頁(yè)面與 Flutter 頁(yè)面之間切換靠娱,因此導(dǎo)航棧內(nèi)可能會(huì)出現(xiàn)多個(gè) Flutter 容器的情況,即多個(gè) Flutter 實(shí)例掠兄。Flutter 實(shí)例的初始化成本非常高昂像云,每啟動(dòng)一個(gè) Flutter 實(shí)例,就會(huì)創(chuàng)建一套新的渲染機(jī)制蚂夕,即 Flutter Engine迅诬,以及底層的 Isolate。而這些實(shí)例之間的內(nèi)存是不互相共享的婿牍,會(huì)帶來(lái)較大的系統(tǒng)資源消耗侈贷。

為了解決混編工程中 Flutter 多實(shí)例的問(wèn)題,業(yè)界有兩種解決方案:

  • 以今日頭條為代表的修改 Flutter Engine 源碼等脂,使多 FlutterView 實(shí)例對(duì)應(yīng)的多 Flutter Engine 能夠在底層共享 Isolate俏蛮;
  • 以閑魚(yú)為代表的共享 FlutterView撑蚌,即由原生層驅(qū)動(dòng) Flutter 層渲染內(nèi)容的方案。

不過(guò)搏屑,目前這兩種解決方案都不夠完美争涌。所以,在 Flutter 官方支持多實(shí)例單引擎之前辣恋,應(yīng)該盡量使用Flutter去開(kāi)發(fā)一些閉環(huán)業(yè)務(wù)亮垫,減少原生頁(yè)面與Flutter頁(yè)面之間的交互,盡量避免Flutter頁(yè)面跳轉(zhuǎn)到原生頁(yè)面伟骨,原生頁(yè)面又啟動(dòng)一個(gè)新的Flutter實(shí)例的情況饮潦,并且保證應(yīng)用內(nèi)不要出現(xiàn)多個(gè) Flutter 容器實(shí)例的情況。

原文作者:xiangzhihong

原文鏈接:人類(lèi)身份驗(yàn)證 - SegmentFault

來(lái)源:思否

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末携狭,一起剝皮案震驚了整個(gè)濱河市害晦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌暑中,老刑警劉巖壹瘟,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鳄逾,居然都是意外死亡稻轨,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)雕凹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)殴俱,“玉大人,你說(shuō)我怎么就攤上這事枚抵∠哂” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵汽摹,是天一觀的道長(zhǎng)李丰。 經(jīng)常有香客問(wèn)我,道長(zhǎng)逼泣,這世上最難降的妖魔是什么趴泌? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮拉庶,結(jié)果婚禮上嗜憔,老公的妹妹穿的比我還像新娘。我一直安慰自己氏仗,他們只是感情好吉捶,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般呐舔。 火紅的嫁衣襯著肌膚如雪币励。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,837評(píng)論 1 290
  • 那天滋早,我揣著相機(jī)與錄音,去河邊找鬼砌们。 笑死杆麸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的浪感。 我是一名探鬼主播昔头,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼影兽!你這毒婦竟也來(lái)了揭斧?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤峻堰,失蹤者是張志新(化名)和其女友劉穎讹开,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體捐名,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡旦万,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了镶蹋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片成艘。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖贺归,靈堂內(nèi)的尸體忽然破棺而出淆两,到底是詐尸還是另有隱情,我是刑警寧澤拂酣,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布秋冰,位于F島的核電站,受9級(jí)特大地震影響婶熬,放射性物質(zhì)發(fā)生泄漏丹莲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一尸诽、第九天 我趴在偏房一處隱蔽的房頂上張望甥材。 院中可真熱鬧,春花似錦性含、人聲如沸洲赵。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)叠萍。三九已至芝发,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間苛谷,已是汗流浹背辅鲸。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腹殿,地道東北人独悴。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像锣尉,于是被迫代替她去往敵國(guó)和親刻炒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

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