2023-02-24 flutter熱重載和熱重啟

1仙逻、_commonTerminalInputHandler

熱重載和熱重啟部分相同的驰吓。都是執(zhí)行residentRunner.restart();
熱重載:
final OperationResult result = await residentRunner.restart();
熱重啟:
final OperationResult result = await residentRunner.restart(fullRestart: true);

 case 'r':
        if (!residentRunner.canHotReload) {
          return false;
        }
        final OperationResult result = await residentRunner.restart();
        if (result.fatal) {
          throwToolExit(result.message);
        }
        if (!result.isOk) {
          _logger.printStatus('Try again after fixing the above error(s).', emphasis: true);
        }
        return true;
      case 'R':
        // If hot restart is not supported for all devices, ignore the command.
        if (!residentRunner.supportsRestart || !residentRunner.hotMode) {
          return false;
        }
        final OperationResult result = await residentRunner.restart(fullRestart: true);
        if (result.fatal) {
          throwToolExit(result.message);
        }
        if (!result.isOk) {
          _logger.printStatus('Try again after fixing the above error(s).', emphasis: true);
        }
        return true;

2、residentRunner.restart();

 Future<OperationResult> restart({
    bool fullRestart = false,
    String reason,
    bool silent = false,
    bool pause = false,
  }) async {
    if (flutterDevices.any((FlutterDevice device) => device.devFS == null)) {
      return OperationResult(1, 'Device initialization has not completed.');
    }
    await _calculateTargetPlatform();
    final Stopwatch timer = Stopwatch()..start();

    // Run source generation if needed.
    await runSourceGenerators();

    if (fullRestart) {
      final OperationResult result = await _fullRestartHelper(
        targetPlatform: _targetPlatform,
        sdkName: _sdkName,
        emulator: _emulator,
        reason: reason,
        silent: silent,
      );
      if (!silent) {
        globals.printStatus('Restarted application in ${getElapsedAsMilliseconds(timer.elapsed)}.');
      }
      unawaited(residentDevtoolsHandler.hotRestart(flutterDevices));
      return result;
    }
    final OperationResult result = await _hotReloadHelper(
      targetPlatform: _targetPlatform,
      sdkName: _sdkName,
      emulator: _emulator,
      reason: reason,
      pause: pause,
    );
    if (result.isOk) {
      final String elapsed = getElapsedAsMilliseconds(timer.elapsed);
      if (!silent) {
        globals.printStatus('${result.message} in $elapsed.');
      }
    }
    return result;
  }

這段代碼實(shí)現(xiàn)了一個(gè) restart 方法系奉,接收幾個(gè)可選參數(shù):fullRestart檬贰,reason,silent 和 pause缺亮,返回一個(gè) Future<OperationResult> 對(duì)象翁涤。

  • 該方法首先檢查所有的 flutterDevices 是否都已經(jīng)初始化完成,如果有任何一個(gè)設(shè)備的 devFS 為 null萌踱,則返回一個(gè) OperationResult 對(duì)象葵礼,表示設(shè)備尚未初始化完成。

  • 接著并鸵,該方法會(huì)調(diào)用 _calculateTargetPlatform 方法來計(jì)算目標(biāo)平臺(tái)鸳粉,并啟動(dòng)一個(gè)計(jì)時(shí)器。

  • 然后园担,該方法會(huì)調(diào)用 runSourceGenerators 方法來運(yùn)行源代碼生成器(如果需要的話)届谈。

  • 如果 fullRestart 參數(shù)為 true枯夜,則調(diào)用 _fullRestartHelper 方法來執(zhí)行一個(gè)完全重啟操作,并打印重啟耗時(shí)艰山。否則湖雹,調(diào)用 _hotReloadHelper 方法來執(zhí)行熱重載操作,并打印操作結(jié)果和耗時(shí)曙搬。

最后摔吏,返回操作結(jié)果。

熱重啟和熱重載纵装,都會(huì)經(jīng)過重新生成源代碼的過程征讲。
_calculateTargetPlatform -》runSourceGenerators

