Flutter 啟動(dòng)流程源碼解析

一: 概述

學(xué)Flutter印象最深的一句話是:萬物皆Widget泛释。 但Widget是不可變的淘讥,頁面發(fā)生變化時(shí)茎刚,Widget一定會(huì)被重新構(gòu)建悲关。 在我們理解中對(duì)象的創(chuàng)建帶來的性能損耗是非常巨大的谎僻,這與Flutter宣傳的高性能不符合。這其中的緣由是寓辱,Widget并不是“邏輯處理”與“視圖渲染”對(duì)象艘绍,它是一個(gè)非常輕量級(jí)的描述類。 因此Widget的批量創(chuàng)建銷毀對(duì)Flutter的性能損耗是非常小的秫筏。

Flutter會(huì)根據(jù)Widget創(chuàng)建出唯一的Element诱鞠,然后會(huì)創(chuàng)建出一個(gè)繼承自Element的唯一RenderObjet。Widget負(fù)責(zé)頁面描述这敬,Element負(fù)責(zé)信息存儲(chǔ)中樞調(diào)度航夺,RenderObjet是真正的繪制實(shí)例。 由于他們結(jié)構(gòu)類似于HTML中的DOM樹崔涂,因此形象的將其稱為Flutter的三棵樹阳掐。

二: 啟動(dòng)流程圖

本文將從源碼解析Flutter的啟動(dòng)流程,順便標(biāo)記三棵樹的創(chuàng)建時(shí)機(jī)冷蚂。源碼均復(fù)制自 Stable 1.17.5, 非核心代碼有刪減缭保。下圖是我根據(jù)源碼調(diào)用順序繪制的簡(jiǎn)單流程圖。

三: 啟動(dòng)詳細(xì)流程

3.1 main

void main() => runApp(MyApp());

3.2 runApp

[flutter/lib/src/widgets/binding.dart]

/// 把傳入 widget 充滿全屏
///
/// 傳入的 widget 會(huì)被強(qiáng)制充滿全屏蝙茶,若想調(diào)整 widget 對(duì)齊方式艺骂,可考慮[Align]、[Center]等
///
/// 再次調(diào)用[runApp]將會(huì)把屏幕之前的root widget替換為傳入的widget尸闸。
/// 新的 widget 樹會(huì)與之前的 widget樹進(jìn)行比較彻亲,任何差異都會(huì)被應(yīng)用到底層的渲染樹上
/// 類似于調(diào)用 [State.setState] 后 [StatefulWidget] 重新構(gòu)建
///
/// 如果需要,使用[WidgetsFlutterBinding]初始化綁定吮廉。
///
/// 參見:
///
///  * [WidgetsBinding.attachRootWidget], widget 結(jié)構(gòu)創(chuàng)建根 widget
///  * [RenderObjectToWidgetAdapter.attachToRenderTree], 元素結(jié)構(gòu)創(chuàng)建根元素苞尝。
///  * [WidgetsBinding.handleBeginFrame], _transientCallbacks 的函數(shù)回調(diào)
///     以確保小部件、元素和渲染樹都構(gòu)建好了宦芦。
void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

從這里可大致推測(cè)出宙址,runApp做了三件事:

  • 初始化 WidgetsFlutterBinding
  • 將界面App加載到 flutter 樹中
  • 計(jì)算面積,開始繪制

3.3 ensureInitialized

[flutter/lib/src/widgets/binding.dart]

/// 給程序做一個(gè)功能集的綁定
///
/// 粘合framework 和 引擎層的膠水
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
  static WidgetsBinding ensureInitialized() {
    if (WidgetsBinding.instance == null)
      WidgetsFlutterBinding();
    return WidgetsBinding.instance;
  }
}

