MVVM模式在嵌入式GUI AWTK 中的实现

AWTK-MVVM是一套为AWTK用C语言开发,并支持各种脚本语言的MVVM框架,实现了数据绑定、命令绑定和窗口导航等基本功能,使用AWTK-MVVM开发应用程序,无需学习AWTK本身的API,只需学习绑定规则和Model的实现方式即可。

1.1 分离用户界面和业务逻辑

在开发应用程序时,要把用户界面和业务逻辑分离开来,这是每个程序员都知道的常识。分离用户界面和业务逻辑有几个重要的好处:

  1. 有利于隔离变化。用户界面是最容易变化的,易用性的改进,外观的美化和需求的变化,首先冲击的就是用户界面。如果用户界面和业务逻辑耦合到一起,界面上一点微小的改动都会导致一系列代码的修改,这不但会增加开发的成本和周期,而且这种改进也是很无趣的,让程序员的幸福感直线下降。

  2. 有利于自动测试。代码中的BUG,越早被发现,修改的成本越低,而单元测试是在早期发现BUG的重要手段。单元测试是自动化的,前期编写测试代码的投入,会因为后期节省测试的时间而获得回报。单元测试代码可以不断积累,为软件构建一道坚固的防火墙,让整个系统越来越稳定。分离用户界面和业务逻辑是提高代码可测试性的重要手段,让编写单元测试程序成为可能。

  3. 有利于分工合作。用户界面和业务逻辑的耦合,会让用户界面的修改很困难,程序员就会抵触用户界面的改动,让设计师和程序员之间的矛盾加大。分离用户界面和业务逻辑后,设计师的工作成果可以平滑的输出给程序员,让程序员和设计师一起过上共同幸福的生活。

1.2 如何分离用户界面和业务逻辑

分离用户界面和业务逻辑,MVVM是目前最常用的模式。MVVM并非凭空出现,而是由MVC和MVP一路演化而来的,每次演化都是为了解决之前没有解决的问题。

1.2.1 MVC模式

MVC是Model-View-Controller简称,它首次把系统分成Model,View和Controller这三部分,明确的把用户界面和业务逻辑分离开来:

mvc
  • 模型(Model)。简单的说,模型就是业务逻辑。我们经常说面向对象的设计和建模,所建立的模型就是这里的模型,是对现实世界中业务逻辑的抽象。面向对象的建模,就是要找到业务逻辑中有哪些类以及这些类之间的关系。类描述了对象的属性和行为,类是设计时的概念,对象是运行时的概念。对象通常就是业务实体,Model包含一个或多个业务实体。很多人(包括国内一些知名的专家)认为模型就是数据,这是没有透彻理解面向对象的设计导致的,对象是即有数据又有行为的,光有数据没有行为,就无法让对象之间协作,无法共同完成业务逻辑。

  • 视图(View)。这个是大家都知道的,就是用户与软件交互的界面,对于GUI应用程序来讲,就是窗口和对话框,以及上面的各种控件。视图应该说是为用户提供了一个界面,让用户可以观察和操作模型。

  • 控制器(Controller)。控制器负责解释来自用户通过键盘/鼠标等设备的输入,并通知模型和视图做出相应的改变。在GUI应用程序中,直白的说,其实就是控件的事件处理函数,它与界面相关和模型都相关,它从界面获取数据,然后调用模型的函数,有时还会直接更新用户界面。

控制器(Controller)不是业务逻辑,理论上只是很薄的一个胶合层,但是很多新手把业务逻辑写在控制器里,导致模型只剩下数据了,让人产生控制器就是业务逻辑,模型就是数据的错觉。

1.2.2 MVP模式

MVC模式解决分离用户界面和业务逻辑的问题,但是还有一个重要的问题没有解决: 控制器是界面相关的,没有办法为控制器编写单元测试程序。为了解决上面的问题,MVP模式应运而生。MVP是Model-View-Presenter简称。