_fullRestartHelper 執(zhí)行熱重啟,_hotReloadHelper 執(zhí)行熱重載橡娄。

3稳诚、runSourceGenerators

 Future<void> runSourceGenerators() async {
    _environment ??= Environment(
      artifacts: globals.artifacts,
      logger: globals.logger,
      cacheDir: globals.cache.getRoot(),
      engineVersion: globals.flutterVersion.engineRevision,
      fileSystem: globals.fs,
      flutterRootDir: globals.fs.directory(Cache.flutterRoot),
      outputDir: globals.fs.directory(getBuildDirectory()),
      processManager: globals.processManager,
      platform: globals.platform,
      projectDir: globals.fs.currentDirectory,
      generateDartPluginRegistry: generateDartPluginRegistry,
      defines: <String, String>{
        // Needed for Dart plugin registry generation.
        kTargetFile: mainPath,
      },
    );

    final CompositeTarget compositeTarget = CompositeTarget(<Target>[
      const GenerateLocalizationsTarget(),
      const DartPluginRegistrantTarget(),
    ]);

    _lastBuild = await globals.buildSystem.buildIncremental(
      compositeTarget,
      _environment,
      _lastBuild,
    );
    if (!_lastBuild.success) {
      for (final ExceptionMeasurement exceptionMeasurement in _lastBuild.exceptions.values) {
        globals.printError(
          exceptionMeasurement.exception.toString(),
          stackTrace: globals.logger.isVerbose
            ? exceptionMeasurement.stackTrace
            : null,
        );
      }
    }
    globals.printTrace('complete');
  }

這段代碼實(shí)現(xiàn)了一個(gè) runSourceGenerators() 方法,它的作用是運(yùn)行Flutter應(yīng)用程序的源代碼生成器來生成本地化和Dart插件注冊(cè)表瀑踢。

  • 該方法首先使用 Flutter 環(huán)境變量(_environment)初始化一個(gè) CompositeTarget扳还,該 CompositeTarget 包含了兩個(gè) Target,分別是 GenerateLocalizationsTarget() 和 DartPluginRegistrantTarget()橱夭。

  • 接著氨距,它使用 globals.buildSystem.buildIncremental() 方法來增量構(gòu)建這個(gè) CompositeTarget,構(gòu)建環(huán)境為 _environment棘劣,上一次構(gòu)建的結(jié)果為 _lastBuild俏让。這個(gè)方法返回一個(gè) BuildResult 對(duì)象,其中包含了本次構(gòu)建的成功或失敗信息茬暇。

  • 如果構(gòu)建失敗首昔,它會(huì)遍歷 _lastBuild.exceptions 中的所有異常,并將它們打印出來糙俗,包括異常信息和堆棧跟蹤(如果日志級(jí)別是 verbose 的話)勒奇。

  • 最后,它會(huì)將日志信息打印到控制臺(tái)巧骚,并返回赊颠。注意,該方法不返回任何值劈彪,但它可能會(huì)改變一些類成員變量的值竣蹦,比如 _environment 和 _lastBuild。

4沧奴、 buildIncremental

Future<BuildResult> buildIncremental(
    Target target,
    Environment environment,
    BuildResult? previousBuild,
  ) async {
    environment.buildDir.createSync(recursive: true);
    environment.outputDir.createSync(recursive: true);

    FileStore? fileCache;
    if (previousBuild == null || _incrementalFileStore[previousBuild] == null) {
      final File cacheFile = environment.buildDir.childFile(FileStore.kFileCache);
      fileCache = FileStore(
        cacheFile: cacheFile,
        logger: _logger,
        strategy: FileStoreStrategy.timestamp,
      )..initialize();
    } else {
      fileCache = _incrementalFileStore[previousBuild];
    }
    final Node node = target._toNode(environment);
    final _BuildInstance buildInstance = _BuildInstance(
      environment: environment,
      fileCache: fileCache!,
      buildSystemConfig: const BuildSystemConfig(),
      logger: _logger,
      fileSystem: _fileSystem,
      platform: _platform,
    );
    bool passed = true;
    try {
      passed = await buildInstance.invokeTarget(node);
    } finally {
      fileCache.persistIncremental();
    }
    final BuildResult result = BuildResult(
      success: passed,
      exceptions: buildInstance.exceptionMeasurements,
      performance: buildInstance.stepTimings,
    );
    _incrementalFileStore[result] = fileCache;
    return result;
  }

