Flutter移動端實(shí)戰(zhàn)手冊

該文章屬于劉小壯原創(chuàng),轉(zhuǎn)載請注明:劉小壯


iOS接入Flutter

在進(jìn)行iOSFlutter的混編時澈吨,iOSAndroid的接入方式略復(fù)雜废登,但也還好≌饣。現(xiàn)在市面上有不少接入Flutter的方案,但大多數(shù)都是千篇一律相互抄的杉辙,沒什么意義鼓寺。

進(jìn)行Flutter混編之前挑秉,有一些必要的文件法梯。

  1. xcode_backend.sh文件,在配置flutter環(huán)境的時候由Flutter工具包提供犀概。
  2. xcconfig環(huán)境變量文件立哑,在Flutter工程中自動生成,每個工程都不一樣姻灶。

xcconfig文件

xcconfigXcode的配置文件铛绰,Flutter在里面配置了一些基本信息和路徑,接入Flutter前需要先將xcconfig接入進(jìn)來产喉,否則一些路徑等信息將會出錯或找不到捂掰。

Flutterxcconfig包含三個文件,Debug.xcconfig镊叁、Release.xcconfigGenerated.xcconfig走触,需要將這些文件配置在下面的位置晦譬,并且按照不同環(huán)境配置不同的文件。

Project -> Info -> Development Target -> Configurations
系統(tǒng)設(shè)置

有些比較大的工程中已經(jīng)在Configurations中設(shè)置了xcconfig文件互广,由于每個Target的一種環(huán)境只能配置一個xcconfig文件敛腌,所以可以在已有的xcconfig文件中import引入Generated.xcconfig文件,并且不需要區(qū)分環(huán)境惫皱。

腳本文件

xcode_backend.sh腳本文件用來構(gòu)建和導(dǎo)出Flutter產(chǎn)物像樊,這是Flutter開發(fā)包為我們默認(rèn)提供的。需要在工程TargetBuild Phases加入一個Run Script文件旅敷,并將下面的腳本代碼粘貼進(jìn)去生棍。需要注意的是,不要忘記前面的/bin/sh操作媳谁,否則會導(dǎo)致權(quán)限錯誤涂滴。

/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed

xcode_backend.sh中有三個參數(shù)類型,build晴音、thin柔纵、embedthin沒有太大意義锤躁,其他兩個則負(fù)責(zé)構(gòu)建和導(dǎo)出搁料。

混合開發(fā)

隨后可以對Xcode工程進(jìn)行編譯,這時候肯定會報(bào)錯的。但是不要慌張郭计,報(bào)錯后我們在工程主目錄下會發(fā)現(xiàn)一個名為Flutter的文件夾霸琴,其中會包含兩個framework,這個文件夾就是Flutter的編譯產(chǎn)物拣宏,我們將這個文件夾整體拖入項(xiàng)目中即可沈贝。

這時候就可以在iOS工程中添加Flutter代碼了,下面是詳細(xì)步驟勋乾。

  1. AppDelegate的集成改為FlutterAppDelegate宋下,并且需要遵循FlutterAppLifeCycleProvider代理。
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>

@interface AppDelegate : FlutterAppDelegate <FlutterAppLifeCycleProvider>

@end
  1. 創(chuàng)建一個FlutterPluginAppLifeCycleDelegate的實(shí)例對象辑莫,這個對象負(fù)責(zé)管理Flutter的生命周期学歧,并從Platform側(cè)接收AppDelegate的事件。我直接將其聲明為一個屬性各吨,在AppDelegate中的各個方法中枝笨,調(diào)用其方法進(jìn)行中轉(zhuǎn)操作。
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [self.lifeCycleDelegate application:application willFinishLaunchingWithOptions:launchOptions];
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
    [self.lifeCycleDelegate applicationWillResignActive:application];
}

 - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
    [self.lifeCycleDelegate application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
    return YES;
}
  1. 隨后即可加入Flutter代碼揭蜒,加入的方式也很簡單横浑,直接實(shí)例化一個FlutterViewController控制器即可,也不需要傳其他參數(shù)進(jìn)去(這里先不考慮多實(shí)例的問題)屉更。
FlutterViewController *flutterViewController = [[FlutterViewController alloc] init];

Flutter將其看做是一個畫布徙融,實(shí)例化一個畫布上去之后,任何操作其實(shí)都是在當(dāng)前頁面完成的瑰谜。

常見錯誤

