Android源碼解析RPC系列(一)---Binder原理

轉(zhuǎn)載請(qǐng)注明文章出處LooperJing丙猬!

看了幾天的Binder瘫里,決定有必要寫一篇博客凌简,記錄一下學(xué)習(xí)成果,Binder是Android中比較綜合的一塊知識(shí)了寿冕,目前的理解只限于JAVA層蕊程。首先Binder是干嘛用的?不用說驼唱,跨進(jìn)程通信全靠它藻茂,操作系統(tǒng)的不同進(jìn)程之間,數(shù)據(jù)不共享玫恳,對(duì)于每個(gè)進(jìn)程來說捌治,它都天真地以為自己獨(dú)享了整個(gè)系統(tǒng),完全不知道其他進(jìn)程的存在纽窟,進(jìn)程之間需要通信需要某種系統(tǒng)機(jī)制才能完成肖油,在Android整個(gè)系統(tǒng)架構(gòu)中,采用了大量的C/S架構(gòu)的思想臂港,所以Binder的作用就顯得非常重要了森枪,但是這種機(jī)制為什么是Binder呢?在Linux中的RPC方式有管道审孽,消息隊(duì)列县袱,共享內(nèi)存等,消息隊(duì)列和管道采用存儲(chǔ)-轉(zhuǎn)發(fā)方式佑力,即數(shù)據(jù)先從發(fā)送方緩存區(qū)拷貝到內(nèi)核開辟的緩存區(qū)中式散,然后再?gòu)膬?nèi)核緩存區(qū)拷貝到接收方緩存區(qū),這樣就有兩次拷貝過程打颤。共享內(nèi)存不需要拷貝暴拄,但控制復(fù)雜,難以使用编饺。Binder是個(gè)折中的方案乖篷,只需要拷貝一次就行了。其次Binder的安全性比較好透且,好在哪里撕蔼,在下還不是很清楚,基于安全性和傳輸?shù)男士紤]秽誊,選擇了Binder鲸沮。Binder的英文意思是粘結(jié)劑,Binder對(duì)象是一個(gè)可以跨進(jìn)程引用的對(duì)象锅论,它的實(shí)體位于一個(gè)進(jìn)程中讼溺,這個(gè)進(jìn)程一般是Server端,該對(duì)象提供了一套方法用以實(shí)現(xiàn)對(duì)服務(wù)的請(qǐng)求棍厌,而它的引用卻遍布于系統(tǒng)的各個(gè)進(jìn)程(Client端)之中肾胯,這樣Client通過Binder的引用訪問Server,所以說耘纱,Binder就像膠水一樣敬肚,把系統(tǒng)各個(gè)進(jìn)程粘結(jié)在一起了,廢話確實(shí)有點(diǎn)多束析。

一艳馒、概念理解

為了從而保障了系統(tǒng)的安全和穩(wěn)定,整個(gè)系統(tǒng)被劃分成內(nèi)核空間和用戶空間
內(nèi)核空間:獨(dú)立于普通的應(yīng)用程序员寇,可以訪問受保護(hù)的內(nèi)存空間弄慰,有訪問底層硬件設(shè)備的所有權(quán)限。
用戶空間:相對(duì)與內(nèi)核空間蝶锋,上層運(yùn)用程序所運(yùn)行的空間就是用戶空間陆爽,用戶空間訪問內(nèi)核空間的唯一方式就是系統(tǒng)調(diào)用。一個(gè)4G的虛擬地址空間扳缕,其中3G是用戶空間慌闭,剩余的1G是內(nèi)核空間。如果一個(gè)用戶空間想與另外一個(gè)用戶空間進(jìn)行通信躯舔,就需要內(nèi)核模塊支持驴剔,這個(gè)運(yùn)行在內(nèi)核空間的,負(fù)責(zé)各個(gè)用戶進(jìn)程通過Binder通信的內(nèi)核模塊叫做Binder驅(qū)動(dòng)粥庄,雖然叫做Binder驅(qū)動(dòng)丧失,但是和硬件并沒有什么關(guān)系,只是實(shí)現(xiàn)方式和設(shè)備驅(qū)動(dòng)程序是一樣的惜互,提供了一些標(biāo)準(zhǔn)文件操作布讹。