這段代碼實(shí)現(xiàn)了一個(gè)異步函數(shù) buildIncremental痘括,用于增量構(gòu)建指定目標(biāo)(Target)的代碼,并在給定環(huán)境(Environment)下執(zhí)行構(gòu)建過程滔吠。它接受三個(gè)參數(shù):目標(biāo)纲菌、環(huán)境和先前的構(gòu)建結(jié)果(可選)抄淑。它返回一個(gè) Future<BuildResult>,其中包含構(gòu)建結(jié)果驰后。

函數(shù)的執(zhí)行流程如下:

  • 首先,創(chuàng)建構(gòu)建目錄和輸出目錄矗愧,如果它們不存在的話灶芝。
  • 然后,根據(jù)先前的構(gòu)建結(jié)果是否為 null唉韭,創(chuàng)建一個(gè)文件緩存對(duì)象(FileStore)或從緩存中獲取一個(gè)夜涕。這個(gè)緩存會(huì)記錄每個(gè)構(gòu)建步驟的輸入和輸出文件,并根據(jù)它們的時(shí)間戳來判斷哪些步驟需要重新構(gòu)建属愤。
  • 接下來女器,將目標(biāo)對(duì)象轉(zhuǎn)換為一個(gè)依賴圖中的節(jié)點(diǎn)(Node)對(duì)象。
    創(chuàng)建一個(gè) _BuildInstance 實(shí)例住诸,并將它需要的參數(shù)傳遞給它驾胆,包括環(huán)境、文件緩存贱呐、日志記錄器丧诺、文件系統(tǒng)和平臺(tái)等。
  • 然后奄薇,調(diào)用 _BuildInstance.invokeTarget() 方法驳阎,傳遞目標(biāo)節(jié)點(diǎn),開始構(gòu)建過程馁蒂。如果構(gòu)建過程中有異常發(fā)生呵晚,異常會(huì)被捕獲并保存到 buildInstance.exceptionMeasurements 列表中。
  • 最后沫屡,調(diào)用 fileCache.persistIncremental() 方法饵隙,將增量構(gòu)建所用到的緩存保存到磁盤上。
  • 創(chuàng)建一個(gè) BuildResult 對(duì)象沮脖,其中包含構(gòu)建結(jié)果的成功或失敗狀態(tài)癞季、異常列表和性能指標(biāo),然后將該結(jié)果與緩存對(duì)象一起保存到 _incrementalFileStore 字典中倘潜,以備下次增量構(gòu)建時(shí)使用绷柒。
  • 返回 BuildResult 對(duì)象作為異步操作的結(jié)果。

因此涮因,這段代碼實(shí)現(xiàn)了一個(gè)增量構(gòu)建過程废睦,其中緩存機(jī)制可以提高構(gòu)建性能,只有在必要時(shí)才會(huì)重新構(gòu)建文件养泡。這可以節(jié)省時(shí)間和資源嗜湃,特別是在大型項(xiàng)目中奈应。

5、 _fullRestartHelper