mvc
  • 模型(Model)。模型还是MVC中的模型,这里不再赘述。

  • 视图(View)。视图还是MVC中的视图,但它不需要向模型注册改变的事件通知了,这个由呈现逻辑去做了。

  • 呈现逻辑(Presenter)。Controller变成了Presenter只是表面现象,最重要的是MVP对视图进行了抽象,呈现逻辑不再向控制器那样直接访问具体的视图了,而是通过视图的接口去访问视图,视图的接口是抽象的,可以有不同的实现,所以可以方便的Mock出一个视图,让编写呈现逻辑的单元测试程序成为可能。注意,这里视图的接口并不是通用的,每个视图都有一个独立的接口,有一个真正的实现和一个用于测试Mock的实现。

从理论上讲,MVP模式已经很完备了,它很好的分离的界面和实现,也可以为呈现逻辑编写单元测试程序了。但是从工程角度来看,MVP却有一个非常致命的缺陷:要编写大量无聊的代码!

为了方便说明,我们以一个编辑图书信息例子,看看要写哪些无聊的代码。这个例子非常简单,却要写不少无聊的代码:

  • 1.初始化的时候,要为控件注册事件处理函数,比如为『保存』按钮注册事件处理函数。

  • 2.初始化的时候,要把图书信息,如书名、作者和出版社等信息从模型中取出来,一个一个的设置到视图的控件中去,在这个过程中,可能需要对数据格式进行转换,比如出版日期,在模型中是一个整数,在视图上表现为一个字符串,这就需要转换。

  • 3.在编辑结束时,要从视图中把图书信息,如书名、作者和出版社等信息一个一个的取出来,再保存到模型中去。在这个过程中,可能需要对数据格式进行转换,比如前面的出版日期,要把视图中的字符串转换回模型中的整数。

  • 4.View都有一个接口定义,有一个真正的实现和一个Mock的实现。

这些代码很简单却很无聊,在每个有界面的模块中,都遵循同样的规律,却又完全不同,不得不重新编写。

1.2.3.MVVM模式

把MVP模式这些规律找出来进行抽象,通过一些规则在视图和模型建立联系,也就是数据绑定和命令绑定,就形成了MVVM模式。MVVM是Model-View-ViewModel简称。在MVVM中:

  • 模型(Model)。模型还是MVC中的模型,这里不再赘述。

  • 视图(View)。视图还是MVC中的视图,但它不需要向模型注册改变的事件通知了,这个由数据绑定去做了。

  • 视图模型(ViewModel)。 ViewModel不是Controller也不是Presenter,视图模型是视图还是模型?按MVVM的发明者John Gossman的话说,视图模型是视图眼中的模型。放大镜下的虫子还是虫子,所以视图眼中的模型还是模型。视图模型与视图没有直接关系,是可以为之编写单元测试的。

呈现逻辑Presenter去哪里了?呈现逻辑从一行行代码变成了一条条数据绑定和命令绑定的规则,对规则的处理和解释成了MVVM框架或公用库,可以在多个项目中共享。

mvvm

MVVM模式有下列好处:

  • 强制分离用户界面和业务逻辑。在MVC和MVP中,不能强制分离用户界面和业务逻辑,程序员的一念之间,就导致用户界面和业务逻辑混在一起。MVVM中不需要编写界面相关的代码,自然没有办法让用户界面和业务逻辑耦合到一起。

  • 界面描述文件和程序代码之间具有更松的耦合。通常用XML或RC文件来描述界面,在MVC和MVP中,在界面上增加一个控件或删除一个控件,都会导致相应的代码需要修改。而在MVVM中,采用了面向意图的编程,界面上表现的只是一种意图,至于在程序中,谁去实现,怎么实现,甚至有没有实现,界面都是不关心的,所以界面和代码之间值的耦合是很松的。

  • 不需要学习GUI的API。通过数据绑定和命令绑定建立视图和模型之间的联系。用声明式的规则取代命令式的代码,不需要写界面相关的代码,所以不需要学习GUI的API,无论是采用Qt还是AWTK,都用同样的方式开发。