Paste_Image.png

二、Binder的通信模型

在寫AIDL的時(shí)候训堆,一般情況下炒事,我們有兩個(gè)進(jìn)程,一個(gè)作為Server端提供某種服務(wù)蔫慧,然后另外一個(gè)進(jìn)程作為Client端,連接Server端之后挠乳,就 可以使用Server里面定義的服務(wù)。這種思想是一種典型的C/S的思想。值得注意的是Android系統(tǒng)中的Binder自身也是C/S的架構(gòu)蕉汪,也有Server端與Client端比吭。一個(gè)大的C/S架構(gòu)中,也有一個(gè)小的C/S架構(gòu)卖怜。

先籠統(tǒng)的說一下,在整個(gè)Binder框架中阐枣,由系列組件組成马靠,分別是Client奄抽、Server、ServiceManager和Binder驅(qū)動(dòng)程序甩鳄,其中Client逞度、Server和ServiceManager運(yùn)行在用戶空間,Binder驅(qū)動(dòng)程序運(yùn)行內(nèi)核空間妙啃。運(yùn)行在用戶空間中的Client档泽、Server和ServiceManager,是在三個(gè)不同進(jìn)程中的揖赴,Server進(jìn)程中中定義了服務(wù)提供給Client進(jìn)程使用馆匿,并且Server中有一個(gè)Binder實(shí)體,但是Server中定義的服務(wù)并不能直接被Client使用燥滑,它需要向ServiceManager注冊(cè)渐北,然后Client要用服務(wù)的時(shí)候,直接向ServiceManager要铭拧,ServiceManager返回一個(gè)Binder的替身(引用)給Client腔稀,這樣Client就可以調(diào)用Server中的服務(wù)了。

場(chǎng)景:進(jìn)程A要調(diào)用進(jìn)程B里面的一個(gè)draw方法處理圖片羽历。

分析:在這種場(chǎng)景下焊虏,進(jìn)程A作為Client端,進(jìn)程B做為Server端秕磷,但是A/B不在同一個(gè)進(jìn)程中诵闭,怎么來調(diào)用B進(jìn)程的draw方法呢,首先進(jìn)程B作為Server端創(chuàng)建了Binder實(shí)體澎嚣,為其取一個(gè)字符形式疏尿,可讀易記的名字,并將這個(gè)Binder連同名字以數(shù)據(jù)包的形式通過Binder驅(qū)動(dòng)發(fā)送給ServiceManager易桃,也就是向ServiceManager注冊(cè)的過程褥琐,告訴ServiceManager,我是進(jìn)程B晤郑,擁有圖像處理的功能敌呈,ServiceManager從數(shù)據(jù)包中取出名字和引用以一個(gè)注冊(cè)表的形式保留了Server進(jìn)程的注冊(cè)信息。為什么是以數(shù)據(jù)包的形式呢造寝,因?yàn)檫@是兩個(gè)進(jìn)程磕洪,直接傳遞對(duì)象是不行滴,只能是一些描述信息〗肓現(xiàn)在Client端進(jìn)程A聯(lián)系ServiceManager析显,說現(xiàn)在我需要進(jìn)程B中圖像處理的功能,ServiceManager從注冊(cè)表中查到了這個(gè)Binder實(shí)體签赃,但是呢谷异,它并不是直接把這個(gè)Binder實(shí)體直接給Client,而是給了一個(gè)Binder實(shí)體的代理分尸,或者說是引用,Client通過Binder的引用訪問Server歹嘹。分析到現(xiàn)在箩绍,有個(gè)關(guān)鍵的問題需要說一下,ServiceManager是一個(gè)進(jìn)程荞下,Server是另一個(gè)進(jìn)程伶选,Server向ServiceManager注冊(cè)Binder必然會(huì)涉及進(jìn)程間通信史飞。當(dāng)前實(shí)現(xiàn)的是進(jìn)程間通信卻又要用到進(jìn)程間通信尖昏,這就好象蛋可以孵出雞前提卻是要找只雞來孵蛋,確實(shí)是這樣的构资,ServiceManager中預(yù)先有了一個(gè)自己的Binder對(duì)象(實(shí)體)抽诉,就是那只雞,然后Server有個(gè)Binder對(duì)象的引用吐绵,就是那個(gè)蛋迹淌,Server需要通過這個(gè)Binder的引用來實(shí)現(xiàn)Binder的注冊(cè)。雞就一只己单,蛋有很多唉窃,ServiceManager進(jìn)程的Binder對(duì)象(實(shí)體)僅有一個(gè),其他進(jìn)程所擁有的全部都是它的代理纹笼。同樣一個(gè)Server端Binder實(shí)體也應(yīng)該只有一個(gè)纹份,對(duì)應(yīng)所有Client端全部都是它的代理。