這是一個(gè)功能集(flutter使用 mixin語法调卑,實(shí)現(xiàn)多繼承的效果)包含:

  • GestureBinding:提供了window.onPointerDataPacket 回調(diào)抡砂,綁定Framework手勢(shì)子系統(tǒng),是Framework事件模型與底層事件的綁定入口恬涧。
  • ServicesBinding:提供了window.onPlatformMessage 回調(diào)注益, 用于綁定平臺(tái)消息通道(message channel),主要處理原生和Flutter通信溯捆。
  • SchedulerBinding:提供了window.onBeginFrame和window.onDrawFrame回調(diào)丑搔,監(jiān)聽刷新事件,綁定Framework繪制調(diào)度子系統(tǒng)。
  • PaintingBinding:綁定繪制庫啤月,主要用于處理圖片緩存煮仇。
  • SemanticsBinding:語義化層與Flutter engine的橋梁,主要是輔助功能的底層支持谎仲。
  • RendererBinding: 提供了window.onMetricsChanged 浙垫、window.onTextScaleFactorChanged 等回調(diào)。它是渲染樹與Flutter engine的橋梁郑诺。
  • WidgetsBinding:提供了window.onLocaleChanged夹姥、onBuildScheduled 等回調(diào)。它是Flutter widget層與engine的橋梁间景。

3.4 scheduleAttachRootWidget

[flutter/lib/src/widgets/binding.dart]

/// 生成一個(gè)計(jì)時(shí)器佃声,同時(shí)附加到根 widget 上.
///
/// [runApp]時(shí)配置艺智,若是想同步構(gòu)建 widget 樹倘要,需使用[attachRootWidget]
void scheduleAttachRootWidget(Widget rootWidget) {
  Timer.run(() {
    attachRootWidget(rootWidget);
  });
}

3.4.1 attachRootWidget

[flutter/lib/src/widgets/binding.dart]

/// 將 widget 附加到 [renderViewElement]上(renderViewElement 是 Element 樹的根元素)
///
/// [runApp] 時(shí)配置生成 widget 樹
///
/// 參見: [RenderObjectToWidgetAdapter.attachToRenderTree].
void attachRootWidget(Widget rootWidget) {
  //構(gòu)造了一個(gè)RenderObjectToWidgetAdapter實(shí)例,它繼承于Widget十拣,所以它本質(zhì)就是Widget封拧。然后我們寫的Widget(也就是rootWidget變量)作為child參數(shù)傳進(jìn)去,而Render樹的根結(jié)點(diǎn)RenderView也作為container參數(shù)傳進(jìn)去夭问。
  //它就是我們Widget樹的根結(jié)點(diǎn)泽西,我們寫的Widget就是掛在它下面,它對(duì)應(yīng)的RenderObject就是RenderView缰趋。

  // _renderViewElement是element的根樹捧杉,首次調(diào)用[runApp]時(shí)初始化
  _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
    container: renderView,
    debugShortDescription: '[root]',
    child: rootWidget,
  ).attachToRenderTree(buildOwner, renderViewElement);
  // buildOwner負(fù)責(zé)管理 Widget 樹的構(gòu)建 
  // 初始化[_buildOwner]必須在[initInstances]方法中完成,
  // 因?yàn)樗枰?[ServicesBinding] 設(shè)置 [defaultBinaryMessenger] 實(shí)例秘血。
}

3.4.2 attachToRenderTree

[flutter/lib/src/widgets/binding.dart]

  /// 將當(dāng)前 widget 充滿屏幕味抖,返回 element 會(huì)作為 [Element] 樹的子樹
  ///
  /// If `element` is null, 該方法會(huì)創(chuàng)建一個(gè) element . else 傳入 element 將有會(huì)替換該 widget。
  ///
  /// 用于[runApp]引導(dǎo)應(yīng)用程序
  RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
    if (element == null) {
      owner.lockState(() {   //lockState灰粮,在下面代碼執(zhí)行過程中仔涩,禁止調(diào)用setState方法
        element = createElement(); //
        element.assignOwner(owner);
      });
      owner.buildScope(element, () {
        element.mount(null, null);
      });
      // This is most likely the first time the framework is ready to produce
      // a frame. Ensure that we are asked for one.
      SchedulerBinding.instance.ensureVisualUpdate();
    } else {
      element._newWidget = this;
      element.markNeedsBuild();
    }
    return element;
  }
3.4.2.1 createElement
@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);

  /// 創(chuàng)建 [RenderObject]托管的 節(jié)點(diǎn)
  ///
  /// 創(chuàng)建的[RenderObject]不會(huì)添加到 render 樹中,需要調(diào)用  
  ///[RenderObjectToWidgetAdapter.attachToRenderTree] 添加
  RenderObjectToWidgetElement(RenderObjectToWidgetAdapter<T> widget) : super(widget);
3.4.2.2 buildScope

[flutter/lib/src/widgets/framework.dart]