视图模型和模型并不相同,之所以要引入视图模型,主要原因有:

  • 模型中的数据有时并不适合直接显示在视图上。比如出版日期,在模型中是整数,直接显示出来,用户就看不懂,需要转换成人类可以理解的字符串。

  • 模型中的一个数据项可能以多种形式呈现在视图上。比如出版日期,除了显示出版日期外,也可能把最近出版的书,显示一个新书标志。

  • 视图中的输入数据和模型中的存储数据,它们的格式有时可能不同。比如出版日期,视图上输入的格式和模型里存储的格式就不一样。

  • 视图需要视图模型提供数据校验规则来判断视图上的输入是否合法。

  • 有些数据是有依赖关系的。比如在输入收货地址时,选择省份时,城市列表跟着变化。

  • 有些状态不属于业务逻辑,但是需要保存下来。如为了支持撤销操作,需要保存命令历史记录。

  • 在C/C++等静态语言中,没有办法通过函数的名称去调用对象的成员函数,也没有办法通过属性的名称去访问对象的成员变量。视图模型需要提供一种机制来实现这些功能,否则绑定规则就没法实现。

MVVM 的缺点。

  • 内存问题。据说WPF内存开销很大,即使在PC上也大得让人无法忍受(常有人借此来反对MVVM)。WEB前端流行的MVVM框架,像React和Vue.js等,采用虚拟DOM的方式,内存需求可能有大幅度增加。

  • 性能问题。MVVM通常的做法是,模型有变化时会刷新整个界面。如果采用虚拟DOM的方式,还需要重新构建一个虚拟DOM,与原来的虚拟DOM比较,性能开销就更大了。

  • 调试问题。如果没有工具的协助,数据绑定会让调试变得困难。

以上这些问题在手机和PC上,已经不是什么大问题了,但对于嵌入式系统来说,仍然是难以跨越的障碍,所以目前几乎没有针对嵌入式平台开发的MVVM框架。

1.3 AWTK-MVVM

AWTK-MVVM是一套用C语言开发的,专门为嵌入式平台优化的MVVM框架。它实现了数据绑定、命令绑定和窗口导航等基本功能,使用AWTK-MVVM开发应用程序,无需学习AWTK本身的API,只需学习绑定规则和模型的实现方式即可。

与其它MVVM框架相比,AWTK-MVVM特点有:

  • 代码小。

    • 核心代码:约3000行。
    • AWTK相关代码:约700行。
    • jerryscript相关代码(可选):约1700行。
  • 性能高。核心代码用C语言开发,其性能与直接使用AWTK差别不大。

  • 内存开销小。

    • 一条绑定规则大概需要150字节。一个窗口上若有10条绑定规则,内存占用了也就1.5K。
    • 列表中的绑定规则可以共享,额外增加的内存开销,比例也是很小的。
  • 隔离更彻底。在AWTK-MVVM中,界面和绑定规则在XML中描述,开发应用程序时只需要开发模型(也就是业务逻辑)即可。除非通过特殊手段,开发者没有机会直接去访问视图。

  • 易调试。

    • 提供视图模型的框架生成工具,避免手写代码造成的错误。
    • 增加了各种异常处理的LOG信息。
    • 提供大量的demo以供参考。
    • 开放源码,调试方便。
  • 支持多语言开发。

    • 目前支持C语言和JS。
    • 以后会根据需要支持新的编程语言。

这里支持的多种编程语言与AWTK的脚本绑定是完全不相关的概念:这里是让应用程序的业务逻辑可以用不同的语言来开发,而AWTK的脚本绑定是让应用程序可以使用不同的脚本语言来调用AWTK的API。

  • 可移植到其它GUI。AWTK-MVVM是为AWTK设计的,但是我们隔离了与AWTK相关的代码,让它可以移植到其它GUI。AWTK相关的代码仅仅700来行,可见移植到新的GUI是非常容易的事情。当然,AWTK可以满足常见的需要,这样做只是给开发者提供更多选择。

AWTK-MVVM也有些限制,目前不支持界面元素动态生成,这时可以结合传统的方法开发。以后AWTK会支持WEB前端流行的框架如Reactjs和Vuejs,在高端平台(如手机、小程序和桌面程序)中应用。

