阿里巴巴Flutter技術(shù)全解析

谷歌的 Flutter 為開發(fā)人員提供了一種構(gòu)建 Android 和 iOS 原生用戶界面的方法,為開發(fā)人員減少了很多相關(guān)的負擔(dān),包括幫助他們節(jié)省時間。有些人可能想知道 Flutter 項目和原生 Android 或 iOS 項目有何不同,例如它們的渲染機制或事件傳遞機制,或者在出現(xiàn)問題時開發(fā)人員如何修復(fù)錯誤和實現(xiàn)項目。

為了回答這些問題,這篇文章將以“hello_flutter”為例,首先介紹 Flutter 的設(shè)計原則,然后討論定制和優(yōu)化,并為對 Flutter 感興趣的開發(fā)人員提供了一些可遵循的步驟。

Flutter 基礎(chǔ)

架構(gòu)

Flutter 的架構(gòu)包含三個不同的層:框架、引擎和嵌入器。

Flutter 的框架使用 Dart 實現(xiàn),提供了 Material 風(fēng)格的小部件、Cupertino 風(fēng)格的小部件(用于 iOS)、文本 / 圖像 / 按鈕小部件、渲染、動畫、手勢等。該層的核心代碼包含了 flutter 代碼庫的包和 sky_engine 代碼庫的包(dart:ui 庫提供了 flutter 框架和引擎之間的接口),例如 io、async 和 ui 包。

Flutter 的引擎是用 C++ 實現(xiàn)的,并包含了 Skia、Dart 和 Text。Skia 是一個開源的 2D 圖形庫,為各種硬件和軟件平臺提供通用 API。它是谷歌 Chrome、Chrome OS、Android、Mozilla Firefox、Firefox OS 等產(chǎn)品的圖形引擎。支持的平臺包括 Windows7+、macOS 10.10.5+、iOS8+、Android4.1+、Ubuntu14.04+ 等。

引擎的 Dart 部分主要包括 Dart 運行時和垃圾回收(GC)。如果 Flutter 在調(diào)試模式下運行,則還包括 JIT(Just in Time)支持,而如果是在發(fā)布模式下,則通過 AOT(Ahead of Time)將 Dart 代碼編譯為原生的“arm”代碼,這個時候就沒有 JIT。Text 是指以下的文本渲染庫:libtxt 庫(用于字體選擇和分隔線),派生自 minikin 和 HartBuzz(用于字形和圖形)。Skia 充當(dāng)渲染后端,在 Android 上使用 FreeType 渲染,在 iOS 上使用 Fuchsia 和 CoreGraphics 渲染。

嵌入器可將 Flutter 嵌入到各種平臺中。它的主要任務(wù)是渲染 Surface 設(shè)置、線程設(shè)置和插件。我們可以看到,F(xiàn)lutter 的平臺相關(guān)層是最小的,其中平臺(例如 iOS)只提供畫布,其余與渲染相關(guān)的邏輯發(fā)生在 Flutter 內(nèi)部,從而實現(xiàn)良好的跨平臺一致性。

工程結(jié)構(gòu)

本文中使用的開發(fā)環(huán)境是 Flutter beta v0.3.1,對應(yīng)的引擎提交標(biāo)簽為 09d05a389。

“hello_flutter”示例項目的工程結(jié)構(gòu)如下:

在上面的例子中,“ios”是 iOS 的代碼,使用 CocoaPods 來管理依賴項,“android”是 Android 的代碼,使用 Gradle 來管理依賴項,“l(fā)ib”是 Dart 代碼,使用 pub 來管理依賴項。pub 中與 Cocoapods 的 Podfile 和 Podfile.lock 相對應(yīng)的分別是 pubspec.yaml 和 pubspec.lock。

模式

Flutter 支持常見的模式,包括調(diào)試、發(fā)布和分析,但它們之間存在一些區(qū)別。

Flutter 的調(diào)試模式對應(yīng)于 Dart 的 JIT 模式,也稱為檢查模式或慢模式,并支持 iOS 和 Android 的設(shè)備和模擬器。在這個模式下,可以啟用斷言功能,包括所有調(diào)試信息、服務(wù)擴展和調(diào)試輔助工具,如“observatory”。這個模式針對快速開發(fā)進行了優(yōu)化,但并沒有針對運行速度、程序包大小或部署進行過優(yōu)化。在這種模式下,采用了基于 JIT 的編譯技術(shù),支持流行的亞秒級有狀態(tài)熱重載。