我們?cè)俅卫斫庖幌翨inder是什么廷痘?在Binder通信模型的四個(gè)角色里面蔓涧;他們的代表都是“Binder”,一個(gè)Binder對(duì)象就代表了所有笋额,包括了Server元暴,Client,ServiceManager,這樣,對(duì)于Binder通信的使用者而言兄猩,不用關(guān)心實(shí)現(xiàn)的細(xì)節(jié)茉盏。對(duì)Server來說,Binder指的是Binder實(shí)體枢冤,或者說是本地對(duì)象援岩,對(duì)于Client來說,Binder指的是Binder代理對(duì)象掏导,也就是Binder的引用享怀。對(duì)于Binder驅(qū)動(dòng)而言,在Binder對(duì)象進(jìn)行跨進(jìn)程傳遞的時(shí)候趟咆,Binder驅(qū)動(dòng)會(huì)自動(dòng)完成這兩種類型的轉(zhuǎn)換添瓷。

簡(jiǎn)單的總結(jié)一下梅屉,通過上面一大段的分析,一個(gè)Server在使用的時(shí)候需要經(jīng)歷三個(gè)階段

  • 服務(wù)注冊(cè) Server端與SystemManager之間的IPC
  • 服務(wù)檢索 Client端與SystemManager之間的IPC
  • 服務(wù)使用 Client端與Server端之間的IPC

三鳞贷、AIDL過程分析

1坯汤、定義一個(gè)AIDL文件
Game.aidl

package test.wangjing.com.aidl.domain;
parcelable Game;

GameManager .aidl

package test.wangjing.com.aidl;

import test.wangjing.com.aidl.domain.Game;

interface GameManager {
          Game querryGameById(int pGameId);
}

2、定義遠(yuǎn)端服務(wù)Service
在遠(yuǎn)程服務(wù)中的onBind方法搀愧,實(shí)現(xiàn)AIDL接口的具體方法惰聂,并且返回Binder對(duì)象

    @Override
    public IBinder onBind(Intent intent) {
        return mGameManager;
    }

    GameManager.Stub mGameManager = new GameManager.Stub() {
        @Override
        public IBinder asBinder() {
            return null;
        }

        @Override
        public Game querryGameById(int pGameId) throws RemoteException {
            return mGameMap.get(pGameId);
        }
    };

3、本地創(chuàng)建連接對(duì)象

 private class GameServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

            mGameManager = (GameManager.Stub) GameManager.Stub.asInterface(iBinder);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    }
public void actionService(View view) {
        Intent intent = new Intent(this, GameService.class);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

以上就是一個(gè)遠(yuǎn)端服務(wù)的一般套路咱筛,如果是在兩個(gè)進(jìn)程中搓幌,就可以進(jìn)程通信了,現(xiàn)在我們分析一下迅箩,這個(gè)通信的流程溉愁。重點(diǎn)是GameManager這個(gè)編譯生成的類。

public interface GameManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements test.wangjing.com.aidl.GameManager {
        private static final java.lang.String DESCRIPTOR = "test.wangjing.com.aidl.GameManager";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an test.wangjing.com.aidl.GameManager interface,
         * generating a proxy if needed.
         */
        public static test.wangjing.com.aidl.GameManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof test.wangjing.com.aidl.GameManager))) {
                return ((test.wangjing.com.aidl.GameManager) iin);
            }
            return new test.wangjing.com.aidl.GameManager.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_querryGameById: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    test.wangjing.com.aidl.domain.Game _result = this.querryGameById(_arg0);
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements test.wangjing.com.aidl.GameManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public test.wangjing.com.aidl.domain.Game querryGameById(int pGameId) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                test.wangjing.com.aidl.domain.Game _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(pGameId);
                    mRemote.transact(Stub.TRANSACTION_querryGameById, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = test.wangjing.com.aidl.domain.Game.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_querryGameById = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public test.wangjing.com.aidl.domain.Game querryGameById(int pGameId) throws android.os.RemoteException;
}

