Android開發(fā)藝術探索 | IPC機制

第二章 IPC機制

學習清單:

  • Android中的多進程模式

  • IPC基礎概念

    • 序列化

      • Serializable接口

      • Parcelable接口

    • Binder

  • Android中的IPC方式

    • Bundle

    • 文件共享

    • Messenger

    • AIDL

    • ContentProvider

    • Socket

  • Binder連接池

  • 如何選用合適的IPC方式


一.Android中的多進程模式

在談IPC之前, 我們首先需要理解Android中的多進程模式.

a. 什么是線程? 什么是進程?

  • 線程是CPU調度的最小單元

  • 一個進程可以有多個線程

  • 一個進程至少要有一個線程, 即主線程, 在Android中也叫做UI線程

注意: 如果一個進程中需要執(zhí)行一個耗時的操作, 將其放在主線程執(zhí)行, 則會造成應用無響應, 也就是ANR(Application Not Responding), 如果要解決這個問題, 就需要用多線程, 將耗時操作交給別的線程處理

b. 多進程有什么用?

  • 某些模塊因特殊原因要運行在單獨進程中

  • 為加大一個應用可使用的內存代承,需通過多進程來獲取多份內存空間

1. 開啟多進程模式:

  • 在Android中使用多進程只有一種方法, 那就是給四大組件(Activity | Service | Receiver | ContentProvider) 在AndroidMenifest中指定android:process屬性, 沒有指定process屬性時, 默認進程的進程名是包名.

補充: 在為android:process屬性命名時, 有兩種方式:

  1. android:process=":remote": 以 " : " 開頭是在當前進程名前附加上包名的簡寫方式, 以這種方式開頭的進程屬于私有進程, 其他應用不可以和它跑在同一個進程中
  1. android:process="com.example.main.remote": 完整的命名方式, 該方式屬于全局進程, 其他應用通過ShareUID方式可以和它跑在同一個線程

2. 開啟多進程模式所帶來的問題:

靜態(tài)成員和單例模式完全失效

  • Android系統(tǒng)為每一個進程都分配了一個獨立的虛擬機, 導致在不同的虛擬機中訪問同一個類的對象會產生多個副本

線程同步機制完全失效

  • 原因同上

SharedPreferences的可靠性下降

  • SharedPreferences不支持兩個進程同時進行讀寫操作奶段,即不支持并發(fā)讀寫暂雹,有一定幾率導致數(shù)據(jù)丟失

Application會多次創(chuàng)建

  • Android系統(tǒng)會為新的進程分配獨立虛擬機凌停,相當于系統(tǒng)又把這個應用重新啟動了一次

二.IPC基礎概念

a. IPC是什么?

  • IPC(Inter-Process Communication驶忌,跨進程通信):指兩個進程之間進行數(shù)據(jù)交換的過程

  • 任何一個操作系統(tǒng)都有對應的IPC機制

b. IPC的使用場景:

  • 當某個進程需要向別的進程獲取數(shù)據(jù)

c. Android的進程架構:

  • 每一個Android進程都是獨立的蕾总,且都由兩部分組成,一部分是用戶空間粟焊,另一部分是內核空間冤狡,如下圖:
image

1. 序列化:

a. 序列化的介紹:

  • 含義: 序列化表示將一個對象轉換成可存儲可傳輸的狀態(tài)。序列化后的對象可以在網(wǎng)絡上進行傳輸项棠,也可以存儲到本地
  • 場景: 需要通過IntentBinder等傳輸類對象就必須完成對象的序列化過程
  • 兩種方式:實現(xiàn)Serializable/Parcelable接口

b.Serializable接口:

實現(xiàn)方式:

  • 實現(xiàn)Serializable接口

  • 為該類指定SerialVersionUID (可選)

注意: 不指定UID可以實現(xiàn)序列化, 但是會影響反序列化. 所以我們應該手動去設定UID的值: private static final long serialVersionUID = 1L;


c. Parcelable接口:

實現(xiàn)方式:

  • 實現(xiàn)Parcelable接口

  • 實現(xiàn)接口中的各種方法, 各方法功能如下:
    image

b.Serializable接口和Parcelable接口的比較:

image

2. Binder:

a.概念:

  • 從API的角度: 是一個類, 實現(xiàn)Binder接口

  • 從IPC的角度: 是Android中的一種跨進程通信方式

  • 從Framework角度: 是ServiceManager連結各種Manager和相應ManagerService的橋梁

  • 從應用層的角度: 是客戶端服務端進行通信的媒介, 客戶端通過連接他來獲取服務端提供的服務或者數(shù)據(jù)

b.Android是基于Linux內核基礎上設計的, 卻沒有把管道/消息隊列/共享內存/信號量/Socket等一些IPC通信手段作為Android的主要IPC方式, 而是采用了Binder機制, 其優(yōu)點有:

  • 傳輸效率高悲雳、可操作性強:傳輸效率主要影響因素是內存拷貝的次數(shù),拷貝次數(shù)越少香追,傳輸速率越高合瓢。幾種數(shù)據(jù)傳輸方式比較:
方式 拷貝次數(shù) 操作難度
Binder 1 簡易
消息隊列 2 簡易
Socket 2 簡易
管道 2 簡易
共享內存 0 復雜
  • 數(shù)據(jù)從發(fā)送方的緩存區(qū)拷貝到了內核和緩存區(qū), 而接收方的緩存區(qū)與內核的緩存區(qū)映射的是同一個物理地址, 節(jié)省了一次數(shù)據(jù)拷貝時間, 如圖:
image-20200601115119909.png
  • 實現(xiàn)C/S架構方便:Linux的眾IPC方式除了Socket以外都不是基于C/S架構,而Socket主要用于網(wǎng)絡間的通信且傳輸效率較低透典。Binder基于C/S 架構 晴楔,Server端與Client端相對獨立,穩(wěn)定性較好掷匠。

  • 安全性高:傳統(tǒng)Linux IPC的接收方無法獲得對方進程可靠的UID/PID滥崩,從而無法鑒別對方身份岖圈;而Binder機制為每個進程分配了UID/PID且在Binder通信時會根據(jù)UID/PID進行有效性檢測讹语。

c.Binder框架定義了四個角色: Server, Client, ServiceManager和Binder驅動

其中Server、Client蜂科、ServiceManager運行于用戶空間顽决,Binder驅動運行于內核空間, 如圖:

image-20200601115427397.png

下面簡單介紹這四個角色:

  • ServiceManager: 服務的管理者, 將Binder名字轉換為Client中對改Binder的引用, 使得Client可以通過Binder名字獲得Service中Binder實體的引用, 如圖:
image-20200601115645032.png
  • Binder驅動:

    • 與硬件設備沒有直接關系, 其工作方式與設備驅動程序是一樣的, 工作于內核態(tài)

    • 提供open(), mmap(), poll(), ioctl()等標準文件操作

    • 以字符驅動設備中的misc設備注冊在設備目錄/dev下, 用戶通過/dev/binder訪問它

    • 負責進程間binder通信的建立, 傳遞, 計數(shù)管理及數(shù)據(jù)的傳遞交互等底層支持

    • 驅動和應用程序之間定義了一套接口協(xié)議, 主要功能有ioctl()接口實現(xiàn), 由于ioctl()靈活方便且能一次調用實現(xiàn)先寫后讀以滿足同步交互, 因此不必分別調用write()和read()接口

    • 其代碼位于linux目錄的driver/misc/binder.c中

  • Service&Client:

    服務器&客戶端. 在Binder驅動和Service Manager提供的基礎設施上, 進行Client-Server之間的通信

d.代理模式Proxy: 給某個對象一個代理對象, 并由代理對象控制對原對象的訪問, 如圖:

image-20200601121454234.png

代理模式的組成:

  • Abstract Subject (抽象主題) : 聲明Real Subject和Proxy的共同接口,這樣在任何可以使用Real Subject的地方都可以使用Proxy

  • Real Subject (真實主題) : 定義了proxy所代表的Real subject

  • Proxy Subject (代理主題) :

    • 內部含有一個Real Subject的引用, 可在任何時候操作目標對象

    • 提供一個代替Real Subject相同的接口, 可在任何時候替代目標對象

e. Binder 工作模式:

  • 服務器端:在服務端創(chuàng)建好了一個Binder對象后导匣,內部就會開啟一個線程用于接收Binder驅動發(fā)送的消息才菠,收到消息后會執(zhí)行onTransact(),并按照參數(shù)執(zhí)行不同的服務端代碼

  • Binder驅動:在服務端成功Binder對象后贡定,Binder驅動也會創(chuàng)建一個mRemote對象(也是Binder類)赋访,客戶端可借助它調用transact()即可向服務端發(fā)送消息

  • 客戶端:客戶端要想訪問Binder的遠程服務,就必須獲取遠程服務的Binder對象在Binder驅動層對應的mRemote引用缓待。當獲取到mRemote對象的引用后蚓耽,就可以調用相應Binder對象的暴露給客戶端的方法


