推送原理解析 极光推送使用详解

推送技术产生场景:

--服务器端主动性: 客户端与服务器交互都是客户端主动的, 服务器一般不能主动与客户端进行数据交互, 因为服务器端无法得知客户端的 IP 地址 及 状态;

--数据实时性: 如果服务器端有紧急数据要传递给客户端, 就必须主动向客户端发送数据;

--基本原理: 使客户端实时获取服务器端消息, Pull 方式, 小周期轮询, 费电费流量; 另一个就是 Push 方式, 服务器端向客户端主动推送数据, 可以省电省流量;

一. 推送原理

1. Android 推送原理简介

(1) SMS 方式推送

SMS 推送:

--SMS: Short Message Service 缩写, 即短信服务;

--实现方式: 服务器端向手机端发送短信, 手机监听短信广播, 然后将拦截的短信信息进行显示;

--优点: 省电, 省流量, 在没有网络的偏远地点也能接收到推送消息;

--缺点: 费钱, 一毛钱一条;

(2) 轮询 方式推送

轮询推送:

--实现方式: 周期性主动获取网络中的数据;

--缺点: 费电, 费流量;

(3) 长链接 方式推送

长链接推送: 主流方法;

--实现方式: 手机端与服务器端建立一条长时间的数据流链接, 手机客户端一直等待服务器端的数据;

--优点: 有一条长链接, 有数据的时候才发送数据, 没有时不消耗流量, 比较省流量;

--缺点: 由于要保存一条长链接, 比较费电; 在网络不稳定的情况下, 推送容易失败;

2. Android 推送解决方案简介

(1) C2DM 推送 (Google)

C2DM 推送简介: 全称 Cloudto Device Messaging, Google 提供的 推送解决方案;

--运行方式: 提供一个轻量级机制, 允许服务器通知应用程序, 主动与客户端进行数据交互, 处理消息排队, 并向运行于目标设备的应用程序分发消息;

--优点: Google 提供的原生框架, 无需在应用中添加第三方代码 和 部署服务器端;

--缺点: 1.该推送依赖 Google 服务器, 需要绑定 Google 帐号, 目前在中国 Google 被屏蔽, 无法使用; 2. 许多手机厂商去掉了软件中的该模块;

--运行机制图:

(2) MQTT 推送 (IBM)

MQTT 推送简介: MQTT 是轻量级的消息发布"订阅协议,

--优点: 省电, 省流量, 轻量级, 有 C++ 版的服务器端组件 RSMB;

--缺点: RSMB 不开源, 部署成本高, 比较复杂;

--IBM相关MQTT官网: http://www-01.ibm.com/support/docview.wss?rs=171&uid=swg24006006 ;

RSMB 服务器端: 全称 Really Small Message Broker, IBM 提供;

--工作方式: 在服务器端, 接收消息, 并将 消息 传输给指定移动设备;

--地址: http://www.alphaworks.ibm.com/tech/rsmb ;

MQTT 推送示例:

--客户端: https://github.com/tokudu/AndroidPushNotificationsDemo ;

--服务器端 PHP: https://github.com/tokudu/PhpMQTTClient ;

(3) 基于 XMPP 的 AndroidPN 推送 (开源)

XMPP 推送简介:

--XMPP 简介: 全称 Extensible Messaging and Presence Protocol (可扩展通讯和表示协议), 基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线探测, 该协议允许因特网用户向因特网上的其他任何人发送即时消息;

--AndroidPN: 基于 XMPP 协议开发的 Java 开源 Android 推送通知实现, 包含了完整的客户端 和 服务器端;

--项目主页: http://sourceforge.net/projects/androidpn/ ;

--原理图:

AndroidPN 缺陷: 如果使用该框架进行推送, 需要在了解 XMPP 和 anroidPN 基础上进行大量的二次开发;

--连接中断: 连接时间过长, 连接会中断, 收不到消息;

--稳定性差: 该框架不是很稳定;

--纠错机制: 消息推送出去后, 不检查是否推送到客户端, 推送出去就不管了;

