Flutter 插件 百度地圖Android插件演示

2020-05-13 17:33:34
Flutter翰守,百度地圖,原生插件

簡(jiǎn)介

先介紹一下蜡峰,這個(gè)插件只是教學(xué)目的,并不完整湿颅。
目前提供了

  • 顯示地圖
  • 獲取地圖中心坐標(biāo)
  • 定位
  • 距離計(jì)算

寫(xiě)這個(gè)插件的時(shí)候载绿,
我只學(xué)了 Flutter 一個(gè)月,Android 沒(méi)學(xué)過(guò)油航,也沒(méi)掌握 Java 崭庸。

項(xiàng)目地址

沒(méi)想到文章越寫(xiě)越多,分成了3部分谊囚,這是最后一部分怕享,
前2部分鏈接:

地圖插件的關(guān)鍵是 Flutter 端的 AndroidView 和 Android 端的 PlatformView 。

Flutter 如何顯示 Android 視圖

要想讓 Flutter 顯示一個(gè)安卓視圖 镰踏。

  1. 安卓端需要提供一個(gè) PlatformView 來(lái)生成安卓視圖函筋,即 View
  2. PlatformView 需要用 PlatformViewFactory 來(lái)創(chuàng)建奠伪。
  3. 最后通過(guò) PlatformViewRegistry 登記 PlatformViewFactory 跌帐。
  4. 在Flutter 端用一個(gè) AndroidView 去請(qǐng)求 Android 端的 PlatformView 。

Flutter 端

AndroidView 常用的參數(shù):

  • viewType
    一個(gè)字符串绊率,指定 Android 端登記的某個(gè)視圖谨敛。就像 MethodChannel 的 name 參數(shù)一樣。
  • onPlatformViewCreated
    一個(gè)回調(diào)函數(shù)滤否,在安卓端的視圖準(zhǔn)備好后調(diào)用脸狸。接受一個(gè) int 型參數(shù)。
  • creationParams
    傳遞給安卓端的參數(shù)顽聂。類(lèi)型為 dynamic 肥惭《⒁牵可選紊搪。
  • creationParamsCodec
    給 creationParams 編碼的編碼器,一般是 StandardMessageCodec 全景。如果指定了 creationParams 耀石,這個(gè)不能為空 。

Flutter 請(qǐng)求安卓視圖的時(shí)候,會(huì)傳遞以下參數(shù):

  • id 這個(gè) id 從0開(kāi)始滞伟,每次 AndroidView 重建后會(huì)加一揭鳞。重建前會(huì)檢查 viewType 和之前的是否相同。
  • viewType
  • width
  • height
  • direction
  • params 如果 AndroidView 的 creationParams 不會(huì)空梆奈,會(huì)加上這個(gè)野崇。

向安卓發(fā)送請(qǐng)求,就是通過(guò)我們熟悉的 MethodChannel 亩钟。
安卓端會(huì)返回一個(gè) textureId 供 TextureLayer 使用乓梨。

Android 端

Android 端收到請(qǐng)求后,

  • 會(huì)在已登記的 PlatformViewFactory 中找到和傳過(guò)來(lái)的 viewType 對(duì)應(yīng)的那個(gè)清酥。
    有了 PlatformViewFactory 扶镀,就可以得到我們的地圖了。

  • 還會(huì)創(chuàng)建一個(gè) SurfaceTexture 焰轻,并給它分配一個(gè) ID 臭觉。這個(gè) ID 也是從 0 開(kāi)始,每次加一辱志。
    這個(gè) ID 將作為 Flutter 端請(qǐng)求的結(jié)果返回蝠筑。

看網(wǎng)上的文章,有看到過(guò) GPU 中的紋理 ID 揩懒。會(huì)是這個(gè)從0開(kāi)始的 ID 嗎菱肖?
有了這個(gè) ID ,F(xiàn)lutter 就可以直接顯示 Android 渲染的視圖旭从,不需要經(jīng)過(guò) CPU 傳數(shù)據(jù)了嗎稳强?
不去研究了。