Flutter 的發(fā)布模式對應(yīng)于 Dart 的 AOT 模式,該模式的目標(biāo)是部署到最終用戶的設(shè)備上,支持真實設(shè)備而不是模擬器。在此模式下,所有斷言都被禁用,為了盡可能多地刪除調(diào)試信息,還會禁用所有調(diào)試工具。這個模式針對快速啟動、快速運行和包大小進行了優(yōu)化,同時禁止所有調(diào)試輔助和服務(wù)擴展。

Flutter 的分析模式與發(fā)布模式類似,只是添加了對服務(wù)擴展和跟蹤的支持,并最小化使用跟蹤信息所需的依賴性。例如,“observatory”可以連到進程上。分析模式不支持模擬器,因為模擬器上的診斷不能代表實際性能。

由于 Flutter 的分析模式和發(fā)布模式在編譯方面沒有差異,因此本文僅討論調(diào)試模式和發(fā)布模式。

實際上,使用 Flutter 開發(fā)的 iOS 或 Android 項目仍然是標(biāo)準(zhǔn)的 iOS 或 Android 項目。Flutter 通過在 BuildPhase 中添加 shell 來生成并嵌入 App.framework 和 Flutter.framework(iOS),并通過 Gradle 添加 flutter.jar 和 vm/isolate_snapshot_data/instr(Android)來編譯相關(guān)代碼并將其嵌入到原生應(yīng)用程序中。因此,本文主要討論 Flutter 引入的構(gòu)建和運行原則。盡管編譯目標(biāo)包括 arm、x64、x86 和 arm64,但它們的原理都很類似,所以本文僅討論與 arm 相關(guān)的原則。(如果沒有特殊描述,Android 默認為 armv7。)

iOS 的代碼編譯和執(zhí)行

在發(fā)布模式下編譯

在發(fā)布模式下,iOS 項目的 Dart 代碼構(gòu)建過程如下:

在圖中,gen_snapshot 是 Dart 編譯器,它使用搖樹優(yōu)化技術(shù)(類似于可生成最小包的依賴樹邏輯,因此在 Flutter 中禁用 Dart 支持的反射)生成匯編形式的機器碼,然后通過編譯工具鏈(如 xcrun)生成最終 App.framework。換句話說,對于所有的 Dart 代碼,包括業(yè)務(wù)邏輯代碼和第三方軟件包代碼,它們所依賴的 Flutter 框架(Dart)代碼最終會變成 App.framework。

搖樹優(yōu)化功能位于 gen_snapshot 中。要查看相應(yīng)的邏輯,可以訪問:

engine/src/third_party/dart/runtime/vm/compiler/aot/precompiler.cc

Dart 代碼最終對應(yīng)于 App.framework 的符號如下:

事實上,與 Android 類似,App.framework 也包括了四個部分:kDartVmSnapshotData、kDartVmSnapshotInstructions、kDartIsolateSnapshotData 和 kDartIsolateSnapshotInstructions。為什么 iOS 使用 App.framework 而不是像 Android 那樣的四個文件?由于 iOS 系統(tǒng)的限制,F(xiàn)lutter 引擎無法在運行時將內(nèi)存頁標(biāo)記為可執(zhí)行,而在 Android 下則可以。

Flutter.framework 對應(yīng)于 Flutter 架構(gòu)中的引擎和嵌入器。實際上,F(xiàn)lutter.framework 位于 flutter 代碼庫的 /bin/cache/artifacts/engine/ios * 中,默認是從谷歌的代碼庫中提取的。當(dāng)需要自定義變更時,可以使用 Ninja 系統(tǒng)下載和構(gòu)建與引擎相關(guān)的代碼。