Future<OperationResult> _fullRestartHelper({
    String targetPlatform,
    String sdkName,
    bool emulator,
    String reason,
    bool silent,
  }) async {
    if (!supportsRestart) {
      return OperationResult(1, 'hotRestart not supported');
    }
    Status status;
    if (!silent) {
      status = globals.logger.startProgress(
        'Performing hot restart...',
        progressId: 'hot.restart',
      );
    }
    OperationResult result;
    String restartEvent;
    try {
      final Stopwatch restartTimer = _stopwatchFactory.createStopwatch('fullRestartHelper')..start();
      if (!(await hotRunnerConfig.setupHotRestart())) {
        return OperationResult(1, 'setupHotRestart failed');
      }
      result = await _restartFromSources(reason: reason);
      restartTimer.stop();
      if (!result.isOk) {
        restartEvent = 'restart-failed';
      } else {
        HotEvent('restart',
          targetPlatform: targetPlatform,
          sdkName: sdkName,
          emulator: emulator,
          fullRestart: true,
          reason: reason,
          fastReassemble: false,
          overallTimeInMs: restartTimer.elapsed.inMilliseconds,
          syncedBytes: result.updateFSReport?.syncedBytes,
          invalidatedSourcesCount: result.updateFSReport?.invalidatedSourcesCount,
          transferTimeInMs: result.updateFSReport?.transferDuration?.inMilliseconds,
          compileTimeInMs: result.updateFSReport?.compileDuration?.inMilliseconds,
          findInvalidatedTimeInMs: result.updateFSReport?.findInvalidatedDuration?.inMilliseconds,
          scannedSourcesCount: result.updateFSReport?.scannedSourcesCount,
        ).send();
      }
    } on vm_service.SentinelException catch (err, st) {
      restartEvent = 'exception';
      return OperationResult(1, 'hot restart failed to complete: $err\n$st', fatal: true);
    } on vm_service.RPCError  catch (err, st) {
      restartEvent = 'exception';
      return OperationResult(1, 'hot restart failed to complete: $err\n$st', fatal: true);
    } finally {
      // The `restartEvent` variable will be null if restart succeeded. We will
      // only handle the case when it failed here.
      if (restartEvent != null) {
        HotEvent(restartEvent,
          targetPlatform: targetPlatform,
          sdkName: sdkName,
          emulator: emulator,
          fullRestart: true,
          reason: reason,
          fastReassemble: false,
        ).send();
      }
      status?.cancel();
    }
    return result;
  }

  

