自我提升(基础技术篇)——消息传递模块(广播,intent,handler)intent篇

前言:接上一篇广播讲后面的,intent

Intent

概述

Android中的Intent是一个类(就是一个封装类许多方法的类,不要想的那么神秘),可以用来在一个组件中启动App中的另一个组件或者是启动另一个App的组件,这里所说的组件指的是Activity、Service以及Broadcast。(这些组件不知道的,自行百度哈)

Intent的作用(其实就是intent这个类的功能)

 启动Activity:可以将Intent对象传递给startActivity()方法或startActivityForResult()方法以启动一个Activity,该Intent对象包含了要启动的Activity的信息及其他必要的数据。

回传Activity的数据:startActivityForResult和onactivityresult的配合使用,可以得到上一个activity中传回的数据(后面详细介绍)

 启动Service:可以将Intent对象传递给startService()方法或bindService()方法以启动一个Service,该Intent对象包含了要启动的Service的信息及其他必要的数据。

 发送广播:广播是一种所有App都可以接收的信息。android系统会发布各种类型的广播,比如发布开机广播或手机充电广播等。我们也可以给其他的App发送广播,可以将Intent对象传递给sendBroadcast()方法或sendOrderedBroadcast()方法或sendStickyBroadcast()方法以发送自定义广播。(如果不知道广播,自行看上一篇)

Intent的类型

显式的Intent

如果Intent中明确包含了要启动的组件的完整类名(包名及类名),那么这个Intent就是explict的,即显式的。使用显式Intent最典型的情形是在你自己的App中启动一个组件,因为你自己肯定知道自己的要启动的组件的类名。比如,为了响应用户操作通过显式的Intent在你的App中启动一个Activity或启动一个Service下载文件。

当创建了一个显式Intent去启动Activity或Service的时候,系统会立即启动Intent中所指定的组件。

隐式的Intent

如果Intent没有包含要启动的组件的完整类名,那么这个Intent就是implict的,即隐式的。虽然隐式的Intent没有指定要启动的组件的类名,但是一般情况下,隐式的Intent都要指定需要执行的action。一般,隐式的Intent只用在当我们想在自己的App中通过Intent启动另一个App的组件的时候,让另一个App的组件接收并处理该Intent。例如,你想在地图上给用户显示一个位置,但是你的App又不支持地图展示,这时候你可以将位置信息放入到一个Intent中,然后给它指定相应的action,通过这样隐式的Intent请求其他的地图型的App(例如Google Map、百度地图等)来在地图中展示一个指定的位置。隐式的Intent也体现了Android的一种设计哲学我自己的App无需包罗万象所有功能,可以通过与其他App组合起来,给用户提供很好的用户体验。而连接自己的App与其他App的纽带就是隐式Intent。(这点很重要,要学会用别人的资源为自己服务)

当创建了一个隐式Intent去使用的时候,Android系统会将该隐式Intent所包含的信息与设备上其他所有App中manifest文件中注册的组件的Intent Filters进行对比过滤,从中找出满足能够接收处理该隐式Intent的App和对应的组件。如果有多个App中的某个组件都符合条件,那么Android会弹出一个对话框让用户选择需要启动哪个App。(这里画重点,有些同学可能还不知道吧)

Intent过滤器

Intent Filter,一个组件可以包含0个或多个Intent Filter。Intent Filter是写在App的manifest文件中的,其通过设置action或uri数据类型等指明了组件能够处理接收的Intent的类型。如果你给你的Activity设置了Intent Filter,那么这就使得其他的App有可能通过隐式Intent启动你的这个Activity。反之,如果你的Activity不包含任何Intent Filter,那么该Activity只能通过显式Intent启动,由于我们一般不会暴露出我们组件的完整类名,所以这种情况下,其他的App基本就不可能通过Intent启动我们的Activity了(因为他们不知道该Activity的完整类名),只能由我们自己的App通过显式Intent启动。