Flutter 相關(guān)代碼的最終產(chǎn)物是 App.framework(由 Dart 代碼生成)和 Flutter.framework(引擎)。在 Xcode 項目中,Generated.xcconfig 描述了與 Flutter 相關(guān)的環(huán)境配置信息,在 Runner 項目設(shè)置中新增的 xcode_backend.sh 實現(xiàn)了一個副本(從 flutter 框架代碼庫到 Runner 項目根目錄下的 Flutter 目錄)和 Flutter.framework 的嵌入以及 App.framework 的編譯和嵌入。

最終生成的 Runner.app 中與 Flutter 相關(guān)的文件如下:

flutter_assets 是 Flutter 資源,App.framework 和 Flutter.framework 是代碼,位于 Frameworks 目錄中。

在發(fā)布模式下運行

與 Flutter 相關(guān)的渲染、事件和通信處理的邏輯如下:

Dart 主函數(shù)的調(diào)用棧如下:

在調(diào)試模式下編譯

在調(diào)試模式下,F(xiàn)lutter 的編譯結(jié)構(gòu)與發(fā)布模式中的編譯結(jié)構(gòu)類似。差異主要表現(xiàn)在兩點:

1.Flutter.framework

在調(diào)試模式下,框架包含 JIT 支持,而在發(fā)布模式下沒有 JIT 支持。

2.App.framework

與 Flutter.framework 不同,App.framework 是原生機器碼,與 AOT 模式中的 Dart 代碼對應(yīng),而在 JIT 模式下,App.framework 只有幾個簡單的 API,Dart 代碼存在于 snapshot_blob.bin 文件中。這部分代碼的快照是帶有簡單標(biāo)記的源代碼的腳本快照。所有的注釋和空格字符都被移除,常量被規(guī)格化,不存在機器代碼、搖樹優(yōu)化或代碼混淆。

App.framework 中的符號表如下:

針對 Runner.app/flutter_assets/snapshot_blob.bin 上運行 strings 命令可以查看以下內(nèi)容:

調(diào)試模式下主入口的調(diào)用棧如下:

Android 的代碼編譯和執(zhí)行

除了一些與平臺相關(guān)的功能之外,Android 其他邏輯(例如對應(yīng)于 AOT 的發(fā)布模式和對應(yīng)于 JIT 的調(diào)試模式)與 iOS 非常相似,只需注意兩個關(guān)鍵差異。

在發(fā)布模式下編譯

在發(fā)布模式下,Android Flutter 項目中的 Dart 代碼結(jié)構(gòu)如下:

vm/isolate_snapshot_data/instr 是 arm 指令,引擎會在運行時加載它們并將其標(biāo)記為可執(zhí)行。vm_snapshot_data/instr 用于初始化 DartVM,調(diào)用入口為 Dart_Initialize(Dart_api.h)。isolate_snapshot_data/instr 對應(yīng)于創(chuàng)建新隔離的 App 代碼,調(diào)用入口為 Dart_CreateIsolate(Dart_api.h)。

Flutter.jar 類似于 iOS 的 Flutter.framework,包括引擎代碼(Flutter.jar 中的 libflutter.so)以及一組將 Flutter 嵌入到 Android 中的類和接口(FlutterMain、FlutterView、FlutterNativeView 等)。事實上,flutter.jar 位于 Flutter 代碼庫的 /bin/cache/artifacts/engine/android* 中,默認從谷歌代碼庫中拉取。當(dāng)需要自定義更改時,可以使用 Ninja 系統(tǒng)下載引擎源代碼來生成 flutter.jar。

以 isolate_snapshot_data/instr 為例,運行 disarm 命令的結(jié)果如下:

其 APK 結(jié)構(gòu)如下:

在全新安裝 APK 之后,使用時間戳(結(jié)合 versionCode 與 packageinfo 的 lastUpdateTime)來決定是否將 Flutter 相關(guān)文件復(fù)制到本地 app 數(shù)據(jù)目錄。復(fù)制的內(nèi)容如下:

isolate/vm_snapshot_data/instr 最終位于 app 本地數(shù)據(jù)目錄中,該目錄是可寫的。因此,可以通過下載和替換這些快照就可以完成 app 的替換和更新。

在發(fā)布模式下執(zhí)行

下圖顯示了發(fā)布模式下的執(zhí)行流程:

在調(diào)試模式下編譯