感覺(jué) SurfaceTexture 和 View 應(yīng)該有著什么關(guān)系退疫。
不過(guò)目前沒(méi)發(fā)現(xiàn)鸽素,也沒(méi)覺(jué)得需要知道。

下面從登記開(kāi)始馍忽,說(shuō)一下需要知道的。

PlatformViewRegistry

要想登記 PlatformViewFactory 坝冕,
需要用到 PlatformViewRegistryregisterViewFactory() 方法瓦呼。

PlatformViewRegistry 是在應(yīng)用啟動(dòng)后,創(chuàng)建 FlutterEngine 時(shí)創(chuàng)建的磨澡。
怎樣得到它?

  • 如果我們可以直接得到 FlutterEngine 稳摄。
    例如在 FlutterActivity 的 configureFlutterEngine() 方法中。
    那么可以先用 FlutterEngine 的 getPlatformViewsController() 方法
    得到 PlatformViewsController 尉共。
    然后用 PlatformViewsController 的 getRegistry() 得到 PlatformViewRegistry 弃锐。

  • 如果我們?cè)趯?xiě)插件,可以在 onAttachedToEngine()
    通過(guò) FlutterPluginBindinggetPlatformViewRegistry() 獲得剧蚣。

PlatformViewRegistry 的 registerViewFactory() 方法接受2個(gè)參數(shù)旋廷,

  • 第一個(gè)是 viewType ,字符串類(lèi)型目尖。是一個(gè)唯一的標(biāo)識(shí)符,用來(lái)標(biāo)識(shí)一個(gè) PlatformViewFactory 瑟曲。
  • 第二個(gè)是 PlatformViewFactory 豪治。用來(lái)創(chuàng)建 PlatformView 。

它有個(gè) boolean 類(lèi)型的返回值烦衣。
登記的時(shí)候會(huì)通過(guò) viewType 檢查是否已經(jīng)登記過(guò)掩浙,
如果登記過(guò)返回 false,否則返回 true 厨姚。

PlatformViewFactory

創(chuàng)建 PlatformViewFactory 需要指定一個(gè)解碼器,用于解碼 Flutter 端傳遞過(guò)來(lái)的參數(shù)矾麻。
這個(gè)解碼器要與 Flutter 端的編碼器對(duì)應(yīng)芭梯,一般是 StandardMessageCodec

我們需要重寫(xiě) create() 方法甩牺。
有3個(gè)可用的參數(shù):

  1. context
    最開(kāi)始是個(gè) FlutterActivity 累奈,經(jīng)過(guò)層層包裝,最后是一個(gè) ContextWrapper 澎媒。
    包含了 InputMethodManager ,InvocationHandler戒努,WindowManager, 重寫(xiě)了 getSystemService() ,
    這些我都不懂。
  2. viewId
    Flutter 端傳遞過(guò)來(lái)的 id 侍筛。
  3. args
    解碼過(guò)的 Flutter 端傳遞過(guò)來(lái)的參數(shù)

需要返回一個(gè) PlatformView

PlatformView

需要重寫(xiě)2個(gè)方法:

  • getView() 返回一個(gè) View 撒穷,就是我們的百度地圖啦。
  • dispose() 做些清理工作禽笑。

當(dāng) Flutter 端的 AndroidView 被 dispose 或重建前,會(huì)調(diào)用這里的 dispose() 蒲每。

下面獻(xiàn)上一個(gè)實(shí)例喻括。

新建一個(gè) Flutter 插件項(xiàng)目

我這里項(xiàng)目名字是 flutter_plugin_demo
特定平臺(tái)的包名是 xyz.waixingjiandie.flutter_plugin_demo望蜡。

新項(xiàng)目的目錄結(jié)構(gòu)

插件的 pubspec.yaml

# /pubspec.yaml
name: flutter_plugin_demo
flutter:
  plugin:
    platforms:
      android:
        package: xyz.waixingjiandie.flutter_plugin_demo
        pluginClass: FlutterPluginDemoPlugin # 指定了 Android 插件的類(lèi)名字
      ios:
        pluginClass: FlutterPluginDemoPlugin