3. IPC方式

image-20200601152330202.png

由上圖可以發(fā)現(xiàn), 這些IPC方式全都是通過Binder實現(xiàn)的, 只是封裝的方法不同, 接下來我們分別介紹這六種IPC方式

1.使用Bundle

a. Bundle: 支持在Activity, Service和Receiver之間通過Intent.putExtra()傳遞bundle數(shù)據(jù)

Intent intent = new Intent();
 Bundle bundle = new Bundle();
 bundle.putString("xxx","xxx");
 intent.putExtra("data", bundle);

b. 原理: Bundle實現(xiàn)了Parcelable接口, 它可以方便的在不同的進程中傳輸

c. 注意: Bundle不支持的數(shù)據(jù)類型無法在進程間傳輸


2.使用文件共享

a. 文件共享: 兩個進程通過讀/寫同一個文件來交換數(shù)據(jù)。比如A進程把數(shù)據(jù)寫入文件旋炒,B進程通過讀取這個文件來獲取數(shù)據(jù)

b. 適用情況:對數(shù)據(jù)同步要求不高的進程之間進行通信步悠,并且要妥善處理并發(fā)讀/寫的問題

c. 雖然SharedPreferences也是文件存儲的一種,但不建議采用

  • 原因: 系統(tǒng)對SharedPreferences的讀/寫有一定的緩存策略瘫镇,即在內存中有一份該文件的緩存鼎兽,因此在多進程模式下答姥,其讀/寫會變得不可靠,甚至丟失數(shù)據(jù)

3.使用Messenger

a. Messenger: 信使, 輕量級IPC方式, 通過它在不同進程間傳遞Message對象, 而在Message中放我們要傳遞的數(shù)據(jù), 從而實現(xiàn)IPC

相關記憶:

  • Handler: 主要用于線程之間的數(shù)據(jù)通信
  • Messenger: 進程間的數(shù)據(jù)通信

b. 特點:

  • 底層實現(xiàn)的是AIDL, 即對AIDL進行了封裝, 更便于進行進程間通信

  • 其服務端以串行的方式來處理客戶端的請求, 不存在并發(fā)執(zhí)行的情形, 故無需考慮線程同步的問題

  • 可在不同進程中傳遞Message對象, Messenger可支持的數(shù)據(jù)類型即Message可支持的數(shù)據(jù)類型

  • arg1, arg2, what字段: int
  • obj字段: Object對象, 支持系統(tǒng)提供的Parcelable對象
  • setData: Bundle對象
  • 有兩個構造函數(shù), 分別接收Handler對象和Binder對象

c. 實現(xiàn)方法

  • 服務端:

    • 創(chuàng)建一個Service來處理客戶端連接請求

    • 創(chuàng)建一個Handler來獲取Messenger對象

    • 在Service的onBind()中返回這個Messenger對象底層的Binder

  • 客戶端:

    • 綁定服務端的Service

    • 通過綁定成功后返回的IBinder對象創(chuàng)建一個Messenger對象

如果需要客戶端能夠回應服務端:

  • 創(chuàng)建一個Handler和一個新的Messenger
  • 將Messenger這個對象通過MessagereplyTo參數(shù)傳遞給服務端
  • 服務端通過replyTo參數(shù)就能夠回應客戶端

d. Messenger的缺點:

  • 主要傳遞的是Message, 難以實現(xiàn)遠程方法調用

  • 以串行的方式處理客戶端發(fā)來的消息, 不適合高并發(fā)場景

解決方法: 使用AIDL來實現(xiàn)IPC


4.使用AIDL

a.AIDL(Android Interface Definition Language谚咬,Android接口定義語言): 可以利用它定義客戶端與服務均認可的編程接口, 以便二者使用進程間通信 (IPC) 進行相互通信, AIDL會生成一個服務端的代理類, 通過它客戶端實現(xiàn)間接調用服務端的方法