在后面的章节中,我们将详细介绍AWTK-MVVM的具体用法。## 第8章

1.1 分离用户界面和业务逻辑

在开发应用程序时,要把用户界面和业务逻辑分离开来,这是每个程序员都知道的常识。分离用户界面和业务逻辑有几个重要的好处:

  1. 有利于隔离变化。用户界面是最容易变化的,易用性的改进,外观的美化和需求的变化,首先冲击的就是用户界面。如果用户界面和业务逻辑耦合到一起,界面上一点微小的改动都会导致一系列代码的修改,这不但会增加开发的成本和周期,而且这种改进也是很无趣的,让程序员的幸福感直线下降。

  2. 有利于自动测试。代码中的BUG,越早被发现,修改的成本越低,而单元测试是在早期发现BUG的重要手段。单元测试是自动化的,前期编写测试代码的投入,会因为后期节省测试的时间而获得回报。单元测试代码可以不断积累,为软件构建一道坚固的防火墙,让整个系统越来越稳定。分离用户界面和业务逻辑是提高代码可测试性的重要手段,让编写单元测试程序成为可能。

  3. 有利于分工合作。用户界面和业务逻辑的耦合,会让用户界面的修改很困难,程序员就会抵触用户界面的改动,让设计师和程序员之间的矛盾加大。分离用户界面和业务逻辑后,设计师的工作成果可以平滑的输出给程序员,让程序员和设计师一起过上共同幸福的生活。

1.2 如何分离用户界面和业务逻辑

分离用户界面和业务逻辑,MVVM是目前最常用的模式。MVVM并非凭空出现,而是由MVC和MVP一路演化而来的,每次演化都是为了解决之前没有解决的问题。

1.2.1 MVC模式

MVC是Model-View-Controller简称,它首次把系统分成Model,View和Controller这三部分,明确的把用户界面和业务逻辑分离开来:

mvc
  • 模型(Model)。简单的说,模型就是业务逻辑。我们经常说面向对象的设计和建模,所建立的模型就是这里的模型,是对现实世界中业务逻辑的抽象。面向对象的建模,就是要找到业务逻辑中有哪些类以及这些类之间的关系。类描述了对象的属性和行为,类是设计时的概念,对象是运行时的概念。对象通常就是业务实体,Model包含一个或多个业务实体。很多人(包括国内一些知名的专家)认为模型就是数据,这是没有透彻理解面向对象的设计导致的,对象是即有数据又有行为的,光有数据没有行为,就无法让对象之间协作,无法共同完成业务逻辑。

  • 视图(View)。这个是大家都知道的,就是用户与软件交互的界面,对于GUI应用程序来讲,就是窗口和对话框,以及上面的各种控件。视图应该说是为用户提供了一个界面,让用户可以观察和操作模型。

  • 控制器(Controller)。控制器负责解释来自用户通过键盘/鼠标等设备的输入,并通知模型和视图做出相应的改变。在GUI应用程序中,直白的说,其实就是控件的事件处理函数,它与界面相关和模型都相关,它从界面获取数据,然后调用模型的函数,有时还会直接更新用户界面。

控制器(Controller)不是业务逻辑,理论上只是很薄的一个胶合层,但是很多新手把业务逻辑写在控制器里,导致模型只剩下数据了,让人产生控制器就是业务逻辑,模型就是数据的错觉。

1.2.2 MVP模式

MVC模式解决分离用户界面和业务逻辑的问题,但是还有一个重要的问题没有解决: 控制器是界面相关的,没有办法为控制器编写单元测试程序。为了解决上面的问题,MVP模式应运而生。MVP是Model-View-Presenter简称。