從類的關(guān)系來看饲趋,首先接口GameManager 繼承 IInterface 拐揭,IInterface是一個(gè)接口,在GameManager內(nèi)部有一個(gè)內(nèi)部類Stub奕塑,Stub繼承了Binder堂污,(Binder實(shí)現(xiàn)了IBinder),并且實(shí)現(xiàn)了GameManager接口龄砰,在Stub中還有一個(gè)內(nèi)部類Proxy盟猖,Proxy也實(shí)現(xiàn)了GameManager接口,一個(gè)整體的結(jié)構(gòu)是這樣的

現(xiàn)在的問題是寝贡,Stub是什么扒披?Proxy又是什么?在上面說了在Binder通信模型的四個(gè)角色里面圃泡;他們的代表都是“Binder”碟案,一個(gè)Binder對(duì)象就代表了所有,包括了Server颇蜡,Clinet价说,ServiceManager,為了兩個(gè)進(jìn)程的通信风秤,系統(tǒng)給予的內(nèi)核支持是Binder,在抽象一點(diǎn)的說,Binder是系統(tǒng)開辟的一塊內(nèi)存空間鳖目,兩個(gè)進(jìn)程往這塊空間里面讀寫數(shù)據(jù)就行了,Stub從Binder中讀數(shù)據(jù)缤弦,Proxy向Binder中寫數(shù)據(jù),達(dá)到進(jìn)程間通信的目的领迈。首先我們分析Stub。

public static abstract class Stub extends android.os.Binder implements test.wangjing.com.aidl.GameManager

Stub 類繼承了Binder ,說明了Stub有了跨進(jìn)程傳輸?shù)哪芰晖保瑢?shí)現(xiàn)了GameManager接口衷蜓,說明它有了根據(jù)游戲ID查詢一個(gè)游戲的能力。我們?cè)赽ind一個(gè)Service之后尘喝,在onServiceConnecttion的回調(diào)里面磁浇,就是通過asInterface方法拿到一個(gè)遠(yuǎn)程的service的。

 public static test.wangjing.com.aidl.GameManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof test.wangjing.com.aidl.GameManager))) {
                return ((test.wangjing.com.aidl.GameManager) iin);
            }
            return new test.wangjing.com.aidl.GameManager.Stub.Proxy(obj);
        }

asInterface調(diào)用queryLocalInterface朽褪。

 public IInterface queryLocalInterface(String descriptor) {
        if (mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }

mDescriptor置吓,mOwner其實(shí)是Binder的成員變量,Stub繼承了Binder缔赠,在構(gòu)造函數(shù)的時(shí)候衍锚,對(duì)著兩個(gè)變量賦的值。

 public Stub() {
      this.attachInterface(this, DESCRIPTOR);
   }
public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }

如果客戶端和服務(wù)端是在一個(gè)進(jìn)程中橡淑,那么其實(shí)queryLocalInterface獲取的就是Stub對(duì)象构拳,如果不在一個(gè)進(jìn)程queryLocalInterface查詢的對(duì)象肯定為null咆爽,因?yàn)椴煌M(jìn)程有不同虛擬機(jī)梁棠,肯定查不到mOwner對(duì)象的,所以這時(shí)候其實(shí)是返回的Proxy對(duì)象了斗埂。拿到Stub對(duì)象后符糊,通常在onServiceConnected中,就把這個(gè)對(duì)象轉(zhuǎn)換成我們多定義AIDL接口呛凶。