b. 支持的數(shù)據(jù)類型:

  • 基本數(shù)據(jù)類型(int, long, char, boolean, double等)

  • StringCharSequence

  • List: 只支持ArrayList, 里面每個元素都必須被AIDL支持

  • Map: 只支持HashMap, 里面每個元素都必須被AIDL支持, 包括key和value

  • Paecelable: 所有實現(xiàn)了Parcelable接口的對象

  • AIDl: 所有的AIDL接口本身也可以在AIDL文件中使用

注意: 除了基本數(shù)據(jù)類型, 其他類型的參數(shù)必須標上方向: in, out, inout, 用于表示數(shù)據(jù)的流向

  • in
*   表示數(shù)據(jù)只能由客戶端流向服務端
    
    
*   服務端將會接收到這個對象的完整數(shù)據(jù), 但在服務端修改它不會對客戶端輸入的對象產生影響
  • out
*   表示數(shù)據(jù)只能由服務端流向客戶端
    
    
*   服務端將會接收到這個對象的的空對象, 但在服務端對接收到的空對象有任何修改之后客戶端將會同步變動
  • inout
*   表示數(shù)據(jù)可在服務端與客戶端之間雙向流通
    
    
*   服務端將會接收到客戶端傳來對象的完整信息, 且客戶端將會同步服務端對該對象的任何變動

c. 兩種AIDL文件

  • 用于定義parcelable對象, 以供其他AIDL文件使用AIDL中非默認支持的數(shù)據(jù)類型的

  • 用于定義方法接口, 以供系統(tǒng)使用來完成跨進程通信的

注意:

  • 自定義的Parcelable對象必須把java文件和自定義的AIDL文件顯式的import進來, 無論是否在同一包內
  • AIDL文件用到自定義Parcelable的對象, 必須新建一個和它同名的AIDL文件, 并在其中聲明它為Parcelable類型

d. AIDL本質上是系統(tǒng)提供了一套可快速實現(xiàn)Binder的工具. 關鍵類和方法:

  • AIDL接口: 繼承IInterface

  • Stub類: Binder的實體類, 服務端通過這個類來提供服務

  • Proxy類: 服務器的本地代理, 客戶端通過這個類來獲取服務

  • asInterface(): 客戶端調用, 將服務端返回的Binder對象, 轉換成客戶端所需的AIDL接口類型對象. 返回對象:

    • 若客戶端和服務端位于同一進程, 則直接返回Stub對象本身

    • 否則, 返回系統(tǒng)封裝后的Stub.proxy對象

  • asBinder(): 根據(jù)當前調用情況返回代理Proxy的Binder對象

  • onTransact(): 運行服務端的Binder線程池中, 當客戶端發(fā)起跨進程請求時, 遠程請求會通過系統(tǒng)底層封裝后交由此方法處理

  • transact(): 運行在客戶端, 當客戶端發(fā)送遠程請求的同時將當前線程掛起. 之后調用服務端的onTransact()直到遠程請求返回, 當前線程才繼續(xù)執(zhí)行

image-20200602153715489.png

e. 實現(xiàn)方法

  • 服務端:

    • 創(chuàng)建一個AIDL文件

    • 創(chuàng)建一個Service, 實現(xiàn)AIDL的接口函數(shù)并暴露AIDL接口

  • 客戶端:

    • 通過bindService綁定服務端的Service

    • 綁定成功后, 將服務端返回的Binder對象轉化為AIDL接口所屬的類型, 進而調用AIDL中提供的方法

總結: 服務端里的某個Service為和它綁定的特定客戶端提供Binder對象, 客戶端通過AIDL的靜態(tài)接口asInterface()將Binder對象轉化為AIDL接口的代理對象, 通過這個代理我們就可以發(fā)送遠程調用請求

f. 可能產生ANR的情形

  • 對于客戶端

    • 調用服務端的方法是運行在服務端的Binder線程池中, 若所調用的方法里執(zhí)行了較耗時的任務, 同時會導致客戶端線程長時間阻塞, 易導致客戶端ANR

    • onServiceConnected()onServiceDisconnected()里直接調用服務端的耗時方法, 易導致客戶端ANR

  • 對于服務端

    • 服務端的方法本身就運行在服務端的Binder線程中, 可在其中執(zhí)行耗時操作, 而無需再開啟子線程

    • 回調客戶端Listener的方法是運行在客戶端的Binder線程中, 若所調用的方法里執(zhí)行了較耗時的任務, 易導致服務端ANR