與 iOS 的情況一樣,Android 中的調(diào)試模式和發(fā)布模式之間的區(qū)別主要在于以下兩個組件:

1.flutter.jar

這里的區(qū)別與之前針對 iOS 所描述的完全相同。

2.app 代碼

app 代碼位于 flutter_assets 下的 snapshot_blob.bin 中,就像 iOS 一樣。

在介紹完 Flutter 有關(guān) iOS 和 Android 的編譯原理后,我們將重點介紹如何配置 Flutter 及其引擎,以進行自定義和優(yōu)化。因為 Flutter 使用了敏捷開發(fā)模式,所以當(dāng)前出現(xiàn)的問題在未來可能就不是問題。因此,以下部分不著重于如何解決問題,而是著重于不同類型的場景,這些場景體現(xiàn)了 Flutter 自定義和優(yōu)化方面的原則。

在 Flutter 中進行定制和優(yōu)化開發(fā)

Flutter 是一個復(fù)雜的系統(tǒng)。除了上面提到的三層架構(gòu),它還包括 Flutter Android Studio(Intellij)插件、pub 代碼庫管理和其他各種組件。不過,定制和優(yōu)化通常與 Flutter 的工具鏈有關(guān),代碼位于 Flutter 代碼庫的 flutter_tools 包中。我們現(xiàn)在將分別介紹如何針對 Android 和 iOS 進行自定義。

自定義 Android

自定義 Android 涉及 flutter.jar、libflutter.so(在 flutter.jar 中)、gen_snapshot、flutter.gradle 和 flutter_tools。在自定義 Flutter 時,需要注意以下事項:

1. 將 Android 中的目標(biāo)設(shè)置為 armeabi

這是構(gòu)建過程的一部分,邏輯是在 flutter.gradle 中定義的。如果要應(yīng)用程序通過 armeabi 支持 armv7/arm64,必須修改 Flutter 的默認邏輯,如下所示:

由于 Gradle 本身的特性,這部分可以在修改后生效。

2. 將 Android 設(shè)置為在啟動時默認使用第一個可啟動的 Activity

這部分與 flutter_tools 有關(guān),修改如下:

這里的要點不是如何修改它,而是如何讓更改生效。原則上,使用“flutter run/build/analyze/test/upgrade”這樣的命令實際上運行的是 Flutter 腳本(flutter_repo_dir/bin/flutter),然后再通過這個腳本運行 flutter_tools.snapshot(由 packages/flutter_tools 生成)。邏輯如下:

if [[ ! -f "SNAPSHOT_PATH" ]] || [[ ! -s "STAMP_PATH" ]] || [[ "(cat "STAMP_PATH")" != "revision" ]] || [[ "FLUTTER_TOOLS_DIR/pubspec.yaml" -nt "$FLUTTER_TOOLS_DIR/pubspec.lock" ]]; then

rm -f "$FLUTTER_ROOT/version"

touch "$FLUTTER_ROOT/bin/cache/.dartignore"

"$FLUTTER_ROOT/bin/internal/update_dart_sdk.sh"

echo Building flutter tool...

if [[ "$TRAVIS" == "true" ]] || [[ "$BOT" == "true" ]] || [[ "$CONTINUOUS_INTEGRATION" == "true" ]] || [[ "$CHROME_HEADLESS" == "1" ]] || [[ "$APPVEYOR" == "true" ]] || [[ "$CI" == "true" ]]; then

PUB_ENVIRONMENT="$PUB_ENVIRONMENT:flutter_bot"

fi

export PUB_ENVIRONMENT="$PUB_ENVIRONMENT:flutter_install"

if [[ -d "$FLUTTER_ROOT/.pub-cache" ]]; then

export PUB_CACHE="${PUB_CACHE:-"$FLUTTER_ROOT/.pub-cache"}"

fi

while : ; do

cd "$FLUTTER_TOOLS_DIR"

"$PUB" upgrade --verbosity=error --no-packages-dir && break

echo Error: Unable to 'pub upgrade' flutter tool. Retrying in five seconds...

sleep 5

done

"$DART" --snapshot="$SNAPSHOT_PATH" --packages="$FLUTTER_TOOLS_DIR/.packages" "$SCRIPT_PATH"