/lib/flutter_plugin_demo.dart
這個(gè)是插件的API,對(duì)應(yīng) “app-facing package”脖律。

/android/
這個(gè)目錄是Android平臺(tái)的代碼腕侄,對(duì)應(yīng) “platform package”芦疏。

/example/
這里是一個(gè)依賴(lài)我們插件的 Flutter 應(yīng)用示例酸茴,向使用者展示如何使用我們的插件。
這個(gè)示例是可以直接運(yùn)行的兢交,官網(wǎng)建議編寫(xiě)插件前,先運(yùn)行一遍配喳。

打開(kāi)Android模塊

Writing custom platform-specific code 這個(gè)不是插件的教程中。
打開(kāi)的路徑是 /android/ 被济。
Developing packages & plugins 這個(gè)插件教程中涧团,
打開(kāi)的是 /example/android/

沒(méi)時(shí)間學(xué)太多 Android 知識(shí)少欺,就不管為什么了。
如果打開(kāi)的是 /android/ 畏陕,會(huì)發(fā)現(xiàn)安卓代碼中導(dǎo)入的 Flutter 包無(wú)效仿滔。
打開(kāi) /example/android/ 的話,也可以訪問(wèn) /android/ 鞠绰,并且 Flutter 包也沒(méi)問(wèn)題。

在我們的項(xiàng)目上右鍵飒焦,選擇 Flutter -> Open Android module in Android Studio 。
打開(kāi)的項(xiàng)目和選擇 /example/android/ 是一樣的牺荠。
那我就以這種方式打開(kāi)安卓模塊吧翁巍。

打開(kāi)后灶壶,我們要面向的路徑是 /android/杈曲。

添加百度地圖SDK

根據(jù)百度地圖文檔 將地圖 SDK 添加到項(xiàng)目中胸懈。

刪掉多余的東西

打開(kāi) FlutterPluginDemoPlugin 類(lèi)所在的文件恰响。

示例插件實(shí)現(xiàn)了 FlutterPluginMethodCallHandler
之所以實(shí)現(xiàn) MethodCallHandler 是因?yàn)槭纠绦蛴玫搅嗽椒ā?br> 為了兼容羔挡,類(lèi)中還有個(gè)多余的 registerWith() 方法间唉。

把多余的去掉后:

package xyz.waixingjiandie.flutter_plugin_demo;

import androidx.annotation.NonNull;

import io.flutter.embedding.engine.plugins.FlutterPlugin;

public class FlutterPluginDemoPlugin implements FlutterPlugin {
  
  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {}

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {}
}

參考 百度地圖文檔 ,要顯示地圖是需要 Activity 的利术。
那么我們的插件就要實(shí)現(xiàn) ActivityAware 。

實(shí)現(xiàn) ActivityAware

public class FlutterPluginDemoPlugin implements FlutterPlugin, ActivityAware {

  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
  }

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
  }

  @Override
  public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {

  }

  @Override
  public void onDetachedFromActivityForConfigChanges() {

  }

  @Override
  public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {

  }

  @Override
  public void onDetachedFromActivity() {

  }
}

我們的插件又多了4個(gè)要重寫(xiě)的方法被冒。

初始化地圖 SDK

要在哪里初始化 SDK 呢轮蜕?

重寫(xiě)的6個(gè)方法中,
onAttachedToEngine() 是在 FlutterPlugin 關(guān)聯(lián)到 FlutterEngine 時(shí)調(diào)用的跃洛。
發(fā)生在登記插件時(shí) PluginRegistry 的 add() 方法中。

FlutterPlugin 關(guān)聯(lián)到 FlutterEngine 后葱蝗,
會(huì)檢查這個(gè) FlutterPlugin 是否實(shí)現(xiàn)了 ActivityAware 细燎。
如果是,再看 FlutterEngine 是否關(guān)聯(lián)了一個(gè) Activity 玻驻。
如果是,那么執(zhí)行 ActivityAware 中的 onAttachedToActivity() 佛析。