到這個步驟集成操作就已經(jīng)完成欺冀,但是很多人在集成過程中會遇到一些錯誤,下面是一些常見錯誤萨脑。

  1. 路徑錯誤隐轩,讀取不到xcode_backend.sh文件等。這是因?yàn)榄h(huán)境變量FLUTTER_ROOT沒有獲取到渤早,FLUTTER_ROOT配置在Generated.xcconfig中职车,可以看一下這個文件是不是配置的有問題。
  2. lipo info *** arm64類似這樣的錯誤鹊杖,一般都是因?yàn)?code>xcode_backend.sh腳本導(dǎo)致的提鸟,可以檢查一下FLUTTER_ROOT環(huán)境變量是否正確。
  3. 下面這種問題一般都是因?yàn)闄?quán)限導(dǎo)致的仅淑,可以查看Build Phases的腳本寫的是不是有問題称勋。
***/flutter_tools/bin/xcode_backend.sh: Permission denied

混合開發(fā)

在進(jìn)行混編過程中,Flutter有一個很大的優(yōu)勢涯竟,就是如果Flutter代碼出問題赡鲜,不會導(dǎo)致原生應(yīng)用的崩潰空厌。當(dāng)Flutter代碼出現(xiàn)崩潰時,會在屏幕上顯示錯誤信息银酬。

在開發(fā)過程中經(jīng)常會涉及到網(wǎng)絡(luò)請求和持久化的問題嘲更,如果混編的話可能會涉及到寫兩套邏輯。例如網(wǎng)絡(luò)請求有一些公共參數(shù)揩瞪,或返回?cái)?shù)據(jù)的統(tǒng)一處理等赋朦,如果維護(hù)兩套邏輯的話會容易出問題。所以建議將網(wǎng)絡(luò)請求和持久化操作都交給Platform處理李破,Flutter側(cè)只負(fù)責(zé)向Platform請求并拿來使用即可宠哄。

這個過程就涉及到兩端數(shù)據(jù)交互的問題,Flutter對于混編給出了兩套方案嗤攻,MethodChannelEventChannel毛嫉。從名字上來看,一個是方法調(diào)用妇菱,另一個是事件傳遞承粤。但實(shí)際開發(fā)過程中,只需要使用MethodChannel即可完成所有需求闯团。

Flutter to Native

下面是Flutter調(diào)用Native的代碼辛臊,在Native中通過FlutterMethodChannel設(shè)置指定的回調(diào)代碼,并且在接收參數(shù)并處理房交。由Flutter通過MethodChannelNative發(fā)起調(diào)用彻舰,并傳入對應(yīng)的參數(shù)。

代碼中在Flutter側(cè)構(gòu)建好數(shù)據(jù)模型涌萤,然后調(diào)用MethodChannelinvokeMethod淹遵,會觸發(fā)Native的回調(diào)口猜。Native拿到Flutter傳過來的數(shù)據(jù)负溪,進(jìn)行解析并執(zhí)行播放操作,隨后會把播放的狀態(tài)碼回調(diào)給Flutter側(cè)济炎,交互完成川抡。

import 'package:flutter/services.dart';

Future<Null> playVideo() async{
  var methodChannel = MethodChannel('flutterChannelName');
  Map params = {'playID' : '302998298', 'duration' : '2520', 'name' : '三生三世十里桃花'};
  String result;
  result = await methodChannel.invokeMethod('PlayAlbumVideo', params);

  String playID   = params['playID'];
  String duration = params['duration'];
  String name     = params['name'];
  showCupertinoDialog(context: context, builder: (BuildContext context){
    return CupertinoAlertDialog(
      title: Text(result),
      content: Text('name:$name playID:$playID duration:$duration'),
      actions: <Widget>[
        FlatButton(
          child: Text('確定'),
          onPressed: (){
            Navigator.pop(context);
          },
        )
      ],
    );
  });
}
NSString *channelName = @"flutterChannelName";
FlutterMethodChannel *methodChannel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:flutterVC];
[methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
    if ([call.method isEqualToString:@"PlayAlbumVideo"]) {
        NSDictionary *params = call.arguments;
        
        VideoPlayerModel *model = [[VideoPlayerModel alloc] init];
        model.playID = [params stringForKey:@"playID"];
        model.duration = [params stringForKey:@"duration"];
        model.name = [params stringForKey:@"name"];
        NSString *playStatus = [SVHistoryPlayUtil playVideoWithModel:model 
                                                        showPlayerVC:self.flutterVC];
        
        result([NSString stringWithFormat:@"播放狀態(tài) %@", playStatus]);
    }
}];