注意:为了确保应用的安全性,启动service时,请始终使用显式 Intent,且不要为服务声明 Intent 过滤器。使用隐式 Intent 启动服务存在安全隐患,因为您无法确定哪些服务将响应 Intent,且用户无法看到哪些服务已启动。从 Android 5.0(API 级别 21)开始,如果使用隐式 Intent 调用bindService(),系统会引发异常。

前面都是基本介绍,下面,我们来详细聊聊intent的构建(当然,这个构建是比较完整的,这里是基本参考官方的)

Intent构建(包含组成)

intent对象携带了 Android 系统用来确定要启动哪个组件的信息(例如,准确的组件名称或应当接收该 Intent 的组件类别),以及收件人组件为了正确执行操作而使用的信息(例如,要采取的操作以及要处理的数据)。

intent中包含的主要信息如下:

组件名称(Component Name)

要启动的组件名称。

这是可选项,但也是构建显式Intent 的一项重要信息,这意味着 Intent 应当仅传递给由组件名称定义的应用组件。 如果没有组件名称,则 Intent 是隐式的,且系统将根据其他 Intent 信息(例如,以下所述的操作、数据和类别)决定哪个组件应当接收 Intent。 因此,如需在应用中启动特定的组件,则应指定该组件的名称。

注:启动Service时,您应始终指定组件名称。 否则,您无法确定哪项服务会响应 Intent,且用户无法看到哪项服务已启动。

Intetn的这一字段是一个ComponentName对象,您可以使用目标组件的完全限定类名指定此对象,其中包括应用的软件包名称。 例如,com.example.ExampleActivity。您可以使用setComponent()、setClass()、setClassName()或Intent构造函数设置组件名称。

操作( Action )

指定要执行的通用操作(例如,“查看”或“选取”)的字符串。

对于广播 Intent,这是指已发生且正在报告的操作。操作在很大程度上决定了其余 Intent 的构成,特别是数据和 extra 中包含的内容。

您可以指定自己的操作,供 Intent 在您的应用内使用(或者供其他应用在您的应用中调用组件)。但是,您通常应该使用由Intent类或其他框架类定义的操作常量。以下是一些用于启动 Activity 的常见操作:

ACTION_VIEW

如果您拥有一些某项 Activity 可向用户显示的信息(例如,要使用图库应用查看的照片;或者要使用地图应用查看的地址),请使用 Intent 将此操作与startActivity()结合使用。

ACTIVITY_SEND

这也称为“共享”Intent。如果您拥有一些用户可通过其他应用(例如,电子邮件应用或社交共享应用)共享的数据,则应使用 Intent 将此操作与startActivity()结合使用。

有关更多定义通用操作的常量,请参阅类参考文档。 其他操作在 Android 框架中的其他位置定义。例如,对于在系统的设置应用中打开特定屏幕的操作,将在Settings中定义。

您可以使用setAction或Intent构造函数为 Intent 指定操作。

如果定义自己的操作,请确保将应用的软件包名称作为前缀。 例如:

static final String ACTION_TIMETRAVEL="com.example.action.TIMETRAVEL";

注:提供一个intent  action大全参考地址 http://www.cnblogs.com/playing/archive/2010/09/15/1826918.html