有2個(gè)檢查彪蓬,由于我實(shí)現(xiàn)了 ActivityAware ,所以第一個(gè)檢查通過(guò)档冬。
對(duì)于第2個(gè)桃纯,要從應(yīng)用的啟動(dòng)說(shuō)起披坏。
啟動(dòng)一個(gè)安卓應(yīng)用,啟動(dòng)的通常是一個(gè) Activity 棒拂。
我們 /example/ 中的示例是可以啟動(dòng)的。
從示例中可以看到這個(gè) Activity 是 FlutterActivity 谜诫。
如果沒(méi)有重寫(xiě) FlutterActivity 的 shouldAttachEngineToActivity() 方法的話攻旦,
第二個(gè)檢查也是通過(guò)的。

6個(gè)方法中且预,這2個(gè)就是最先執(zhí)行的2個(gè),
onAttachedToEngine() 先于 onAttachedToActivity() 锋谐,
但是不能獲得 Activity 截酷。

初始化地圖 SDK 是不需要 Activity 的。需要的是 ApplicationContext 多搀。
放在哪里都行灾部,但是在 onAttachedToEngine() 獲取 ApplicationContext 方便點(diǎn)。

初始化代碼

  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    // 在使用SDK各組件之前初始化context信息赌髓,傳入ApplicationContext
    SDKInitializer.initialize(flutterPluginBinding.getApplicationContext());
    // 自4.3.0起,百度地圖SDK所有接口均支持百度坐標(biāo)和國(guó)測(cè)局坐標(biāo)夷野,用此方法設(shè)置您使用的坐標(biāo)類(lèi)型.
    // 包括BD09LL和GCJ02兩種坐標(biāo)悯搔,默認(rèn)是BD09LL坐標(biāo)。
    SDKInitializer.setCoordType(CoordType.BD09LL);
  }

通過(guò) FlutterPluginBinding 的 getApplicationContext() 就能得到 ApplicationContext 妒貌。
如果在 onAttachedToActivity() 初始化,
可以用 ActivityPluginBinding 的 getActivity() 得到 Activity 后灌曙,
在用 Activity 的 getApplicationContext() 得到 ApplicationContext 。

SDK 初始化后逆害,創(chuàng)建一個(gè)用于顯示地圖的對(duì)象蚣驼。

PlatformView

PlatformView 需要返回一個(gè) View 隙姿,這個(gè) View 是由百度地圖 SDK 創(chuàng)建的厂捞。
查看百度地圖文檔,
地圖容器分為 GLSurfaceView 和 TextureView 兩種靡馁。

  • GLSurfaceView
    包括 MapView,MapFragment 和 SupportMapFragment 三種容器臭墨。
  • TextureView
    包括 TexureMapView,TextureMapFragment 和 TextureSupportMapFragment 三種容器尤误。

好像 TextureView 更好一點(diǎn)结缚,我選擇 TexureMapView 。

public class BaiduMapControler implements PlatformView {
  private final TextureMapView mapView;

  public BaiduMapControler(Activity activity) {
    this.mapView = new TextureMapView(activity);
  }

  @Override
  public View getView() {
    return mapView;
  }

  @Override
  public void dispose() {

  }
}

PlatformViewFactory

public class BaiduMapFactory extends PlatformViewFactory {
  private final Activity activity;

  public BaiduMapFactory(Activity activity) {
    super(StandardMessageCodec.INSTANCE);
    this.activity = activity;
  }

  @Override
  public PlatformView create(Context context, int viewId, Object args) {
    return new BaiduMapControler(activity);
  }
}

登記 PlatformViewFactory

因?yàn)榘俣鹊貓D需要 Activity 红竭,所以在 onAttachedToActivity() 中登記茵宪。

public class FlutterPluginDemoPlugin implements FlutterPlugin, ActivityAware {
  // 為了在 onAttachedToActivity() 中得到 PlatformViewRegistry
  private FlutterPluginBinding pluginBinding;

  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    pluginBinding = flutterPluginBinding;
  }

  @Override
  public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
    pluginBinding.getPlatformViewRegistry().registerViewFactory(
        "baidu_map",
        new BaiduMapFactory(binding.getActivity()));
  }
}