Native to Flutter

Native調(diào)用Flutter的代碼和Flutter調(diào)用Native的基本類似,只是調(diào)用和設(shè)置回調(diào)的角色不同须尚。同樣的崖堤,Flutter由于要接收Native的消息回調(diào),所以需要注冊一個回調(diào)耐床,由Native發(fā)起對Flutter的調(diào)用并傳入?yún)?shù)密幔。

NativeFlutter的相互調(diào)用都需要設(shè)置一個名字,每一個名字對應(yīng)一個MethodChannel對象撩轰,每一個對象可以發(fā)起多次調(diào)用胯甩,不同調(diào)用以invokeMethod做區(qū)分昧廷。

import 'package:flutter/services.dart';

@override
void initState() {
    super.initState();
    
    MethodChannel methodChannel = MethodChannel('nativeChannelName');
    methodChannel.setMethodCallHandler(callbackHandler);
}

Future<dynamic> callbackHandler(MethodCall call) {
    if(call.method == 'requestHomeData') {
      String title = call.arguments['title'];
      String content = call.arguments['content'];
      showCupertinoDialog(context: context, builder: (BuildContext context){
        return CupertinoAlertDialog(
          title: Text(title),
          content: Text(content),
          actions: <Widget>[
            FlatButton(
              child: Text('確定'),
              onPressed: (){
                Navigator.pop(context);
              },
            )
          ],
        );
      });
    }
}
NSString *channelName = @"nativeChannelName";
FlutterMethodChannel *methodChannel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:flutterVC];
[RequestManager requestWithURL:url success:^(NSDictionary *result) {
    [methodChannel invokeMethod:@"requestHomeData" arguments:result];
}];

調(diào)試工具集

iOSAndroid開發(fā)中,各自的編譯器都提供了很好的調(diào)試工具集偎箫,方便進(jìn)行內(nèi)存木柬、性能、視圖等調(diào)試淹办。Flutter也提供了調(diào)試工具和命令眉枕,下面基于VSCode編譯器來講一下Flutter調(diào)試,相對而言Android Studio提供的調(diào)試功能可能會更多一些怜森。

性能調(diào)試

VSCode支持一些簡單的命令行調(diào)試指令速挑,在程序運(yùn)行過程中,在Command Palette命令行面板中輸入performance塔插,并選擇Toggle Performance Overlay命令即可梗摇。此命令有一個要求就是需要App在運(yùn)行狀態(tài)。

性能調(diào)試

隨后會在界面上出現(xiàn)一個性能面板想许,這個頁面分為兩部分伶授,GPU線程和UI線程的幀率。每個部分分為三個橫線流纹,代表著不同的卡頓層級糜烹。如果是綠色則表示不會影響界面渲染,如果是紅色則有可能會影響界面的流暢性漱凝。如果出現(xiàn)紅色線條疮蹦,則表示當(dāng)前執(zhí)行的代碼需要優(yōu)化。

Dart DevTools

VSCodeFlutter提供了一套調(diào)試工具集-Dart DevTools茸炒,這套工具集功能非常全愕乎,包含性能、UI壁公、熱更新感论、熱重載、log日志等很多功能紊册。

安裝Dart DevTools后比肄,在App運(yùn)行狀態(tài)下,可以在VSCode的右下角啟動這個工具囊陡,工具會以網(wǎng)頁的形式展現(xiàn)芳绩,并且可以控制App。

主界面

下面是Dart DevTools的主界面撞反,我運(yùn)行的是一個界面類似于微信的App妥色。從Inspector中可以看到頁面的視圖結(jié)構(gòu),Android Studio也有類似的功能遏片。頁面整體是一個樹形結(jié)構(gòu)嘹害,并且選中某一個控件后鳍侣,會在右側(cè)展示出控件的變量值,例如frame吼拥、color等倚聚,這個功能非常實(shí)用。

Dart DevTools

我運(yùn)行的設(shè)備是Xcode模擬器凿可,如果想切換AndroidMaterial Design惑折,點(diǎn)擊上面的iOS按鈕即可直接切換設(shè)備。剛才上面說到的查看內(nèi)存的性能面板枯跑,點(diǎn)擊iOS按鈕旁邊的Performance Overlay即可出現(xiàn)惨驶。

Select Widget

如果想知道在Dart DevTools中選擇的節(jié)點(diǎn),具體對應(yīng)哪個控件敛助,可以選擇Select Widget Mode使屏幕上被選中的控件高亮粗卜。