echo "$revision" > "$STAMP_PATH"

fi

很明顯,如果要重建 flutter_tools,可以刪除 flutter_repo_dir/bin/cache/flutter_tools.stamp(以便重新生成它),或者注釋掉 if/fi(每次都重新生成)。

3. 在調(diào)試模式下發(fā)布 Flutter

如果你發(fā)現(xiàn) Flutter 在開發(fā)中出現(xiàn)延遲,并且猜測這可能是由邏輯或調(diào)試模式引起的,那么可以在發(fā)布模式下構(gòu)建 APK,或者將 Flutter 強制改為為發(fā)布模式,如下所示:

自定義 iOS

與自定義 iOS 相關(guān)的內(nèi)容包括 Flutter.framework、gen_snapshot、xcode_backend.sh 和 flutter_tools。在自定義 Flutter 時,需要注意以下事項:

1. 在優(yōu)化期間重復(fù)替換 Flutter.framework 導(dǎo)致的重新編譯

這部分的邏輯與構(gòu)建有關(guān),位于 xcode_backend.sh 中。為了確保每次都能獲得正確的 Flutter.framework,F(xiàn)lutter 每次都會根據(jù)配置查找并替換 Flutter.framework(請參閱 Generated.xcconfig 配置)。不過,這會導(dǎo)致重新編譯依賴于這個框架的項目代碼。必要的修改如下:

2. 在調(diào)試模式下發(fā)布 Flutter

要進行這個自定義,請將 Generated.xcconfig 中的 FLUTTER_BUILD_MODE 更改為“Release”,將 FLUTTER_FRAMEWORK_DIR 更改為與“Release”對應(yīng)的路徑。

3. 設(shè)置對 armv7 的支持

有關(guān)此方案的原始文檔,請參閱 https://github.com/flutter/engine/wiki/iOS-Builds-Supporting-ARMv7。

事實上,F(xiàn)lutter 本身在 iOS 中支持 armv7,但目前官方還沒有提供支持,因此必須修改相關(guān)邏輯,如下所示:

a. 生成默認邏輯:

Flutter.framework(arm64)

b. 修改 Flutter,以便每次都可以重建 flutter_tools。修改 build_aot.Dart 和 mac.Dart,將 iOS 的相關(guān) arm64 更改為 armv7,并將 gen_snapshot 更改為 i386。

可以通過以下命令生成 i386 的 gen_snapshot:

./flutter/tools/gn --runtime-mode=debug --ios --ios-cpu=arm

ninja -C out/ios_debug_arm

這里有一種隱含的邏輯:

構(gòu)造 gen_snapshot 的預(yù)定義宏(x86_64/__ i386 等)。目標(biāo) gen_snapshot 的結(jié)構(gòu)和最終的 App.framework 結(jié)構(gòu)必須保持一致。也就是說,使用 x86_64->x86_64->arm64 或 i386->i386->armv7。

c. 在 iPhone4S 上,當(dāng) gen_snapshot 生成不受支持的 SDIV 命令時會發(fā)生 EXC_BAD_INSTRUCTION(EXC_ARM_UNDEFINED)錯誤,這可以通過向 gen_snapshot(位于 build_aot.Dart 中)添加參數(shù)“——no-use-integer-division”來解決。其背后的邏輯如下:

d.“l(fā)ipo -create”在 a 步驟和 b 步驟中生成的 Flutter.framework,以便生成支持 armv7 和 arm64 的 Flutter.framework。

e. 修改 Flutter.framework 中的 Info.plist,并刪除:

UIRequiredDeviceCapabilities

arm64

同樣,你必須在 App.framework 上執(zhí)行相同的操作,以避免受到 AppStore 中應(yīng)用程序細化的影響。

調(diào)試 Flutter 工具

在調(diào)試模式下構(gòu)建 APK 時,如果你想知道 Flutter 的特定執(zhí)行邏輯,可以采用以下方法:

a. 了解 flutter_tools 命令的參數(shù)。

b. 將 packages/flutter_tools 作為 Dart 項目打開,并添加新的“Dart Command Line App”配置。將 Dart 文件設(shè)置為“flutter_tools.Dart”,將工作目錄設(shè)置為 Flutter 項目的路徑,并將 Program 參數(shù)設(shè)置為先前獲得的參數(shù)。