在 Flutter 端顯示百度地圖

/lib/flutter_plugin_demo.dart

export 'src/baidu_map.dart';

/lib/src/baidu_map.dart

import 'package:flutter/material.dart';

class BaiduMapView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return AndroidView(
      viewType: "baidu_map",
    );
  }
}

/example/lib/main.dart

import 'package:flutter/material.dart';

import 'package:flutter_plugin_demo/flutter_plugin_demo.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child: BaiduMapView(),
        ),
      ),
    );
  }
}

運(yùn)行后應(yīng)該就可以看到地圖了稀火,如果只能看到格子,需要從百度獲取開(kāi)發(fā)密鑰(AK)凰狞。

在百度地圖官網(wǎng)文檔中:

類(lèi) TextureMapView
使用這個(gè)類(lèi)必須按照它的生命周期進(jìn)行操控钦听,你必須參照以下方法onCreate(Bundle)阀蒂、 onResume()褂始、onPause()痴突、onDestroy()原押。等聲明周期函數(shù)圾另。在使用地圖組件之前請(qǐng)確保已經(jīng)調(diào)用了 SDKInitializer.initialize(Context) 函數(shù)以提供全局 Context 信息。

所以要想辦法獲取生命周期雕沉。

地圖生命周期

和生命周期有關(guān)的方法在 Activity 中集乔,要重寫(xiě)里面的方法好像有點(diǎn)不太方便。
向谷歌地圖插件學(xué)習(xí)坡椒,讓我們的 BaiduMapControler 再實(shí)現(xiàn)一個(gè) DefaultLifecycleObserver 扰路。

public class BaiduMapControler implements PlatformView, DefaultLifecycleObserver {
  private final TextureMapView mapView;

  @Override
  public View getView() {
    return mapView;
  }

  @Override
  public void dispose() {

  }

  @Override
  public void onCreate(@NonNull LifecycleOwner owner) {
  }

  @Override
  public void onStart(@NonNull LifecycleOwner owner) {
  }

  @Override
  public void onResume(@NonNull LifecycleOwner owner) {
  }

  @Override
  public void onPause(@NonNull LifecycleOwner owner) {
  }

  @Override
  public void onStop(@NonNull LifecycleOwner owner) {
  }

  @Override
  public void onDestroy(@NonNull LifecycleOwner owner) {
  }
}

多出的的方法正是和生命周期相關(guān)的。

DefaultLifecycleObserver 是什么呢倔叼?

DefaultLifecycleObserver

它可以作為 Activity 生命周期狀態(tài)的觀察者汗唱。
當(dāng) Activity 生命周期變化時(shí),會(huì)調(diào)用觀察者中相應(yīng)的方法丈攒。

生命周期的觀察者被保存在 Lifecycle 類(lèi)中哩罪。

要觀察某個(gè) Activity 的生命周期,就要把觀察者添加到 Activity 所擁有的 Lifecycle 中巡验。

獲取 Lifecycle

方法1

之前我們使用 ActivityPluginBindinggetActivity() 獲取了 Activity 际插。
它也有個(gè) getLifecycle() 方法用來(lái)獲取 Lifecycle ,不過(guò)返回的類(lèi)型是 Object 显设。
如果把它強(qiáng)制轉(zhuǎn)換成 Lifecycle 框弛。
會(huì)出現(xiàn)異常:
“io.flutter.embedding.engine.plugins.lifecycle.HiddenLifecycleReference cannot be cast to androidx.lifecycle.Lifecycle”。

看下 HiddenLifecycleReference 的文檔:

An Object that can be used to obtain a Lifecycle reference.

DO NOT USE THIS CLASS IN AN APP OR A PLUGIN.

This class is used by the flutter_android_lifecycle package to provide access to a Lifecycle in a way that makes it easier for Flutter and the Flutter plugin ecosystem to handle breaking changes in Lifecycle libraries.