Select Widget Mode
Debug Paint

點(diǎn)擊Debug Paint可以讓每個控件都高亮,通過這個模式可以看到ListView的滑動方向纳击,以及每個控件的大小及控件之間的距離续扔。

Debug Paint

除此之外,還可以選擇Paint Baseline使所有控件的底線高亮焕数,功能和Debug Paint類似纱昧,不做敘述。

Memory

Dart DevTools中提供的內(nèi)存調(diào)試工具更加直觀堡赔,可以實(shí)時顯示內(nèi)存使用情況识脆。在剛開始運(yùn)行時,我們發(fā)現(xiàn)一個內(nèi)存峰值善已,把鼠標(biāo)放上去可以看到具體的內(nèi)存使用情況灼捂。內(nèi)存會有具體分類,Used换团、GC等悉稠。

Memory

Dart DevTools的內(nèi)存工具還是不夠完美,Xcode可以選擇某段內(nèi)存啥寇,看到這塊內(nèi)存中涉及到主要堆棧調(diào)用偎球,并且點(diǎn)擊調(diào)用椚髟可以跳轉(zhuǎn)到Xcode對應(yīng)的代碼中辑甜,而Dart DevTools還不具備這個功能,可能和Web的展示形式有關(guān)系袍冷。

內(nèi)存管理Flutter使用的是GC磷醋,回收速度可能不是很快,iOS中的ARC則是基于引用計(jì)數(shù)立即回收的胡诗。還有很多其他的功能邓线,這里就不一一詳細(xì)敘述了淌友,各位同學(xué)可以自己探索。

多實(shí)例

項(xiàng)目中是通過實(shí)例化FlutterViewController控制器來顯示Flutter界面的骇陈,整個Flutter頁面可以理解為一個畫布震庭,通過頁面不斷的變化,改變畫布上的東西你雌。所以器联,在單實(shí)例的情況下,Flutter頁面中間不能插入原生頁面婿崭。

這時候如果我們想在多個地方展示Flutter頁面拨拓,而這些頁面并不是Flutter -> Flutter的連貫跳轉(zhuǎn)形式,那怎么來實(shí)現(xiàn)這個場景呢氓栈?Google的建議是創(chuàng)建Flutter的多實(shí)例渣磷,并通過傳入不同的參數(shù)實(shí)例化不同的頁面。但這樣會造成很嚴(yán)重的內(nèi)存問題授瘦,所以并不能這么做醋界。

Router

如果不能真正創(chuàng)建多個實(shí)例對象,那就需要通過其他方式來實(shí)現(xiàn)多實(shí)例提完。Flutter頁面顯示其實(shí)并不是跟著FlutterVC走的物独,而是跟著FlutterEngine走的。所以在創(chuàng)建一次FlutterVC之后氯葬,就將FlutterEngine保存下來挡篓,在其他位置創(chuàng)建FlutterVC時直接通過FlutterEngine的方式創(chuàng)建,并且在創(chuàng)建后進(jìn)行跳轉(zhuǎn)操作帚称。

在進(jìn)行頁面切換時官研,通過channelMethod調(diào)用Flutter側(cè)的路由切換代碼,并將切換后的新頁面FlutterVC添加到Native上闯睹。這種實(shí)現(xiàn)方式戏羽,就是通過FlutterRouter的方式實(shí)現(xiàn)的,下面將會介紹Router的兩種表現(xiàn)形式楼吃,靜態(tài)路由和動態(tài)路由始花。

靜態(tài)路由

靜態(tài)路由是MaterialApp提供的一個APIroutes本質(zhì)上是一個Map對象孩锡,其組成結(jié)構(gòu)是key是調(diào)用頁面的唯一標(biāo)識符酷宵,value就是對應(yīng)頁面的Widget

在定義靜態(tài)路由時躬窜,可以在創(chuàng)建Widget時傳入?yún)?shù)浇垦,例如實(shí)例化ContactWidget時就可以傳入對應(yīng)的參數(shù)過去。

void main() {
  runApp(
    MaterialApp(
      home: Page2(),
      routes: {
        'page1': (_) => Page1(),
        'page2': (_) => Page2()
      },
    ),
  );
}

class Page1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ContactWidget();
  }
}

class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return HomeScreen();
  }
}

進(jìn)行頁面跳轉(zhuǎn)時荣挨,通過Navigator進(jìn)行調(diào)用男韧,每次調(diào)用都會重新創(chuàng)建對應(yīng)的Widget朴摊。進(jìn)行調(diào)用時pushNamed函數(shù)會傳入一個參數(shù),這個參數(shù)就是定義Map時對應(yīng)頁面的key此虑。