/// 為更新的 widget 樹建立一個(gè)作用域粘舟,并調(diào)用給定的' callback '(如果有的話)
/// 然后熔脂,使用 [scheduleBuildFor] 按深度順序構(gòu)建所有標(biāo)記為dirty的元素
///
/// 這種機(jī)制可以防止構(gòu)建方法過渡性地要求其他構(gòu)建方法運(yùn)行,從而可能導(dǎo)致無限循環(huán)
///
/// 臟列表在`callback'返回之后被處理柑肴,并使用[scheduleBuildFor]按深度順序構(gòu)建所有標(biāo)記為臟的元素
/// 如果在此方法運(yùn)行時(shí)將元素標(biāo)記為臟元素霞揉,則此元素必須比“ context”節(jié)點(diǎn)深,并且比任何先前構(gòu)建的節(jié)點(diǎn)深
///

/// 要在不執(zhí)行任何其他工作的情況下刷新當(dāng)前臟列表晰骑,可以調(diào)用此函數(shù)而無需回調(diào)
/// framework會(huì)在每幀繪制中調(diào)用 [WidgetsBinding.drawFrame]
///
/// 一次只能激活一個(gè)[buildScope]适秩。
///
/// 每個(gè)[buildScope]都有各自的[lockState]作用域。
///
/// 若想在每次調(diào)用此方法時(shí)打印控制臺(tái)消息,將[debugPrintBuildScope]設(shè)置為true隶症。
/// 該方法在調(diào)試 widgets 未被標(biāo)記為dirty或過于頻繁地被標(biāo)記為dirty的問題時(shí)非常有用政模。
void buildScope(Element context, [ VoidCallback callback ]) {
    if (callback == null && _dirtyElements.isEmpty)
        return;
    Timeline.startSync('Build', arguments: timelineWhitelistArguments);
    try {
        _scheduledFlushDirtyElements = true;
        if (callback != null) {
            Element debugPreviousBuildTarget;
            _dirtyElementsNeedsResorting = false;
            try {
                callback(); //回調(diào)mount()
            }
            ...
        }
        _dirtyElements.sort(Element._sort);
        _dirtyElementsNeedsResorting = false;
        int dirtyCount = _dirtyElements.length;
        int index = 0;
        while (index < dirtyCount) {
            try {
                _dirtyElements[index].rebuild(); //針對(duì)臟元素執(zhí)行rebuild操作
            }
            ...
        }
    } finally {
        ...
        Timeline.finishSync();
    }
}
3.4.2.3 mount

[flutter/lib/src/widgets/binding.dart]

@override
void mount(Element parent, dynamic newSlot) {
  super.mount(parent, newSlot);
  _rebuild();
}
3.4.2.3.1 mount

[flutter/lib/src/widgets/framework.dart]

/// 將這個(gè)元素添加到給定父節(jié)點(diǎn)的給定位置中。
///
/// 首次將新創(chuàng)建的元素添加到樹時(shí)蚂会,framework 調(diào)用此函數(shù)淋样。使用此方法初始化依賴于擁有父類的狀態(tài)。
/// 獨(dú)立于父類的 State 更容易在構(gòu)造函數(shù)中初始化胁住。
///
/// 此方法將元素從“initial(初始)” 生命周期狀態(tài)轉(zhuǎn)換為 “active (活動(dòng))” 生命周期狀態(tài)趁猴。
///
/// 重寫此方法的子類可能也要重寫[update], [visitChildren], 
/// [RenderObjectElement.insertRenderObjectChild],
/// [RenderObjectElement.moveRenderObjectChild], 
/// [RenderObjectElement.removeRenderObjectChild].
@mustCallSuper
void mount(Element parent, dynamic newSlot) {
  _parent = parent;
  _slot = newSlot;
  _depth = _parent != null ? _parent.depth + 1 : 1;
  _active = true;
  if (parent != null) // Only assign ownership if the parent is non-null
    _owner = parent.owner;
  final Key key = widget.key;
  if (key is GlobalKey) {
    key._register(this);
  }
  _updateInheritance();
}
3.4.2.3.2 mount

[flutter/lib/src/widgets/framework.dart]

/// 實(shí)現(xiàn)類重寫mount
  @override
  void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    _renderObject = widget.createRenderObject(this);//通過Elemet生成 RenderObject樹
    attachRenderObject(newSlot);
    _dirty = false;
  }