特別強(qiáng)調(diào)了不讓用敷硅。說(shuō)是給“flutter_android_lifecycle”這個(gè)包用的。
谷歌地圖插件好像就用了那個(gè)包力奋。

看了那個(gè)包的代碼,實(shí)現(xiàn)是很簡(jiǎn)單的猿挚。
所以可行的方法是這樣:

  @Override
  public void onAttachedToActivity(ActivityPluginBinding binding) {
    //lifecycle = (Lifecycle) binding.getLifecycle();
    HiddenLifecycleReference reference = (HiddenLifecycleReference) binding.getLifecycle();
    lifecycle = reference.getLifecycle();
  }

方法2

FlutterActivity 有個(gè) getLifecycle() 方法是可以返回 Lifecycle 的铣墨。
我們傳遞給 BaiduMapControler 的 Activity ,其實(shí)就是 FlutterActivity 屡律。

下面是使用方法2的例子超埋。

最終的代碼

public class BaiduMapControler implements PlatformView, DefaultLifecycleObserver {
  private static final String TAG = "BaiduMapController";

  private final TextureMapView mapView;

  public BaiduMapControler(FlutterActivity activity) {
    this.mapView = new TextureMapView(activity);
    activity.getLifecycle().addObserver(this);
  }

  @Override
  public View getView() {
    return mapView;
  }

  @Override
  public void dispose() {

  }

  @Override
  public void onCreate(@NonNull LifecycleOwner owner) {
    Log.d(TAG, "onCreate");
    mapView.onCreate(null, null);
  }

  @Override
  public void onStart(@NonNull LifecycleOwner owner) {
    Log.d(TAG, "onStart");
  }

  @Override
  public void onResume(@NonNull LifecycleOwner owner) {
    Log.d(TAG, "onResume");
    mapView.onResume();
  }

  @Override
  public void onPause(@NonNull LifecycleOwner owner) {
    Log.d(TAG, "onPause");
    mapView.onPause();
  }

  @Override
  public void onStop(@NonNull LifecycleOwner owner) {
    Log.d(TAG, "onStop");
  }

  @Override
  public void onDestroy(@NonNull LifecycleOwner owner) {
    Log.d(TAG, "onDestroy");
    mapView.onDestroy();
  }
}

參考資料

插件

紋理

Android 生命周期

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吓笙,一起剝皮案震驚了整個(gè)濱河市絮蒿,隨后出現(xiàn)的幾起案子土涝,更是在濱河造成了極大的恐慌但壮,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異博杖,居然都是意外死亡剃根,警方通過(guò)查閱死者的電腦和手機(jī)跟继,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)莺匠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)摇庙,“玉大人卫袒,你說(shuō)我怎么就攤上這事夕凝。” “怎么了转砖?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵汞窗,是天一觀的道長(zhǎng)杉辙。 經(jīng)常有香客問(wèn)我综看,道長(zhǎng)红碑,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮奕剃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己聂薪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布扑馁。 她就那樣靜靜地躺著复罐,像睡著了一般效诅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼七芭。 笑死锌历,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播扇丛,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了曼振?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤集索,失蹤者是張志新(化名)和其女友劉穎务荆,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蚪黑,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡忌穿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年朴译,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了盯拱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坟乾。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡详羡,死狀恐怖蟹漓,靈堂內(nèi)的尸體忽然破棺而出炕横,到底是詐尸還是另有隱情,我是刑警寧澤葡粒,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布份殿,位于F島的核電站,受9級(jí)特大地震影響嗽交,放射性物質(zhì)發(fā)生泄漏卿嘲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一夫壁、第九天 我趴在偏房一處隱蔽的房頂上張望拾枣。 院中可真熱鬧,春花似錦掌唾、人聲如沸放前。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)凭语。三九已至,卻和暖如春撩扒,著一層夾襖步出監(jiān)牢的瞬間似扔,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工搓谆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炒辉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓泉手,卻偏偏與公主長(zhǎng)得像黔寇,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子斩萌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359