mvc
  • 模型(Model)。模型还是MVC中的模型,这里不再赘述。

  • 视图(View)。视图还是MVC中的视图,但它不需要向模型注册改变的事件通知了,这个由呈现逻辑去做了。

  • 呈现逻辑(Presenter)。Controller变成了Presenter只是表面现象,最重要的是MVP对视图进行了抽象,呈现逻辑不再向控制器那样直接访问具体的视图了,而是通过视图的接口去访问视图,视图的接口是抽象的,可以有不同的实现,所以可以方便的Mock出一个视图,让编写呈现逻辑的单元测试程序成为可能。注意,这里视图的接口并不是通用的,每个视图都有一个独立的接口,有一个真正的实现和一个用于测试Mock的实现。

从理论上讲,MVP模式已经很完备了,它很好的分离的界面和实现,也可以为呈现逻辑编写单元测试程序了。但是从工程角度来看,MVP却有一个非常致命的缺陷:要编写大量无聊的代码!

为了方便说明,我们以一个编辑图书信息例子,看看要写哪些无聊的代码。这个例子非常简单,却要写不少无聊的代码:

  • 1.初始化的时候,要为控件注册事件处理函数,比如为『保存』按钮注册事件处理函数。

  • 2.初始化的时候,要把图书信息,如书名、作者和出版社等信息从模型中取出来,一个一个的设置到视图的控件中去,在这个过程中,可能需要对数据格式进行转换,比如出版日期,在模型中是一个整数,在视图上表现为一个字符串,这就需要转换。

  • 3.在编辑结束时,要从视图中把图书信息,如书名、作者和出版社等信息一个一个的取出来,再保存到模型中去。在这个过程中,可能需要对数据格式进行转换,比如前面的出版日期,要把视图中的字符串转换回模型中的整数。

  • 4.View都有一个接口定义,有一个真正的实现和一个Mock的实现。

这些代码很简单却很无聊,在每个有界面的模块中,都遵循同样的规律,却又完全不同,不得不重新编写。

1.2.3.MVVM模式

把MVP模式这些规律找出来进行抽象,通过一些规则在视图和模型建立联系,也就是数据绑定和命令绑定,就形成了MVVM模式。MVVM是Model-View-ViewModel简称。在MVVM中:

  • 模型(Model)。模型还是MVC中的模型,这里不再赘述。

  • 视图(View)。视图还是MVC中的视图,但它不需要向模型注册改变的事件通知了,这个由数据绑定去做了。

  • 视图模型(ViewModel)。 ViewModel不是Controller也不是Presenter,视图模型是视图还是模型?按MVVM的发明者John Gossman的话说,视图模型是视图眼中的模型。放大镜下的虫子还是虫子,所以视图眼中的模型还是模型。视图模型与视图没有直接关系,是可以为之编写单元测试的。

呈现逻辑Presenter去哪里了?呈现逻辑从一行行代码变成了一条条数据绑定和命令绑定的规则,对规则的处理和解释成了MVVM框架或公用库,可以在多个项目中共享。

mvvm

MVVM模式有下列好处:

  • 强制分离用户界面和业务逻辑。在MVC和MVP中,不能强制分离用户界面和业务逻辑,程序员的一念之间,就导致用户界面和业务逻辑混在一起。MVVM中不需要编写界面相关的代码,自然没有办法让用户界面和业务逻辑耦合到一起。

  • 界面描述文件和程序代码之间具有更松的耦合。通常用XML或RC文件来描述界面,在MVC和MVP中,在界面上增加一个控件或删除一个控件,都会导致相应的代码需要修改。而在MVVM中,采用了面向意图的编程,界面上表现的只是一种意图,至于在程序中,谁去实现,怎么实现,甚至有没有实现,界面都是不关心的,所以界面和代码之间值的耦合是很松的。

  • 不需要学习GUI的API。通过数据绑定和命令绑定建立视图和模型之间的联系。用声明式的规则取代命令式的代码,不需要写界面相关的代码,所以不需要学习GUI的API,无论是采用Qt还是AWTK,都用同样的方式开发。