3.4.2.4 _rebuild

[flutter/lib/src/widgets/binding.dart]

void _rebuild() {
  try {
    _child = updateChild(_child, widget.child, _rootChildSlot);
  } catch (exception, stack) {
    final FlutterErrorDetails details = FlutterErrorDetails(
      exception: exception,
      stack: stack,
      library: 'widgets library',
      context: ErrorDescription('attaching to the render tree'),
    );
    FlutterError.reportError(details);
    final Widget error = ErrorWidget.builder(details);
    _child = updateChild(null, error, _rootChildSlot);
  }
}
3.4.2.5 updateChild

[flutter/lib/src/widgets/framework.dart]

/// 用給定的新配置更新給定的子節(jié)點(diǎn)。
///
/// 此方法是 widgets 系統(tǒng)的核心彪见。每次根據(jù)更新后的配置 “添加”儡司、“更新”、“刪除子節(jié)點(diǎn)”時(shí)都會(huì)調(diào)用它余指。
///
/// `newSlot`參數(shù)指定此元素的 [slot](節(jié)點(diǎn)槽點(diǎn)) 的新值捕犬。
///
/// 若`child`為null,而`newWidget`不為null酵镜,則需要為其創(chuàng)建一個(gè)新元素碉碉,
/// 并使用newWidget配置一個(gè)[Element]。
///
/// 若`newWidget`為null淮韭,而`child`不為null垢粮,則需將其刪除,因?yàn)樗鼪]有配置信息了靠粪。
///
/// 若兩個(gè)值都不為 null蜡吧,則需要將 'child' 的配置更新為 'newWidget' 提供的新配置。
/// 如果可以將“ newWidget”提供給現(xiàn)有的子節(jié)點(diǎn)(由[Widget.canUpdate]確定)占键,則將其給定昔善。
/// 否則,需要丟棄舊的子節(jié)點(diǎn)捞慌,并為新配置創(chuàng)建一個(gè)新的子節(jié)點(diǎn)耀鸦。
///
/// 如果兩者都為null,則說明沒有也不會(huì)有子節(jié)點(diǎn)了啸澡,因此啥也不干
///
/// [updateChild]方法如果必須創(chuàng)建一個(gè)子節(jié)點(diǎn)袖订,則返回一個(gè)新的子節(jié)點(diǎn);
/// 如果必須更新子節(jié)點(diǎn)嗅虏,則返回傳入的子節(jié)點(diǎn)洛姑;如果必須刪除該子節(jié)點(diǎn)而沒替換它,則返回null皮服。 
///
/// 下面的表格總結(jié)了上面的內(nèi)容:
///
/// |                     | **newWidget == null**  | **newWidget != null**   |
/// | :-----------------: | :--------------------- | :---------------------- |
/// |  **child == null**  |  Returns null.         |  Returns new [Element]. |
/// |  **child != null**  |  刪除Old child, returns null. | 如果可能的話更新Old child楞艾,返回child或新的 [Element]. |

@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
  if (newWidget == null) {
    if (child != null)
      deactivateChild(child);
    return null;
  }
  Element newChild;
  if (child != null) {
    bool hasSameSuperclass = true;
    if (hasSameSuperclass && child.widget == newWidget) {
      if (child.slot != newSlot)
        updateSlotForChild(child, newSlot);
      newChild = child;
    } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
      if (child.slot != newSlot)
        updateSlotForChild(child, newSlot);
      child.update(newWidget);
      newChild = child;
    } else {
      deactivateChild(child);
      newChild = inflateWidget(newWidget, newSlot);
    }
  } else {
    newChild = inflateWidget(newWidget, newSlot);
  }
  return newChild;
}

3.5.scheduleWarmUpFrame

[flutter/lib/src/widgets/binding.dart]