6购披、 await _restartFromSources(reason: reason);

  Future<OperationResult> _restartFromSources({
    String reason,
  }) async {
    final Stopwatch restartTimer = Stopwatch()..start();
    UpdateFSReport updatedDevFS;
    try {
      updatedDevFS = await _updateDevFS(fullRestart: true);
    } finally {
      hotRunnerConfig.updateDevFSComplete();
    }
    if (!updatedDevFS.success) {
      for (final FlutterDevice device in flutterDevices) {
        if (device.generator != null) {
          await device.generator.reject();
        }
      }
      return OperationResult(1, 'DevFS synchronization failed');
    }
    _resetDirtyAssets();
    for (final FlutterDevice device in flutterDevices) {
      // VM must have accepted the kernel binary, there will be no reload
      // report, so we let incremental compiler know that source code was accepted.
      if (device.generator != null) {
        device.generator.accept();
      }
    }
    // Check if the isolate is paused and resume it.
    final List<Future<void>> operations = <Future<void>>[];
    for (final FlutterDevice device in flutterDevices) {
      final Set<String> uiIsolatesIds = <String>{};
      final List<FlutterView> views = await device.vmService.getFlutterViews();
      for (final FlutterView view in views) {
        if (view.uiIsolate == null) {
          continue;
        }
        uiIsolatesIds.add(view.uiIsolate.id);
        // Reload the isolate.
        final Future<vm_service.Isolate> reloadIsolate = device.vmService
          .getIsolateOrNull(view.uiIsolate.id);
        operations.add(reloadIsolate.then((vm_service.Isolate isolate) async {
          if ((isolate != null) && isPauseEvent(isolate.pauseEvent.kind)) {
            // The embedder requires that the isolate is unpaused, because the
            // runInView method requires interaction with dart engine APIs that
            // are not thread-safe, and thus must be run on the same thread that
            // would be blocked by the pause. Simply un-pausing is not sufficient,
            // because this does not prevent the isolate from immediately hitting
            // a breakpoint (for example if the breakpoint was placed in a loop
            // or in a frequently called method) or an exception. Instead, all
            // breakpoints are first disabled and exception pause mode set to
            // None, and then the isolate resumed.
            // These settings to not need restoring as Hot Restart results in
            // new isolates, which will be configured by the editor as they are
            // started.
            final List<Future<void>> breakpointAndExceptionRemoval = <Future<void>>[
              device.vmService.service.setIsolatePauseMode(isolate.id,
                exceptionPauseMode: vm_service.ExceptionPauseMode.kNone),
              for (final vm_service.Breakpoint breakpoint in isolate.breakpoints)
                device.vmService.service.removeBreakpoint(isolate.id, breakpoint.id)
            ];
            await Future.wait(breakpointAndExceptionRemoval);
            await device.vmService.service.resume(view.uiIsolate.id);
          }
        }));
      }

      // The engine handles killing and recreating isolates that it has spawned
      // ("uiIsolates"). The isolates that were spawned from these uiIsolates
      // will not be restarted, and so they must be manually killed.
      final vm_service.VM vm = await device.vmService.service.getVM();
      for (final vm_service.IsolateRef isolateRef in vm.isolates) {
        if (uiIsolatesIds.contains(isolateRef.id)) {
          continue;
        }
        operations.add(device.vmService.service.kill(isolateRef.id)
          .catchError((dynamic error, StackTrace stackTrace) {
            // Do nothing on a SentinelException since it means the isolate
            // has already been killed.
            // Error code 105 indicates the isolate is not yet runnable, and might
            // be triggered if the tool is attempting to kill the asset parsing
            // isolate before it has finished starting up.
          }, test: (dynamic error) => error is vm_service.SentinelException
            || (error is vm_service.RPCError && error.code == 105)));
      }
    }
    await Future.wait(operations);

    await _launchFromDevFS();
    restartTimer.stop();
    globals.printTrace('Hot restart performed in ${getElapsedAsMilliseconds(restartTimer.elapsed)}.');
    _addBenchmarkData('hotRestartMillisecondsToFrame',
        restartTimer.elapsed.inMilliseconds);

    // Send timing analytics.
    globals.flutterUsage.sendTiming('hot', 'restart', restartTimer.elapsed);

    // Toggle the main dill name after successfully uploading.
    _swap =! _swap;

    return OperationResult(
      OperationResult.ok.code,
      OperationResult.ok.message,
      updateFSReport: updatedDevFS,
    );
  }

在這里發(fā)現(xiàn)一個(gè)目錄:
/private/var/folders/4j/z1n6phgx4d11klg60p7d3b240000gn/T/flutter_tools.COlwZv/flutter_tool.Kr5rSU/app.dill

編譯過程:


image.png

編譯同步過程:


image.png

VmService call方法:

  Future<T> _call<T>(String method, [Map args = const {}]) async {
    print('--->method:$method');
    final request = _OutstandingRequest(method);
    _outstandingRequests[request.id] = request;
    Map m = {
      'jsonrpc': '2.0',
      'id': request.id,
      'method': method,
      'params': args,
    };
    String message = jsonEncode(m);
    _onSend.add(message);
    _writeMessage(message);
    return await request.future as T;
  }

在這個(gè)方法添加上日志杖挣,比較hot reload 和hot restart 區(qū)別:

  • flutter attach
    --->method:registerService
    --->method:getVersion
    --->method:registerService
    --->method:registerService
    --->method:registerService
    --->method:registerService
    --->method:registerService
    --->method:registerService
    --->method:streamListen
    --->method:getVersion
    --->method:_flutter.listViews
    --->method:getVM
    --->method:_createDevFS
    Syncing files to device iPhone 14 Pro...
    --->method:streamListen
    --->method:_flutter.listViews
    --->method:getIsolate
    --->method:streamCancel
    --->method:_flutter.listViews
    --->method:ext.flutter.activeDevToolsServerAddress
    --->method:_flutter.listViews
    --->method:ext.flutter.connectedVmServiceUri

  • 熱重載:
    --->method:_flutter.listViews
    --->method:getIsolate
    --->method:ext.flutter.reassemble

  • 熱重啟:
    --->method:_flutter.listViews
    --->method:getIsolate
    --->method:getVM
    --->method:_flutter.listViews
    --->method:streamListen
    --->method:_flutter.runInView
    Restarted application in 1,419ms.
    --->method:streamListen
    --->method:_flutter.listViews
    --->method:getIsolate
    --->method:streamCancel
    --->method:_flutter.listViews
    --->method:_flutter.listViews
    --->method:ext.flutter.activeDevToolsServerAddress
    --->method:ext.flutter.connectedVmServiceUri