3. 推送 4s 评价标准

4S 标准:Safe(安全),Stable(稳定),Save(省电省流量省成本),Slim(体积小);

(1) Safe (安全)

推送安全标准:

--透明传输: 只负责点对点的传输的质量, 不关心中间的传输介质 与 传输业务逻辑 协议等;

--加密方案: 信息需要加密, 另外推送的 ID 系统需要独立与后台已有的 ID 系统;

(2) Stable (稳定)

服务器稳定: 长链接方案对服务器开销要求很高, 服务器端开发难度很大;

--在线峰值: 同时在线连接数到达100万的稳定性;

--并发时延: 高并发时的消息平均延迟, 一分钟处理 100万 条数据;

--服务器稳定: 稳定性时延占总时间的 99.9%, 有备份和负载均衡的机制;

客户端稳定: 中国网络状况复杂, 手机长时间联网比较难, 稳定性比较难, 开发时要考虑每个省的每个运营商, 每款手机的机型;

--联网时间: 每日联网时间 23.5 小时以上;

--消息到达率: 消息收到后 9 小时内客户端的消息到达率;

(3) Save (节省)

节省评判:

--电量节省: 注意 CPU 休眠率, 服务短待机时间百分比评判;

--流量节省: 处理协议 和 冗余数据包, 使用空载待机月流量评判;

--成本节省: 单服务器同时承载连接数, 同时承载连接数越多, 成本越低, 个推单服务器连接 300 万(业内顶尖水平);

(4) Slim (体积小)

集成 SDK 大小: 客户端推送的 SDK 的大小尽量小, 一般要小于 300K;

二. 极光推送概述

1. 功能概述

极光推送基本功能:主动即时的向用户发起交互, 可以发送聊天消息;

--作用: 通过向精准的目标用户推送有价值的消息, 可以提供用户忠诚度, 提高留存率;

(1) 推送方式

推送方式简介:

--通知: 推送文本内容, 展示在通知栏中;

--自定义消息: 推送自定义消息, 给用户自行处理;

--富媒体: 推送 HTML 页面内容;

(2) 推送目标

推送目标简介:

--广播推送: 向所有用户发送广播消息;

--标签推送: 根据用户设置的标签分组, 向某一组推送消息;

--别名推送: 客户端绑定用户别名, 向单个用户推送信息;

(3) 用户分群

用户分群简介: 可以根据 JPush 提供的多条件组合, 对用户进行群组划分, 实现实时筛选推送;

(4) 推送历史

推送历史简介: 通过 WEB 或者 API 发出的推送, 都可以在推送历史记录中查询到, 并可以实时显示推送结果;

2. 推送框架

推送框架:

--推送数据源: 自己开发的服务器端 或者 使用 极光推送官网的 WEB 后台;

--JPush API: 部署在服务器端, 开发者的服务器端发起推送时, 将数据传到 JPush API 中, 然后再向下传递;

--建立长链接: 集成 JPush 的 SDK 客户端启动后会建立一个到 JPush Cloud 的长链接, 提供 App 永远在线的能力;

--原理图:

3. 极光推送原理

参考文章(极光推送官方博客): http://blog.jpush.cn/jpush_wireless_push_principle/ ;

(1) 客户端原理

IP地址 分配原理:

--IP 地址有限: IPv4 的 IP 地址数量有限, 运营商要动态地为 手机分配 IP 地址, 这些 IP 地址都是运营商的内网 IP;

--网络地址转换 (NAT): 全称 Network Address Translation, 网关维护一个外网 IP 地址, 与 内网 IP 地址对应;

--外网 IP 不固定: 由于运营商持有的外网 IP 数量有限, 需要动态的分配给接入运营商的用户, 因此在手机一段时间没有数据传输时会将该手机分配的外网 IP 地址收回, 分配给其它用户;

--解决方案: Android 手机端想要保持长链接, 首先外网 IP 地址不能变, 不能让运营商收回 这个 IP 地址;

Android 手机端实现方案:

--心跳: 为了长时间保持外网 IP, 需要客户端定期发送心跳给运营商, 以便刷新 NAT 列表;

--Timer 定时方法: 该类计划循环执行定时任务, 但是使用该类会使 CPU 保持唤醒状态, 比较费电;

--AlarmManager 定时方法: 该类封装了 Android 手机的 RTC 硬件时钟模块, 可以在 CPU 休眠时正常运行, 定时任务执行时再唤醒 CPU, 这样做到了电量节省;

(2) 服务器原理

C10K 问题: 单台服务器解决 同时保持一万长链接的性能问题;

4. Android SDK 简介

Android SDK 本质: JPush SDK 集成到 Android APP 中后,作为一个 Service 在 Android 端长期运行, 始终与 服务器端 保持者长链接, 相当与永远在线;

(1) 多平台支持

多平台支持:

--手机芯片类型: 一般的手机是 ARM 芯片, 但是有些手机是 MIPS 芯片 或者 x86 芯片;

--so 库支持: 每个 CPU 芯片类型对应的 so 库, 都需要特殊编译, 无法跨平台调用, 如 ARM 平台的 so 库在 x86 平台就无法运行;

(2) 电量与流量说明

流量消耗: JPush 的协议是自定义的, 协议的数据量经过了精简, 流量消耗非常少;

电量消耗: JPush 心跳保持连接时可以在 CPU 不唤醒的情况下执行, 减少了不必要的代码运行, 电量非常节省;

(3) 相关库说明

JPush 依赖库:

--.so 依赖库内容: JPush 需要一个 so 动态库, 该库是 C 语言编写, 在 Linux 下进行交叉编译而来, 可以在 ARM 芯片的手机上运行;

--jar 依赖库内容: 对 Java 代码的封装;

三. 极光推送简单 Demo

Demo 概述: JPush 官方提供了一个简单 Demo, 向我们演示了 JPush 基本的推送流程, 基本步骤分为下面几块, 即

--Web 配置操作部分: 包括 注册开发者帐号, 创建应用;

--下载手机示例: 下载系统自动生成的 Android 应用示例;

--发送推送指令: 在 Web 端发送推送指令, 手机端接收该指令;

--官方地址: 关于该模块详细信息可参考官方文档, http://docs.jpush.io/guideline/android_3m/ ;

1. Web 配置操作

(1) 帐号创建

该步骤就不再赘述, 普通的帐号注册即可;

(2) 创建应用

创建应用步骤:

--创建应用界面: https://www.jpush.cn/common/apps ;

--创建应用: 点击 创建应用 按钮即可;

--配置应用信息: 输入应用名称, 上传一个图标, 然后填写一个包名, JPush 系统会在后台根据你输入的包名生成的推送的 Android 应用 Demo, 该 Demo 包含了该配置的信息;

查看应用设置界面:

2. 下载导入 Android 应用源码

(1) 下载 Android 应用源码

下载源码: 在应用设置的 Android 模块, 有 "下载 Android Example" 按钮, 点击该按钮即可下载 Android 应用;

(2) 导入 Android 应用源码

将下载后的源码解压, 不用任何修改, 即可导入到应用中运行;

3. 发送推送

(1) 不初始化推送无效

直接在后台推送: 我们在 Android 手机界面不做任何操作, 直接在网页后台推送消息;

--发送通知: 在应用的 "推送" 模块, 点击发送通知按钮;

--输入推送内容: 输入任意推送内容 "Jpush Demo Send By octopus 1.", 点击页面最下方的 "立即发送" 按钮;

--对话框选择: Web 界面会弹出对话框, 提示是否发送, 选 "发送吧" 即可;

--查看推送结果: 点击之后又弹出对话框, 点击 "去看看" 可以查看推送结果;

--推送结果分析: 点击上面的 "去看看" 可以查看推送结果, 点击 "推送历史" 按钮, 也可一查看推送结果; 由于我们没有在 客户端进行初始化, 因此推送没有成功;

(2) 初始化后推送

初始化之后推送:

--手机端初始化: 点击手机端的 "initPush" 按钮, 进行初始化;

--发送消息: 发送 "Jpush Demo Send By octopus 2." 消息, 此时连上一次推送的消息也送达, 网络不同会延迟一定时间;

--查看发送结果: 此时显示的推送, 两次都成功了;

(3) 停止 和 恢复 推送功能

停止恢复推送功能: 当点击停止推送 "stopPush" 按钮时, 推送手机端就不会再接收推送内容, 当点击恢复推送时, 会将期间积攒的所有推送内容一次性推送到手机中;

四. Android 应用集成 JPush

1. 依赖库拷贝

(1) SDK 简介

SDK 下载: 最新的 SDK 压缩包 Jpush-Android-sdk-1.7.3.zip ;

--JPush SDK 下载页面: http://docs.jpush.io/resources/ ;

--Android SDK 下载地址: https://www.jpush.cn/downloads/sdk/android/ ;

详细文件说明: 将 Jpush-Android-sdk-1.7.3.zip 解压, 解压后的目录是 Jpush-Android-sdk;

--查看文档目录: 使用 tree -L 3 命令, 查看深度为 3 的路径结构;

octopus@octopus:~/JPush/Jpush-Android-sdk$ tree -L 3

.

├── AndroidManifest.xml

├── ChangeLog.txt

├── doc

│  └── Jpush-sdk-集成指南.pdf

├── example

│  ├── AndroidManifest.xml

│  ├── libs

│  │  ├── armeabi

│  │  ├── armeabi-v7a

│  │  └── jpush-sdk-release1.7.3.jar

│  ├── proguard-project.txt

│  ├── project.properties

│  ├── res

│  │  ├── drawable-hdpi

│  │  ├── drawable-ldpi

│  │  ├── drawable-mdpi

│  │  ├── drawable-xhdpi

│  │  ├── layout

│  │  ├── values

│  │  └── values-zh

│  └── src

│      └── com

└── libs

├── armeabi

│  └── libjpush173.so

├── armeabi-v7a

│  └── libjpush173.so

└── jpush-sdk-release1.7.3.jar

--AndroidManifest.xml (配置文件): 这是 Android 应用的主要配置文件示例;

--ChangeLog.txt (升级说明): SDK 版本升级的说明;

--doc (文档): doc 目录下 有个 "Jpush-sdk-集成指南.pdf" 文档, 这是集成 JPush 文档;

--example (代码示例): example 明显是个 Android 示例 demo;

--libs (依赖库): Android 应用中相关库, 放在这个目录中, libjpush173.so 是依赖的 C 底层库, jpush-sdk-release1.7.3.jar 是依赖的 Java 库;

(2) 拷贝 依赖库 到 Android 应用中

执行过程:

--创建应用: 创建一个空应用, 注意应用最小版本应大于 2.1, 否则不能支持极光推送, 应用的包名为 cn.org.octopus.jpush.demo 即可, 不用在后台另外创建一个

--拷贝依赖库: 在工程根目录下创建一个 libs 目录, 拷贝jpush-sdk-release1.7.3.jar到 libs 目录中, 将armeabi/libjpush173.so和armeabi-v7a/libjpush173.so拷贝到 libs 目录, 注意要连文件夹一起拷贝, 拷贝完后如下图;

2. 配置 AndroidManifest.xml 文件

(1) 权限配置

JPush 定义权限: JPush 定义了一个权限, 允许应用接收 JPush 代码发送的广播消息, You Package.permission.JPUSH_MESSAGE, 注意要使用你的包名替换其中的 Your Package;


用户权限:


可选用户权限 :


(2) 配置 JPush 服务

JPush 服务:

--推送服务:


android:name="cn.jpush.android.service.PushService"

android:enabled="true"

android:exported="false" >

--下载服务:


android:name="cn.jpush.android.service.DownloadService"

android:enabled="true"

android:exported="false" >

(3) 配置 JPush 广播接收者

JPush 广播接收者配置:

--推送接收者:


android:name="cn.jpush.android.service.PushReceiver"