Navigator.of(context).pushNamed('page1');
動態(tài)路由

靜態(tài)路由的方式并不是很靈活甚纲,相對而言動態(tài)路由更加靈活。動態(tài)路由不需要預(yù)先設(shè)定routes朦前,直接調(diào)用即可贩疙。和普通push不同的是,動態(tài)路由在push時通過PageRouteBuilder來構(gòu)建push對象况既,在Builder的構(gòu)建方法中執(zhí)行對應(yīng)的頁面跳轉(zhuǎn)操作即可这溅。

結(jié)合之前說的channelMethod,就是在channelMethod對應(yīng)的Callback回調(diào)中棒仍,執(zhí)行Navigatorpush函數(shù)悲靴,接收Native傳遞過來的參數(shù)并構(gòu)建對應(yīng)的Widget頁面,將Widget返回給Builder即可完成頁面跳轉(zhuǎn)操作莫其。所以說動態(tài)路由的方式非常靈活癞尚。

無論是通過靜態(tài)路由還是動態(tài)路由的方式創(chuàng)建,都可以通過then函數(shù)接收新頁面返回時的返回值乱陡。

Navigator.of(context).push(PageRouteBuilder(
    pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
      return ContactWidget('next page value');
    }
    transitionsBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
      return FadeTransition(
        child: child,
        opacity: animation,
      );
    }
)).then((onValue){
      print('pop的返回值 $onValue');
});

但動態(tài)路由的跳轉(zhuǎn)方式也有一些問題浇揩,會導(dǎo)致動畫失效。所以需要重寫BuildertransitionsBuilder函數(shù)憨颠,來自定義轉(zhuǎn)場動畫胳徽。

無論是通過靜態(tài)路由還是動態(tài)路由的方式創(chuàng)建,都會存在一些問題爽彤。由于每次都是新創(chuàng)建Widget养盗,所以在創(chuàng)建時會有黑屏的問題。而且每次創(chuàng)建的話适篙,都會丟失當(dāng)前頁面上次的上下文狀態(tài)往核,每次進(jìn)來都是一個新頁面。


簡書由于排版的問題嚷节,閱讀體驗(yàn)并不好聂儒,布局、圖片顯示硫痰、代碼等很多問題衩婚。所以建議到我Github上,下載Flutter編程指南 PDF合集碍论。把所有Flutter文章總計(jì)三篇谅猾,都寫在這個PDF中柄慰,而且左側(cè)有目錄鳍悠,方便閱讀税娜。

Flutter編程指南

下載地址:Flutter編程指南 PDF
麻煩各位大佬點(diǎn)個贊,謝謝藏研!??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敬矩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蠢挡,更是在濱河造成了極大的恐慌弧岳,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件业踏,死亡現(xiàn)場離奇詭異禽炬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)勤家,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門腹尖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人伐脖,你說我怎么就攤上這事热幔。” “怎么了讼庇?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵绎巨,是天一觀的道長。 經(jīng)常有香客問我蠕啄,道長场勤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任歼跟,我火速辦了婚禮却嗡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嘹承。我一直安慰自己窗价,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布叹卷。 她就那樣靜靜地躺著撼港,像睡著了一般。 火紅的嫁衣襯著肌膚如雪骤竹。 梳的紋絲不亂的頭發(fā)上帝牡,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機(jī)與錄音蒙揣,去河邊找鬼靶溜。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的罩息。 我是一名探鬼主播嗤详,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瓷炮!你這毒婦竟也來了葱色?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤娘香,失蹤者是張志新(化名)和其女友劉穎苍狰,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烘绽,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淋昭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了安接。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片响牛。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖赫段,靈堂內(nèi)的尸體忽然破棺而出呀打,到底是詐尸還是另有隱情,我是刑警寧澤糯笙,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布贬丛,位于F島的核電站,受9級特大地震影響给涕,放射性物質(zhì)發(fā)生泄漏豺憔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一够庙、第九天 我趴在偏房一處隱蔽的房頂上張望恭应。 院中可真熱鬧,春花似錦耘眨、人聲如沸昼榛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胆屿。三九已至,卻和暖如春偶宫,著一層夾襖步出監(jiān)牢的瞬間非迹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工纯趋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留憎兽,地道東北人冷离。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像纯命,于是被迫代替她去往敵國和親西剥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348