從上面調(diào)試過程中的,可以看出下面轉(zhuǎn)載的內(nèi)容沒有什么出入刚陡。

下面內(nèi)容從https://juejin.cn/post/7041728779038752799轉(zhuǎn)載:

熱重載的步驟:

  • 工程改動(dòng):熱重載Server會(huì)逐一掃描工程中的文件惩妇,檢查是否有新增、刪除或者改動(dòng)筐乳,直到找到在上次編譯之后歌殃,發(fā)生變化的 Dart 代碼。
    增量編譯:熱重載模塊會(huì)將發(fā)生變化的 Dart 代碼蝙云,通過編譯轉(zhuǎn)化為增量的 Dart Kernel 文件氓皱。
  • 推送更新:熱重載Server將增量的 Dart Kernel 文件通過 RPC 協(xié)議,發(fā)送給正在手機(jī)上運(yùn)行的 Dart VM勃刨。
  • 代碼合并:Dart VM 會(huì)將收到的增量 Dart Kernel 文件波材,與原有的 Dart Kernel 文件進(jìn)行合并,然后重新加載新的 Dart Kernel 文件身隐。
  • Widget增量渲染:在確認(rèn) Dart VM 資源加載成功后各聘,F(xiàn)lutter 會(huì)將其 UI 線程重置,通知 flutter.framework 重建 Widget抡医。

不支持熱重載的場(chǎng)景

  • main 方法里的更改
  • initState 方法里的更改
  • 代碼出現(xiàn)編譯錯(cuò)誤
  • 全局變量和靜態(tài)屬性的更改
  • Widget 狀態(tài)無法兼容
  • 枚舉和泛類型更改

Flutter 的熱重載是基于 JIT 編譯模式的代碼增量同步躲因。由于 JIT 屬于動(dòng)態(tài)編譯,能夠?qū)?Dart 代碼編譯成生成中間代碼忌傻,讓 Dart VM 在運(yùn)行時(shí)解釋執(zhí)行大脉,因此可以通過動(dòng)態(tài)更新中間代碼實(shí)現(xiàn)增量同步。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子哭懈,更是在濱河造成了極大的恐慌,老刑警劉巖秤标,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異宙刘,居然都是意外死亡苍姜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門悬包,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衙猪,“玉大人,你說我怎么就攤上這事〉媸停” “怎么了丝格?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)棵譬。 經(jīng)常有香客問我显蝌,道長(zhǎng),這世上最難降的妖魔是什么订咸? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任曼尊,我火速辦了婚禮,結(jié)果婚禮上算谈,老公的妹妹穿的比我還像新娘。我一直安慰自己料滥,他們只是感情好然眼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著葵腹,像睡著了一般高每。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上践宴,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天鲸匿,我揣著相機(jī)與錄音,去河邊找鬼阻肩。 笑死带欢,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的烤惊。 我是一名探鬼主播乔煞,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼柒室!你這毒婦竟也來了渡贾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤雄右,失蹤者是張志新(化名)和其女友劉穎空骚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體擂仍,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡囤屹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了逢渔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牺丙。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出冲簿,到底是詐尸還是另有隱情粟判,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布峦剔,位于F島的核電站档礁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏吝沫。R本人自食惡果不足惜呻澜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惨险。 院中可真熱鬧羹幸,春花似錦、人聲如沸辫愉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)恭朗。三九已至屏镊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間痰腮,已是汗流浹背而芥。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留膀值,地道東北人棍丐。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像沧踏,于是被迫代替她去往敵國(guó)和親骄酗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

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