android:enabled="true" >


-- 时钟相关接收者 :


(4) 配置 渠道 和 推送标识

渠道 和 AppKey 配置:








(5) 最终的配置文件

配置好的文件:


package="cn.org.octopus.jpush.demo"

android:versionCode="1"

android:versionName="1.0" >

android:minSdkVersion="17"

android:targetSdkVersion="19" />

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme">

android:name=".MainActivity"

android:label="@string/app_name" >

android:name="cn.jpush.android.service.PushService"

android:enabled="true"

android:exported="false" >

android:name="cn.jpush.android.service.DownloadService"

android:enabled="true"

android:exported="false" >

android:name="cn.jpush.android.service.PushReceiver"

android:enabled="true" >

3. 推送测试

后台推送:

--控制台地址: https://www.jpush.cn/common/apps/ ;

--发起推送: 进入控制台, 点击对应的应用, 进入推送页面;

--查看结果:

--推送统计:

五. JPush 相关 API

1. 初始化 停止 恢复 推送

推送控制方法:

--初始化推送: JPushInterface.init(Context context), 初始化之后才能接收推送消息;

--恢复推送: JPushInterface.resumePush(Context context), 停止推送后, 调用该方法, 即可恢复推送;

--停止推送: JPushInterface.stopPush(Context context), 调用该方法之后, 手机便收不到推送信息了;

代码示例 :

public void onClick(View view) {

int id = view.getId();

switch (id) {

case R.id.init_jpush:

//初始化 JPush, 初始化之后才可以进行推送, 否则推送失败

JPushInterface.init(this);

//设置调试模式, 可以在 LogCat 中查看 JPush 日志

JPushInterface.setDebugMode(true);

break;

case R.id.start_jpush:

//恢复推送

JPushInterface.resumePush(getApplicationContext());

break;

case R.id.stop_jpush:

//停止推送

JPushInterface.stopPush(getApplicationContext());

break;

default:

break;

}

}

界面效果及说明:

--界面效果:

--按钮说明: 点击 "初始化" 按钮就可以接收推送消息, 点击 "停止推送" 按钮手机停止接收消息推送, 点击 "恢复推送" 即开始接收推送消息;

2. 根据 别名 和 标签 分组推送

参考文档: http://docs.jpush.cn/pages/viewpage.action?pageId=557241

(1) 概念介绍

别名:

--作用: 别名用于代表安装了应用的用户, 每个用户对应着一个别名;

--用户与别名对应性 (多对一): 每个用户只能指定一个别名, 一个别名可以同时指定给多个用户, 给别名发消息时, 会同时给所有设置该别名的用户发消息;

标签:

--作用: 方便开发者根据标签发送推送消息;

--用户与标签对应性(多对多): 一个用户可以有多个标签, 一个标签可以设置给多个用户;

(2) 设置别名标签接口方法

设置别名与标签方法:

--方法接口:

public static void setAliasAndTags(

Context context, //上下文对象

String alias, //别名, 只能设置一个别名

Set tags, //标签集合, 可设置多个标签

TagAliasCallback callback) //回调接口, 其中有一个 gotResult 接口方法, 系统回传入错误码给 responseCode 参数

-- 别名设置说明 : a. 设置 null(没有地址) 即不设值值; b. 设置 "" (初始化后) 即清空之前的设置; c. 设置会覆盖之前的设置; d. 长度 40字节 UTF8 编码;

--标签设置说明:a. 设置 null(没有地址) 即不设值值; b. 设置 空集合(没有数据, 已经初始化) 即清空之前的设置; c. 设置会覆盖之前的设置; d. 每个标签长度 40字节 UTF8 编码, 最多 100 个标签;

设置别名方法:

--方法接口:

public static void setAlias(

Context context, //上下文对象

String alias, //别名内容

TagAliasCallback callback) //回调接口

-- 参数说明 : 该参数与 setAliasAndTags 方法参数相同;

设置标签方法:

--方法接口:

public static void setTags(

Context context,

Set tags,

TagAliasCallback callback)