提示: 解決客戶端頻繁調用服務器方法導致性能極大損耗的辦法 : 實現(xiàn)觀察者模式. 即當客戶端關注的數(shù)據(jù)發(fā)生變化時, 再讓服務端通知客戶端去做相應的業(yè)務處理

g. AIDL解注冊失敗

  • 原因:Binder進行對象傳輸實際是通過序列化和反序列化進行鹦付,即Binder會把客戶端傳遞過來的對象重新轉化并生成一個新的對象,雖然在注冊和解注冊的過程中使用的是同一個客戶端對象择卦,但經過Binder傳到服務端后會生成兩個不同的對象睁壁。另外,多次跨進程傳輸?shù)耐粋€客戶端對象會在服務端生成不同的對象互捌,但它們在底層的Binder對象是相同

  • 解決辦法:當客戶端解注冊的時候潘明,遍歷服務端所有的Listener,找到和解注冊Listener具有相同的Binder對象的服務端Listener秕噪,刪掉即可

需要用到RemoteCallBackList:Android系統(tǒng)專門提供的用于刪除跨進程listener的接口钳降。其內部自動實現(xiàn)了線程同步的功能


5.使用ContentProvider

a. ContentProvider: Android中提供的專門用于不同應用間進行數(shù)據(jù)共享的方式,底層實現(xiàn)使用Binder

b. 特點:

  • 相比于AIDL腌巾,使用更加簡單遂填,無需關心底層細節(jié)

  • 系統(tǒng)預置了許多ContentProvider如:通訊錄信息、日程表信息等

  • ContentProvider主要以表格的形式來組織數(shù)據(jù)澈蝙,使用方法與數(shù)據(jù)庫很類似

  • ContentProvider還支持文件數(shù)據(jù)吓坚,如圖片、視頻等

c. 使用方法:

  • 服務端:

    • 新建一個類繼承自ContentProvider, 并實現(xiàn)六個抽象方法:

      • onCreate(): ContentProvider的創(chuàng)建, 一般用于一些初始化動作

      • query(): 對數(shù)據(jù)表進行查詢操作

      • insert(): 對數(shù)據(jù)表進行插入操作

      • delete(): 對數(shù)據(jù)表進行刪除操作

      • update(): 對數(shù)據(jù)表進行更新操作

      • getType(): 返回一個Uri請求所對應的MIME(媒體)類型, 如圖片灯荧、視頻

      注意:

      • update, insert和delete均運行在Binder線程池中, 而onCreate運行在主線程(UI線程), 即不能在onCreate中執(zhí)行耗時操作
      • 一個SQLiteDatabase內部對數(shù)據(jù)庫的操作有同步處理, 但多個SQLiteDatabase之間無法同步
    • 注冊ContentProvider

<provider
  android:name=".provider.BookProvider" 
  android:authorities="com.ljw.charpter_2.book.provider"/>
  • 客戶端: 通過getContentResolver()獲取到ContentResolver對象, 并通過此對象執(zhí)行相應的操作

6.使用Socket

a. Socket: 套接字, 是網(wǎng)絡通信中的概念, 分為: 流式套接字和用戶數(shù)據(jù)報套接字, 分別對應TCP和UDP

網(wǎng)絡傳輸協(xié)議:

TCP: 面向連接的協(xié)議, 提供穩(wěn)定的雙向通信功能, 具有很高的穩(wěn)定性

UDP: 無連接, 提供不穩(wěn)定的單向通道, 也可實現(xiàn)雙向功能, 效率更高, 但不能保證數(shù)據(jù)安全送達

b. 注意:

  • 不能在主線程(UI線程)中訪問網(wǎng)絡

  • 使用Socket進行通信, 需要聲明權限

<uses-permission android:name="android.permission.INTERNET" />
 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

c. 使用方法:

  • 服務端:

    • 創(chuàng)建一個Service, 在線程中建立端口, 等待客戶端連接請求

    • 在與客戶端連接后, 會生成一個新的Socket, 通過它可以和客戶端進行數(shù)據(jù)傳輸

    • 在于客戶端斷開連接后, 關閉相應的Socket并結束線程

  • 客戶端:

    • 開啟一個線程, 通過Socket發(fā)出連接請求

    • 連接成功后, 通過這個Socket讀取服務端消息

    • 斷開連接, 關閉Socket

image-20200604165308839.png

以上六種IPC方式的優(yōu)缺點及使用場景如下圖:

image-20200604165606377.png

4.Binder連接池

  • 背景: 當多個業(yè)務模塊都需要使用到AIDL來進行IPC時, 則需要為每一個模塊創(chuàng)建對應的aidl文件, 與之對應的則要創(chuàng)建許多的Service服務. 這樣會極大的占用我們有限的系統(tǒng)資源

  • 功能: 將每一個模塊的Binder請求都統(tǒng)一轉發(fā)到一個遠程Service中去執(zhí)行, 避免重復創(chuàng)建新的Service

    • 原理: 每個業(yè)務模塊有著自己的AIDL接口并實現(xiàn), 然后向服務端提供自己的唯一標識和Binder對象. 服務端只需要一個Service并提供一個queryBinder接口, 它將根據(jù)業(yè)務模塊的標識來返回相應的Binder對象, 不同的業(yè)務模塊拿到自己的所需的Binder對象就可以進行對應的遠程操作
  • 實現(xiàn):

    • AIDL接口

      • 實現(xiàn)相關模塊的AIDL接口

      • 創(chuàng)建一個IBinderPool.aidl文件, 獲取相關模塊所需的AIDL接口

        interface IBinderPool {
          IBinder queryBinder(int binderCode);
         }
        
      • 創(chuàng)建一個BinderPool文件, 實現(xiàn)IBinderPool接口

       public static class BinderPoolImpl extends IBinderPool.Stub {
          public BinderPoolImpl() {
          super();
          }
         ?
          @Override
          public IBinder queryBinder(int binderCode) throws RemoteException {
          IBinder binder = null;
          switch (binderCode) {
          case BINDER_SECURITY_CENTER: {
          binder = new SecurityCenterImpl();
          break;
          }
          case BINDER_COMPUTE: {
          binder = new ComputerImpl();
          break;
          }
          default:
          break;
          }
          return binder;
          }
          }
      
      • Binder連接池的具體實現(xiàn), 來綁定遠程服務
    • 服務端: 遠程服務BinderPoolService實現(xiàn), 在onBind()處返回實例化的IBinderPool實體類對象

    • 客戶端:

      • 通過BinderPool類中的getInstance(Context)獲取BinderPoll類實例

      • 通過BinderPool類實例里實現(xiàn)的queryBinder(int)方法獲取所需要的Binder對象


總結

  • IPC是指兩個進程之間進行數(shù)據(jù)交換的過程

  • Android中的IPC方式底層都是由Binder實現(xiàn)的, 由此可見Binder在Android中的重要性

  • 要根據(jù)不同的需求, 使用不同的IPC方式, 因地制宜

  • 要多用, 不然會忘

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末礁击,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子逗载,更是在濱河造成了極大的恐慌哆窿,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厉斟,死亡現(xiàn)場離奇詭異挚躯,居然都是意外死亡,警方通過查閱死者的電腦和手機擦秽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門码荔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人感挥,你說我怎么就攤上這事缩搅。” “怎么了链快?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵誉己,是天一觀的道長。 經常有香客問我域蜗,道長巨双,這世上最難降的妖魔是什么噪猾? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮筑累,結果婚禮上袱蜡,老公的妹妹穿的比我還像新娘。我一直安慰自己慢宗,他們只是感情好坪蚁,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著镜沽,像睡著了一般敏晤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缅茉,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天嘴脾,我揣著相機與錄音,去河邊找鬼蔬墩。 笑死译打,一個胖子當著我的面吹牛,可吹牛的內容都是我干的拇颅。 我是一名探鬼主播奏司,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼樟插!你這毒婦竟也來了韵洋?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤岸夯,失蹤者是張志新(化名)和其女友劉穎麻献,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體猜扮,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年监婶,在試婚紗的時候發(fā)現(xiàn)自己被綠了旅赢。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡惑惶,死狀恐怖煮盼,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情带污,我是刑警寧澤僵控,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站鱼冀,受9級特大地震影響报破,放射性物質發(fā)生泄漏悠就。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一充易、第九天 我趴在偏房一處隱蔽的房頂上張望梗脾。 院中可真熱鬧,春花似錦盹靴、人聲如沸炸茧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梭冠。三九已至,卻和暖如春改备,著一層夾襖步出監(jiān)牢的瞬間妈嘹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工绍妨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留润脸,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓他去,卻偏偏與公主長得像毙驯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子灾测,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容