/// 盡可能快的幀繪制参咙,并非等待Vsync信號(hào)的響應(yīng)
///
/// 在程序啟動(dòng)時(shí)使用,以便第一幀繪制能額外獲得幾毫秒
///
/// 鎖定事件調(diào)度硫眯,直至此幀繪制完成 
///
/// 若提前執(zhí)行了 [scheduleFrame] 或 [scheduleForcedFrame]蕴侧,它會(huì)延時(shí)執(zhí)行 
///
/// 若別的幀繪制調(diào)度已開始,或別的地方調(diào)用了[scheduleWarmUpFrame] 則此方法不會(huì)執(zhí)行
///
/// 更推薦 [scheduleFrame] 來更新試圖
void scheduleWarmUpFrame() {
  if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
    return;
  _warmUpFrame = true;
  Timeline.startSync('Warm-up frame');
  final bool hadScheduledFrame = _hasScheduledFrame;
  Timer.run(() {
    handleBeginFrame(null);
  });
  Timer.run(() {
    handleDrawFrame();//
    resetEpoch();// 重置時(shí)間
    _warmUpFrame = false;
    if (hadScheduledFrame)
      scheduleFrame();
  });
  // 事件鎖定
  lockEvents(() async {
    await endOfFrame;
    Timeline.finishSync();
  });
}

3.5.1. handleBeginFrame

[flutter/lib/src/widgets/binding.dart]

/// 由引擎調(diào)用以準(zhǔn)備 framework 生成新的幀两入。
///
/// 此函數(shù)調(diào)用[scheduleFrameCallback]注冊(cè)的所有瞬時(shí)幀回調(diào)(TRANSIENT FRAME CALLBACKS)
/// 然后返回净宵,運(yùn)行任何預(yù)定的微任務(wù)(例如,通過瞬態(tài)幀回調(diào)解決的任何[Future]的處理程序)裹纳,
/// 并調(diào)用[handleDrawFrame]以繼續(xù)幀择葡。
///
/// 若傳入時(shí)間戳為空,則重用最后一幀的時(shí)間戳剃氧。
///
/// 若想在 debug 模式下的每幀開始處顯示橫敏储,將[debugPrintBeginFrameBanner]設(shè)置為true。
/// 橫幅將使用[debugPrint]打印到控制臺(tái)朋鞍,并包含幀號(hào)(每幀加1)和幀的時(shí)間戳已添。
/// 若傳入時(shí)間戳為空,則顯示字符串“warm-up frame”而非時(shí)間戳番舆。
/// 它允許響應(yīng)操作系統(tǒng)的“Vsync”信號(hào)酝碳,從而將 frames 主動(dòng)推入的幀與引擎請(qǐng)求的幀區(qū)分開來矾踱。
///
/// 也可以將[debugPrintEndFrameBanner]設(shè)置為true恨狈,從而在每幀末尾顯示橫幅。
/// 這使的開發(fā)者可以區(qū)分打印的日志是在'幀期間'還是在'幀之間'(例如呛讲,響應(yīng)事件或計(jì)時(shí)器)禾怠。
void handleBeginFrame(Duration? rawTimeStamp) {
  Timeline.startSync('Frame', arguments: timelineArgumentsIndicatingLandmarkEvent);
  _firstRawTimeStampInEpoch ??= rawTimeStamp;
  _currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? _lastRawTimeStamp);
  if (rawTimeStamp != null)
    _lastRawTimeStamp = rawTimeStamp;
    _hasScheduledFrame = false;
  try {
    // TRANSIENT FRAME CALLBACKS
    Timeline.startSync('Animate', arguments: timelineArgumentsIndicatingLandmarkEvent);
    _schedulerPhase = SchedulerPhase.transientCallbacks;
    final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
    _transientCallbacks = <int, _FrameCallbackEntry>{};
    callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
      if (!_removedIds.contains(id))
        _invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp!, callbackEntry.debugStack);
    });
    _removedIds.clear();
  } finally {
    _schedulerPhase = SchedulerPhase.midFrameMicrotasks;
  }
}

3.5.2. handleDrawFrame

[flutter/lib/src/widgets/binding.dart]