--参数说明:该参数与setAliasAndTags 方法参数相同;

过滤无效标签: 感觉这纯属 JPush API 定义缺陷, 这个应该对我们隐藏才对;

--方法接口:

public static Set filterValidTags(Set tags)

--作用: 设置标签时如果 标签Set集合 中有一个是无效的, 那么整个设置都无效, 应该是设计缺陷, 后期修补 BUG 的权益之际;

回调接口: TagAliasCallback 回调接口;

--方法接口:

public void gotResult(

int responseCode, //错误码

String alias, //别名

Set tags); //标签集合

代码示例:

--设置方法代码:

String alias = set_alias.getText().toString();

String tag = set_tag.getText().toString();

Set set = new HashSet();

set.add(tag);

JPushInterface.setAliasAndTags(

getApplicationContext(),

alias,

set,

this);

Log.i(TAG, "已设置别名 与 标签");

-- 回调方法 :

@Override

public void gotResult(int arg0, String arg1, Set arg2) {

Log.i(TAG, "错误码 : " + arg0 + " , 别名 : " + arg1);

}

--手机端设置:

-- 后台发送极光推送 :

--设置标签:

--标签报错: 如果设置的标签没有手机注册, 会出现如下报错;

--推送结果:

--推送通知消息:

3. 根据 RegistrationID 进行单机推送

参考文档 : http://docs.jpush.cn/pages/viewpage.action?pageId=8814639

(1) 概念介绍

RegistrationID 简介: 应用第一次注册到 JPush 后台时, 会生成一个唯一的设备标识 RegistrationID, 每个设备不重复;

(2) 获取RegistrationID

方法接口:

//SDK 初次注册成功后,开发者通过在自定义的 Receiver 里监听 Action - cn.jpush.android.intent.REGISTRATION 来获取对应的 RegistrationID。注册成功后,也可以通过此函数获取

public static String getRegistrationID(Context context)

-- 获取 RegistrationID 代码 :

//获取 RegistrationID

String registrationid = JPushInterface.getRegistrationID(getApplicationContext());

tv_registrationid.setText("RegistrationID : " + registrationid);

--界面示例:

--后台推送设置:

4. 清除通知

清除通知简介 :

-- 方法接口 :

public static void clearAllNotifications(Context context);

-- 代码示例 :

case R.id.clear_notification:

//清除所有通知

JPushInterface.clearAllNotifications(getApplicationContext());

break;

-- 效果 : 在后台发送一条消息, 点击清除按钮, 所有的推送通知都清除;

5. 推送时间限制

(1) 设置允许推送时间

方法接口:

public static void setPushTime(

Context context, //上下文对象

Set weekDays, //允许接收推送的 一周天数

int startHour, //开始时间

int endHour) //结束时间

-- 代码示例 :

Set days = new HashSet();

days.add(1);

days.add(2);

days.add(3);

days.add(4);

days.add(5);

JPushInterface.setPushTime(getApplicationContext(), days, 10, 23);

(2) 设置禁止推送时间

方法接口:

public static void setSilenceTime(

Context context, //上下文对象

int startHour, //开始时间-小时

int startMinute, //开始时间-分钟

int endHour, //结束时间-小时

int endMinute) //结束时间-分钟

-- 代码示例 :

JPushInterface.setSilenceTime(getApplicationContext(), 22, 30, 8, 30);

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,391评论 25 707
  • 版本记录 前言   现在很多APP都有推送功能,其中极光推送就是很多APP的首选。我们最近的几个APP也是用的极光...
    刀客传奇阅读 8,360评论 0 8
  • 版本记录 前言 前一篇已经对ios新特性进行了介绍,这一篇则对ios的SDK进行说明。1. 极光推送集成(一)2....
    刀客传奇阅读 1,275评论 0 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 生活中,很多年轻人靠自己的奋斗去实现梦想,去满足自己的物质需求。他们既不能拼爹,也不能拼妈,那么,对于年轻人来说,...
    谢筱言阅读 1,004评论 2 2