自定義和調(diào)試引擎

請考慮以下情形。假設(shè)我們基于 Flutter beta v0.3.1 定制和開發(fā)服務(wù),為了確保穩(wěn)定性,SDK 在某段時間內(nèi)不會升級。同時,F(xiàn)lutter v0.3.1 的 master 分支上修改了一個 bug,標(biāo)記為 fix_bug_commit。你將如何應(yīng)對這種情況?

1.Flutter beta v0.3.1 將其相應(yīng)的引擎代碼提交指定為 09d05a389。

請參閱:flutter/bin/internal/engine.version。

2. 獲取引擎代碼。

3. 由于 master 代碼是在第二步中獲得的,我們需要的是與特定提交相對應(yīng)的代碼(09d05a389),所以需要從這次提交拉取一個新分支:custom_beta_v0.3.1。

4. 在 custom_beta_v0.3.1(commit:09d05a389)上運行“gclient sync”,獲取與 flutter beta v0.3.1 相對應(yīng)的所有引擎代碼。

5. 使用“git cherry-pick fix_bug_commit”將 master 的變更同步到 custom_beta_v0.3.1。如果變更依賴了最新依賴項,則可能會發(fā)生編譯錯誤。

6. 運行以下命令應(yīng)用與 iOS 相關(guān)的變更:

./flutter/tools/gn --runtime-mode=debug --ios --ios-cpu=arm

ninja -C out/ios_debug_arm

./flutter/tools/gn --runtime-mode=release --ios --ios-cpu=arm

ninja -C out/ios_release_arm

./flutter/tools/gn --runtime-mode=profile --ios --ios-cpu=arm

ninja -C out/ios_profile_arm

./flutter/tools/gn --runtime-mode=debug --ios --ios-cpu=arm64

ninja -C out/ios_debug

./flutter/tools/gn --runtime-mode=release --ios --ios-cpu=arm64

ninja -C out/ios_release

./flutter/tools/gn --runtime-mode=profile --ios --ios-cpu=arm64

ninja -C out/ios_profile

要調(diào)試 Flutter.framework 源代碼,請使用以下命令:

./flutter/tools/gn --runtime-mode=debug --unoptimized --ios --ios-cpu=arm64

ninja -C out/ios_debug_unopt

用生成的文件替換 Flutter 中的 Flutter.framework 和 gen_snapshot,這樣就可以調(diào)試引擎源代碼。

7. 最后,運行以下命令應(yīng)用與 Android 相關(guān)的變更:

./flutter/tools/gn --runtime-mode=debug --android --android-cpu=arm

ninja -C out/android_debug

./flutter/tools/gn --runtime-mode=release --android --android-cpu=arm

ninja -C out/android_release

./flutter/tools/gn --runtime-mode=profile --android --android-cpu=arm

ninja -C out/android_profile

你可以使用生成的文件替換 flutter/bin/cache/artifacts/engine/android* 下的 gen_snapshot 和 flutter.jar,以便生成 Android 的 arm 和 debug/release/profile 文件包。


1、具有1-5工作經(jīng)驗的,面對目前流行的技術(shù)不知從何下手,

需要突破技術(shù)瓶頸的可以加。

2、在公司待久了,過得很安逸,

但跳槽時面試碰壁。

需要在短時間內(nèi)進修、跳槽拿高薪的可以加。

3、如果沒有工作經(jīng)驗,但基礎(chǔ)非常扎實,對java工作機制,

常用設(shè)計思想,常用java開發(fā)框架掌握熟練的,可以加。

4、覺得自己很牛B,一般需求都能搞定。

但是所學(xué)的知識點沒有系統(tǒng)化,很難在技術(shù)領(lǐng)域繼續(xù)突破的可以加。

5.?群號:高級架構(gòu)群?Java進階群:180705916備注好信息!

6.阿里Java高級大牛直播講解知識點,分享知識,

多年工作經(jīng)驗的梳理和總結(jié),帶著大家全面、

科學(xué)地建立自己的技術(shù)體系和技術(shù)認知!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,755評論 2 371