private class GameServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mGameManager = (GameManager.Stub) GameManager.Stub.asInterface(iBinder);
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    }

比如我們這里會(huì)轉(zhuǎn)換成GameManager男娄,有了GameManager對(duì)象,就可以調(diào)用后querryGameById方法了漾稀。如果是一個(gè)進(jìn)程模闲,那直接調(diào)用的是自己的querryGameById方法,如果不是一個(gè)進(jìn)程崭捍,那調(diào)用了就是代理的querryGameById方法了尸折。

private static class Proxy implements test.wangjing.com.aidl.GameManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public test.wangjing.com.aidl.domain.Game querryGameById(int pGameId) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                test.wangjing.com.aidl.domain.Game _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(pGameId);
                    mRemote.transact(Stub.TRANSACTION_querryGameById, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = test.wangjing.com.aidl.domain.Game.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

看到其中關(guān)鍵的一行是

        mRemote.transact(Stub.TRANSACTION_querryGameById, _data, _reply, 0);

mRemote就是一個(gè)IBinder對(duì)象,相對(duì)于Stub殷蛇,Proxy 是組合關(guān)系(HAS-A)实夹,內(nèi)部有一個(gè)IBinder對(duì)象mRemote,Stub是繼承關(guān)系(IS-A)粒梦,直接實(shí)現(xiàn)了IBinder接口亮航。

public native boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException;

transact是個(gè)native方法,最終還會(huì)回掉JAVA層的onTransact方法匀们。

@Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_querryGameById: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    test.wangjing.com.aidl.domain.Game _result = this.querryGameById(_arg0);
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

onTransact根據(jù)調(diào)用號(hào)(每個(gè)AIDL函數(shù)都有一個(gè)編號(hào)缴淋,在跨進(jìn)程的時(shí)候,不會(huì)傳遞函數(shù),而是傳遞編號(hào)指明調(diào)用哪個(gè)函數(shù))調(diào)用相關(guān)函數(shù)重抖;在這個(gè)例子里面圆存,調(diào)用了Binder本地對(duì)象的querryGameById方法;這個(gè)方法將結(jié)果返回給驅(qū)動(dòng)仇哆,驅(qū)動(dòng)喚醒掛起的Client進(jìn)程里面的線程并將結(jié)果返回沦辙。于是一次跨進(jìn)程調(diào)用就完成了。

***Please accept mybest wishes for your happiness and success ! ***

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末讹剔,一起剝皮案震驚了整個(gè)濱河市油讯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌延欠,老刑警劉巖陌兑,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異由捎,居然都是意外死亡兔综,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門狞玛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來软驰,“玉大人,你說我怎么就攤上這事心肪《Э鳎” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵硬鞍,是天一觀的道長(zhǎng)慧瘤。 經(jīng)常有香客問我,道長(zhǎng)固该,這世上最難降的妖魔是什么锅减? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮伐坏,結(jié)果婚禮上怔匣,老公的妹妹穿的比我還像新娘。我一直安慰自己著淆,他們只是感情好劫狠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著永部,像睡著了一般独泞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上苔埋,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天懦砂,我揣著相機(jī)與錄音,去河邊找鬼。 笑死荞膘,一個(gè)胖子當(dāng)著我的面吹牛罚随,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播羽资,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼淘菩,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了屠升?” 一聲冷哼從身側(cè)響起潮改,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎腹暖,沒想到半個(gè)月后汇在,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脏答,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年糕殉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片殖告。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡阿蝶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出丛肮,到底是詐尸還是另有隱情赡磅,我是刑警寧澤魄缚,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布宝与,位于F島的核電站,受9級(jí)特大地震影響冶匹,放射性物質(zhì)發(fā)生泄漏习劫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一嚼隘、第九天 我趴在偏房一處隱蔽的房頂上張望诽里。 院中可真熱鬧,春花似錦飞蛹、人聲如沸谤狡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽墓懂。三九已至,卻和暖如春霉囚,著一層夾襖步出監(jiān)牢的瞬間捕仔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留榜跌,地道東北人闪唆。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像钓葫,于是被迫代替她去往敵國(guó)和親悄蕾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容