/// 由引擎調(diào)用以繪制出新的幀。
///
/// 此方法在 [handleBeginFrame] 之后立即被調(diào)用贝搁。
/// 它調(diào)用由[addPersistentFrameCallback]注冊(cè)的所有回調(diào)吗氏,這些回調(diào)通常驅(qū)動(dòng)渲染管道,
/// 然后調(diào)用由[addPostFrameCallback]注冊(cè)的回調(diào)雷逆。
///
/// 可以參閱 [handleBeginFrame] 的掛載調(diào)試(debugging hooks)弦讽,它在處理幀回調(diào)時(shí)可能很有用。
void handleDrawFrame() {
  assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
  Timeline.finishSync(); // end the "Animate" phase
  try {
    // PERSISTENT FRAME CALLBACKS
    _schedulerPhase = SchedulerPhase.persistentCallbacks;
    for (final FrameCallback callback in _persistentCallbacks)
      _invokeFrameCallback(callback, _currentFrameTimeStamp!);

    // POST-FRAME CALLBACKS
    _schedulerPhase = SchedulerPhase.postFrameCallbacks;
    final List<FrameCallback> localPostFrameCallbacks =
        List<FrameCallback>.from(_postFrameCallbacks);
    _postFrameCallbacks.clear();
    for (final FrameCallback callback in localPostFrameCallbacks)
      _invokeFrameCallback(callback, _currentFrameTimeStamp!);
  } finally {
    _schedulerPhase = SchedulerPhase.idle;
    Timeline.finishSync(); // end the Frame
    _currentFrameTimeStamp = null;
  }
}

3.5.3. scheduleFrame

[flutter/lib/src/widgets/binding.dart]

/// 如有必要膀哲,可通過調(diào)用[Window.scheduleFrame]來調(diào)度一個(gè)新幀往产。
///
/// 調(diào)用此方法后,引擎將(最終)調(diào)用 [handleBeginFrame]某宪。 
/// (該調(diào)用可能會(huì)延遲仿村,例:如果屏幕處于關(guān)閉狀態(tài),則通常會(huì)延遲直到屏幕打開且應(yīng)用程序可見為止兴喂。)
/// 在一個(gè)幀中調(diào)用此方法則會(huì)強(qiáng)制調(diào)度另一個(gè)幀蔼囊,即使當(dāng)前幀尚未完成焚志。
///
/// 調(diào)度幀由操作系統(tǒng)提供的 “Vsync” 信號(hào)觸發(fā)。
/// 歷史上屏幕通過 “Vsync”信號(hào) 刷新顯示內(nèi)容
/// 現(xiàn)在硬件的操作更復(fù)雜些畏鼓,但通過“Vsync” 信號(hào)重新渲染從而刷新APP的方案繼續(xù)被延用酱酬。
///
/// 若想打印調(diào)度幀的堆棧信息,需將[debugPrintScheduleFrameStacks]設(shè)置為true云矫。
///
/// 參見:
///
///  * [scheduleForcedFrame], 在調(diào)度幀時(shí)會(huì)忽略[lifecycleState]岳悟。
///  * [scheduleWarmUpFrame], 完全忽略“Vsync”信號(hào)并立即觸發(fā)幀繪制。
void scheduleFrame() {
  if (_hasScheduledFrame || !framesEnabled)
    return;
  ensureFrameCallbacksRegistered();
  window.scheduleFrame();
  _hasScheduledFrame = true;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末泼差,一起剝皮案震驚了整個(gè)濱河市贵少,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌堆缘,老刑警劉巖滔灶,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異吼肥,居然都是意外死亡录平,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門缀皱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斗这,“玉大人,你說我怎么就攤上這事啤斗”砑” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵钮莲,是天一觀的道長(zhǎng)免钻。 經(jīng)常有香客問我,道長(zhǎng)崔拥,這世上最難降的妖魔是什么极舔? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮链瓦,結(jié)果婚禮上拆魏,老公的妹妹穿的比我還像新娘。我一直安慰自己慈俯,他們只是感情好渤刃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肥卡,像睡著了一般溪掀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上步鉴,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天揪胃,我揣著相機(jī)與錄音璃哟,去河邊找鬼。 笑死喊递,一個(gè)胖子當(dāng)著我的面吹牛随闪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播骚勘,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼铐伴,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了俏讹?” 一聲冷哼從身側(cè)響起当宴,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎泽疆,沒想到半個(gè)月后户矢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡殉疼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年梯浪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓢娜。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挂洛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出眠砾,到底是詐尸還是另有隱情虏劲,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布荠藤,位于F島的核電站伙单,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏哈肖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一念秧、第九天 我趴在偏房一處隱蔽的房頂上張望淤井。 院中可真熱鬧,春花似錦摊趾、人聲如沸币狠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漩绵。三九已至,卻和暖如春肛炮,著一層夾襖步出監(jiān)牢的瞬間止吐,已是汗流浹背宝踪。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留碍扔,地道東北人瘩燥。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像不同,于是被迫代替她去往敵國(guó)和親厉膀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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