视图模型和模型并不相同,之所以要引入视图模型,主要原因有:

  • 模型中的数据有时并不适合直接显示在视图上。比如出版日期,在模型中是整数,直接显示出来,用户就看不懂,需要转换成人类可以理解的字符串。

  • 模型中的一个数据项可能以多种形式呈现在视图上。比如出版日期,除了显示出版日期外,也可能把最近出版的书,显示一个新书标志。

  • 视图中的输入数据和模型中的存储数据,它们的格式有时可能不同。比如出版日期,视图上输入的格式和模型里存储的格式就不一样。

  • 视图需要视图模型提供数据校验规则来判断视图上的输入是否合法。

  • 有些数据是有依赖关系的。比如在输入收货地址时,选择省份时,城市列表跟着变化。

  • 有些状态不属于业务逻辑,但是需要保存下来。如为了支持撤销操作,需要保存命令历史记录。

  • 在C/C++等静态语言中,没有办法通过函数的名称去调用对象的成员函数,也没有办法通过属性的名称去访问对象的成员变量。视图模型需要提供一种机制来实现这些功能,否则绑定规则就没法实现。

MVVM 的缺点。

  • 内存问题。据说WPF内存开销很大,即使在PC上也大得让人无法忍受(常有人借此来反对MVVM)。WEB前端流行的MVVM框架,像React和Vue.js等,采用虚拟DOM的方式,内存需求可能有大幅度增加。

  • 性能问题。MVVM通常的做法是,模型有变化时会刷新整个界面。如果采用虚拟DOM的方式,还需要重新构建一个虚拟DOM,与原来的虚拟DOM比较,性能开销就更大了。

  • 调试问题。如果没有工具的协助,数据绑定会让调试变得困难。

以上这些问题在手机和PC上,已经不是什么大问题了,但对于嵌入式系统来说,仍然是难以跨越的障碍,所以目前几乎没有针对嵌入式平台开发的MVVM框架。

1.3 AWTK-MVVM

AWTK-MVVM是一套用C语言开发的,专门为嵌入式平台优化的MVVM框架。它实现了数据绑定、命令绑定和窗口导航等基本功能,使用AWTK-MVVM开发应用程序,无需学习AWTK本身的API,只需学习绑定规则和模型的实现方式即可。

与其它MVVM框架相比,AWTK-MVVM特点有:

  • 代码小。

    • 核心代码:约3000行。
    • AWTK相关代码:约700行。
    • jerryscript相关代码(可选):约1700行。
  • 性能高。核心代码用C语言开发,其性能与直接使用AWTK差别不大。

  • 内存开销小。

    • 一条绑定规则大概需要150字节。一个窗口上若有10条绑定规则,内存占用了也就1.5K。
    • 列表中的绑定规则可以共享,额外增加的内存开销,比例也是很小的。
  • 隔离更彻底。在AWTK-MVVM中,界面和绑定规则在XML中描述,开发应用程序时只需要开发模型(也就是业务逻辑)即可。除非通过特殊手段,开发者没有机会直接去访问视图。

  • 易调试。

    • 提供视图模型的框架生成工具,避免手写代码造成的错误。
    • 增加了各种异常处理的LOG信息。
    • 提供大量的demo以供参考。
    • 开放源码,调试方便。
  • 支持多语言开发。

    • 目前支持C语言和JS。
    • 以后会根据需要支持新的编程语言。

这里支持的多种编程语言与AWTK的脚本绑定是完全不相关的概念:这里是让应用程序的业务逻辑可以用不同的语言来开发,而AWTK的脚本绑定是让应用程序可以使用不同的脚本语言来调用AWTK的API。

  • 可移植到其它GUI。AWTK-MVVM是为AWTK设计的,但是我们隔离了与AWTK相关的代码,让它可以移植到其它GUI。AWTK相关的代码仅仅700来行,可见移植到新的GUI是非常容易的事情。当然,AWTK可以满足常见的需要,这样做只是给开发者提供更多选择。

AWTK-MVVM也有些限制,目前不支持界面元素动态生成,这时可以结合传统的方法开发。以后AWTK会支持WEB前端流行的框架如Reactjs和Vuejs,在高端平台(如手机、小程序和桌面程序)中应用。

在后面的章节中,我们将详细介绍AWTK-MVVM的具体用法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容