数据(Data

引用待操作数据和/或该数据 MIME 类型的 URI(Uri对象)。提供的数据类型通常由 Intent 的操作决定。例如,如果操作是ACTION_EDIT,则数据应包含待编辑文档的 URI。

创建 Intent 时,除了指定 URI 以外,指定数据类型(其 MIME 类型)往往也很重要。例如,能够显示图像的 Activity 可能无法播放音频文件,即便 URI 格式十分类似时也是如此。因此,指定数据的 MIME 类型有助于 Android 系统找到接收 Intent 的最佳组件。但有时,MIME 类型可以从 URI 中推断得出,特别当数据是content:URI 时尤其如此。这表明数据位于设备中,且由ContentProvider控制,这使得数据 MIME 类型对系统可见。

要仅设置数据 URI,请调用setData()。 要仅设置 MIME 类型,请调用setType()。如有必要,您可以使用setDataAndType()同时显式设置二者。

注意:若要同时设置 URI 和 MIME 类型,请勿调用setData()和setType(),因为它们会互相抵消彼此的值。请始终使用setDataAndType()同时设置 URI 和 MIME 类型。

类别( Category )

一个包含应处理 Intent 组件类型的附加信息的字符串。 您可以将任意数量的类别描述放入一个 Intent 中,但大多数 Intent 均不需要类别。 以下是一些常见类别:

CATEGORY_BROWSABLE

目标 Activity 允许本身通过网络浏览器启动,以显示链接引用的数据,如图像或电子邮件。

CATEGORY_LAUNCHER

该 Activity 是任务的初始 Activity,在系统的应用启动器中列出。

有关类别的完整列表,百度吧

您可以使用addCategory()指定类别。

以上列出的这些属性(组件名称、操作、数据和类别)表示 Intent 的既定特征。 通过读取这些属性,Android 系统能够解析应当启动哪个应用组件。

Extra( 笔记拿出来了,重点 ,用心看)

或许就这么看,不知道是什么但是,你是否记得 intent.putExtra(key,value)呢?没错,这个Extra就是我们常用的那个东西。

extras,顾名思义,就是额外的数据信息,Intent中有一个Bundle对象存储着各种键值对,接收该Intent的组件可以从中读取出所需要的信息以便完成相应的工作。有的Intent需要靠Uri携带数据,有的Intent是靠extras携带数据信息。

你可以通过调用Intent对象的各种重载的putExtra(key, value)方法向Intent中加入各种键值对形式的额外数据。你也可以直接创建一个Bundle对象,向该Bundle对象传入很多键值对,然后通过调用Intent对象的putExtras(Bundle)方法将其一块设置给Intent对象中去。

例如,你创建了一个action为ACTION_SEND的Intent对象,然后想用它启动e-mail发送邮件,那么你需要给该Intent对象设置两个extra的值:

用Intent.EXTRA_EMAIL作为key值设置收件方,用Intent.EXTRA_SUBJECT作为key值设置邮件标题。

Intent类里面也指定了很多预定义的EXTRA_*形式的extra,例如上面我们提到的(Intent.EXTRA_EMAIL和Intent.EXTRA_SUBJECT)。如果你想要声明你自己自定义的extra,请确保将你的App的包名作为你的extra的前缀,例如:

static final String EXTRA_GIGAWATTS ="com.example.EXTRA_GIGAWATTS";

标志(Flags

flag就是标记的意思,Intent类中定义的flag能够起到作为Intent对象的元数据的作用。这些flag会告知Android系统如何启动Activity(例如,新启动的Activity属于哪个task)以及在该Activity启动后如何对待它。Flags大全,也请自行百度,就不写了。

本来想写一些例子的,不过想了一下,我的重点不在那上面就不写了(想了解的同学,自行百度,隐式intent,显式intent)

强制用户使用App Chooser

如果有多个应用响应隐式 Intent,则用户可以选择要使用的应用,并将其设置为该操作的默认选项。 如果用户可能希望今后一直使用相同的应用执行某项操作(例如,打开网页时,用户往往倾向于仅使用一种网络浏览器),则这一点十分有用。

但是,如果多个应用可以响应 Intent,且用户可能希望每次使用不同的应用,则应采用显式方式显示选择器对话框。 选择器对话框每次都会要求用户选择用于操作的应用(用户无法为该操作选择默认应用)。 例如,当应用使用ACTION_SEND操作执行“共享”时,用户根据目前的状况可能需要使用另一不同的应用,因此应当始终使用选择器对话框。

要显示选择器,请使用createChooser()创建Intent,并将其传递给startActivity()。例如:


其实,本来还想聊聊自己app添加条件,接收外部app调用的东西,不过,后来发现,这一部分,除非自己做到像某Q或者某地图那样,不然,根本就没人来调用你。所以,就不写了,需要了解的,自行百度:接收intent调用自己app

顺便总结一下啊 intent-filter其实,就是action category data的组合,这里就不深入了,毕竟这是基础技术篇,属于基础部分。

下面我们再介绍,intent中比较特殊的一个东西,penddingIntent

penddingIntent(这部分,属于额外部分,平时使用的频率相对较少)

概述

在Android中,我们常常使用PendingIntent来表达一种“留待日后处理”的意思。从这个角度来说,PendingIntent可以被理解为一种特殊的异步处理机制。不过,单就命名而言,PendingIntent其实具有一定误导性,因为它既不继承于Intent,也不包含Intent,它的核心可以粗略地汇总成四个字——“异步激发”。

很明显,这种异步激发常常是要跨进程执行的。比如说A进程作为发起端,它可以从系统“获取”一个PendingIntent,然后A进程可以将PendingIntent对象通过binder机制“传递”给B进程,再由B进程在未来某个合适时机,“回调”PendingIntent对象的send()动作,完成激发。

在Android系统中,最适合做集中性管理的组件就是AMS(Activity Manager Service)啦,所以它义不容辞地承担起管理所有PendingIntent的职责。这样我们就可以画出如下示意图:


注意其中的第4步“递送相应的intent”。这一步递送的intent是从何而来的呢?简单地说,当发起端获取PendingIntent时,其实是需要同时提供若干intent的。这些intent和PendingIntent只是配套的关系,而不是聚合的关系,它们会被缓存在AMS中。日后,一旦处理端将PendingIntent的“激发”语义传递到AMS,AMS就会尝试找到与这个PendingIntent对应的若干intent,并递送出去。

当然,以上说的只是大概情况,实际的技术细节会更复杂一点儿。下面我们就来谈谈细节。

PendingIntent的技术细节

发起端获取PendingIntent

我们先要理解,所谓的“发起端获取PendingIntent”到底指的是什么。难道只是简单new一个PendingIntent对象吗?当然不是。此处的“获取”动作其实还含有向AMS“注册”intent的语义。

在PendingIntent.java文件中,我们可以看到有如下几个比较常见的静态函数:

public static PendingIntentgetActivity(Context context, int requestCode, Intent intent, int flags)

public static PendingIntentgetActivities(Context context, int requestCode, Intent[] intents, int flags)

public static PendingIntentgetActivities(Context context, int requestCode, Intent[] intents, int flags, Bundle options)

public static PendingIntentgetBroadcast(Context context, int requestCode, Intent intent, int flags)

public static PendingIntentgetService(Context context, int requestCode, Intent intent, int flags)

通过这些名字,大致猜猜想,就是从activity,broadcast,service中获取的,这样想是不是逻辑很通呀,但是,说实话,这几个函数的命名可真不怎么样,所以我们简单解释一下。上面的getActivity()的意思其实是,获取一个PendingIntent对象,而且该对象日后激发时所做的事情是启动一个新activity。也就是说,当它异步激发时,会执行类似Context.startActivity()那样的动作。相应地,getBroadcast()和getService()所获取的PendingIntent对象在激发时,会分别执行类似Context..sendBroadcast()和Context.startService()这样的动作。至于最后两个getActivities(),用得比较少,激发时可以启动几个activity。

怎么样,是不是感觉,被坑了,也不知道谁取得名字,误导性这么强。下面举个简单例子:


其中那句new PendingIntent(target)创建了PendingIntent对象,其重要性自不待言。然而,这个对象的内部核心其实是由上面那个getIntentSender()函数得来的。而这个IIntentSender核心才是我们真正需要关心的东西。

说穿了,此处的IIntentSender对象是个binder代理,它对应的binder实体是AMS中的PendingIntentRecord对象。PendingIntent对象构造之时,IIntentSender代理作为参数传进来,并记录在PendingIntent的mTarget域。日后,当PendingIntent执行异步激发时,其内部就是靠这个mTarget域向AMS传递语义的。(这里可能有点难懂,简单说,就是获取一个标识,以后,激发的时候,通过这个标识,去激发对应的事件)

我们前文说过,PendingIntent常常会经由binder机制,传递到另一个进程去。而binder机制可以保证,目标进程得到的PendingIntent的mTarget域也是合法的IIntentSender代理,而且和发起端的IIntentSender代理对应着同一个PendingIntentRecord实体。示意图如下:


看到这个图,你知道了mTarget,但是,这里面有一个PendingIntentRecord,这是什么鬼?别急,下面就讲

AMS里的PendingIntentRecord



注意其中那个key域。这里的Key是个PendingIntentRecord的内嵌类

注意其中的allIntents[]数组域以及requestIntent域。前者记录了当初获取PendingIntent时,用户所指定的所有intent(虽然一般情况下只会指定一个intent,但类似getActivities()这样的函数还是可以指定多个intent的),而后者可以粗浅地理解为用户所指定的那个intent数组中的最后一个intent。现在大家应该清楚异步激发时用到的intent都存在哪里了吧。

我们再来看看Key的构造函数,是怎样吧


Key不光承担着记录信息的作用,它还承担“键值”的作用。这是不是和我们平时理解的key不一样。其实,不用多想,这就是一个类罢了

在AMS中,管理着系统中所有的PendingIntentRecord节点,所以需要把这些节点组织成一张表(嗯,可能有些同学不知道什么叫节点,也不明白为什么这里会事一张表,没关系,黑盒思想,不用知道,因为这部分东西涉及偏底层了,先就这样吧)

AMS中的PendingIntentRecord总表


这张哈希映射表的键值类型就是刚才所说的PendingIntentRecord.Key。

以后每当我们要获取PendingIntent对象时,PendingIntent里的mTarget是这样得到的:AMS会先查mIntentSenderRecords表,如果能找到符合的PendingIntentRecord节点,则返回之。如果找不到,就创建一个新的PendingIntentRecord节点。因为PendingIntentRecord是个binder实体,所以经过binder机制传递后,客户进程拿到的就是个合法的binder代理。如此一来,前文的示意图可以进一步修改成下图:


AMS里的getIntentSender()函数

现在,我们回过头继续说前文的getActivity(),以及其调用的getIntentSender()。我们先列一遍getActivity()的原型:


context参数是调用方的上下文。

requestCode是个简单的整数,起区分作用。

intent是异步激发时将发出的intent。

flags可以包含一些既有的标识,比如FLAG_ONE_SHOT、FLAG_NO_CREATE、FLAG_CANCEL_CURRENT、FLAG_UPDATE_CURRENT等等。(不少同学对这个域不是很清楚,没关系,黑盒思想)

options可以携带一些额外的数据。

getActivity()的代码很简单,其参数基本上都传给了getIntentSender()。

getIntentSender()的原型大体是这样的:


其参数比getActivity()要多一些,我们逐个说明

type参数表明PendingIntent的类型 getActivity()和getActivities()动作里指定的类型值是INTENT_SENDER_ACTIVITY,getBroadcast()和getService()和动作里指定的类型值分别是INTENT_SENDER_BROADCAST和INTENT_SENDER_SERVICE。另外,在Activity.java文件中,我们还看到一个createPendingResult()函数,这个函数表达了发起方的activity日后希望得到result回馈的意思,所以其内部调用getIntentSender()时指定的类型值为INTENT_SENDER_ACTIVITY_RESULT。

packageName参数表示发起端所属的包名。

token参数是个指代回馈目标方的代理。
这是什么意思呢?我们常用的getActivity()、getBroadcast()和getService()中,只是把这个参数简单地指定为null,表示这个PendingIntent激发时,是不需要发回什么回馈的。不过当我们希望获取类型为INTENT_SENDER_ACTIVITY_RESULT的PendingIntent时,就需要指定token参数了。具体可参考createPendingResult()的代码:


看到了吗?传入的token为Activity的mToken或者其mParent.mToken。说得简单点儿,AMS内部可以根据这个token找到其对应的ActivityRecord,日后当PendingIntent激发时,AMS可以根据这个ActivityRecord确定出该向哪个目标进程的哪个Activity发出result语义。

resultWho参数和token参数息息相关,一般也是null啦。在createPendingResult()中,其值为Activity的mEmbeddedID字符串。

requestCode参数是个简单的整数,可以在获取PendingIntent时由用户指定,它可以起区分的作用。

intents数组参数是异步激发时希望发出的intent。对于getActivity()、getBroadcast()和getService()来说,都只会指定一个intent而已。只有getActivities()会尝试一次传入若干intent。

resolvedTypes参数基本上和intent是相关的。一般是这样得到的:


这个值常常和intent内部的mData URI有关系,比如最终的值可能是URI对应的MIME类型。

flags参数可以指定PendingIntent的一些行为特点。具体了解,百度吧。

还有一些底层实现,这里就不详细说了,下面,就说说关于激发的事情。

PendingIntent的激发动作

下面我们来看PendingIntent的激发动作。在前文我们已经说过,当需要激发PendingIntent之时,主要是通过调用PendingIntent的send()函数来完成激发动作的。PendingIntent提供了多个形式的send()函数,然而这些函数的内部其实调用的是同一个send(),其函数原型如下:


该函数内部最关键的一句是:


我们前文已经介绍过这个mTarget域了,它对应着AMS中的某个PendingIntentRecord。

所以我们要看一下PendingIntentRecord一侧的send()函数,其代码如下:


其中sendInner()才是真正做激发动作的函数。

sendInner()完成的主要逻辑动作有:

1 如果当前PendingIntentRecord节点已经处于canceled域为true的状态,那么说明这个节点已经被取消掉了,此时sendInner()不会做任何实质上的激发动作,只是简单地return ActivityManager.START_CANCELED而已。

2 如果当初在创建这个节点时,使用者已经指定了FLAG_ONE_SHOT标志位的话,那么此时sendInner()会把这个PendingIntentRecord节点从AMS中的总表中摘除,并且把canceled域设为true。而后的操作和普通激发时的动作是一致的,也就是说也会走下面的第3)步。

3 关于普通激发时应执行的逻辑动作是,根据当初创建PendingIntentRecord节点时,用户指定的type类型,进行不同的处理。这个type其实就是我们前文所说的INTENT_SENDER_ACTIVITY、INTENT_SENDER_BROADCAST、INTENT_SENDER_SERVICE等类型啦,大家如有兴趣,可自己参考本文一开始所说的getActivity()、getBroadcast()、getService()等函数的实现代码。

理论说一大堆,下面还是用一个例子说明一下吧:

这个例子是我们常见的一个例子,就是通知。点击通知栏,然后,就会跳转到对应activity:



这样写就可以了,然后,在通知栏那里点击通知,ok,跳转到对应地方,是不是非常简单。当然,这里只是简单举个例子,不过,目前而言,我们用到的也不会太多。

总结一下,pendingintent,其实,就是不马上执行罢了,相当于,一个触发器,等什么时候,触发了某个条件,就去执行。简单来说,这玩意用法就是

1.包装intent(意图,目的)

2.pendingintent装载前面的intent(当然要配置一些属性,比如context呀之类的)

3.等待触发(AMS做的事情,我们不管)

4.触发(比如点击事件之类的)

最后要提一个东西,曾经遇到的坑


没错就是这个方法,当接收数据的activity已经存在与栈中的时候,我们常规的getIntent,是得不到新过来的intent的,需要,用上面这个方法,才能获取到新的intent。这只是一个小插曲,不过,这个问题,还是需要记住的。

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

推荐阅读更多精彩内容