淺談Android進(jìn)程間通訊--Binder

淺談Android進(jìn)程間通訊(Binder)


進(jìn)程間通訊IPC不是Android中所特有的感憾,任何一個(gè)操作系統(tǒng)需要相應(yīng)的IPC機(jī)制膘婶。理解Binder對(duì)于理解整個(gè)Android系統(tǒng)有著非常重要的作用折汞,Android系統(tǒng)的四大組件会放,AMS,PMS等系統(tǒng)服務(wù)無(wú)一不與Binder掛鉤诫隅。
本文主要從以下幾個(gè)方面講解Binder:

  • linux進(jìn)程間通信相關(guān)背景知識(shí)
  • 圖解Binder通信模型
  • Java層的Binder
  • AIDL使用詳解
  • serverManager進(jìn)程與client進(jìn)程或server進(jìn)程的交互

1. linux進(jìn)程間通信相關(guān)知識(shí)

linux進(jìn)程模型.png

進(jìn)程隔離

進(jìn)程隔離是為保護(hù)操作系統(tǒng)中進(jìn)程互不干擾而設(shè)計(jì)的一組不同硬件和軟件的技術(shù)腐魂。這個(gè)技術(shù)是為了避免進(jìn)程A寫入進(jìn)程B的情況發(fā)生。 進(jìn)程的隔離實(shí)現(xiàn)逐纬,使用了虛擬地址空間蛔屹。進(jìn)程A的虛擬地址和進(jìn)程B的虛擬地址不同,這樣就防止進(jìn)程A將數(shù)據(jù)信息寫入進(jìn)程B豁生。

用戶空間/內(nèi)核空間

Linux Kernel 是操作系統(tǒng)的核心兔毒,獨(dú)立于普通的應(yīng)用程序,可以訪問(wèn)受保護(hù)的內(nèi)存空間甸箱,也有訪問(wèn)底層硬件設(shè)備的所有權(quán)限育叁。

對(duì)于Kernel這么一個(gè)高安全級(jí)別的東西,顯然是不容許其它的應(yīng)用程序隨便調(diào)用或訪問(wèn)的芍殖,所以需要對(duì)Kernel提供一定的保護(hù)機(jī)制擂红,這個(gè)保護(hù)機(jī)制用來(lái)告訴那些應(yīng)用程序,你只可以訪問(wèn)某些許可的資源围小,不許可的資源是拒絕被訪問(wèn)的昵骤,于是就把Kernel和上層的應(yīng)用程序抽像的隔離開(kāi),分別稱之為Kernel Space和User Space肯适。

內(nèi)核模塊/驅(qū)動(dòng)

通過(guò)系統(tǒng)調(diào)用变秦,用戶空間可以訪問(wèn)內(nèi)核空間,那么如果一個(gè)用戶空間想與另外一個(gè)用戶空間進(jìn)行通信怎么辦呢框舔?很自然想到的是讓操作系統(tǒng)內(nèi)核添加支持蹦玫;傳統(tǒng)的Linux通信機(jī)制赎婚,比如Socket,管道等都是內(nèi)核支持的樱溉;但是Binder并不是Linux內(nèi)核的一部分挣输,它是怎么做到訪問(wèn)內(nèi)核空間的呢?Linux的動(dòng)態(tài)可加載內(nèi)核模塊(Loadable Kernel Module福贞,LKM)機(jī)制解決了這個(gè)問(wèn)題撩嚼;模塊是具有獨(dú)立功能的程序,它可以被單獨(dú)編譯挖帘,但不能獨(dú)立運(yùn)行完丽。它在運(yùn)行時(shí)被鏈接到內(nèi)核作為內(nèi)核的一部分在內(nèi)核空間運(yùn)行。這樣拇舀,Android系統(tǒng)可以通過(guò)添加一個(gè)內(nèi)核模塊運(yùn)行在內(nèi)核空間逻族,用戶進(jìn)程之間的通過(guò)這個(gè)模塊作為橋梁,就可以完成通信了骄崩。

在Android系統(tǒng)中聘鳞,這個(gè)運(yùn)行在內(nèi)核空間的,負(fù)責(zé)各個(gè)用戶進(jìn)程通過(guò)Binder通信的內(nèi)核模塊叫做Binder驅(qū)動(dòng);

驅(qū)動(dòng)程序一般指的是設(shè)備驅(qū)動(dòng)程序(Device Driver)要拂,是一種可以使計(jì)算機(jī)和設(shè)備通信的特殊程序抠璃。相當(dāng)于硬件的接口,操作系統(tǒng)只有通過(guò)這個(gè)接口宇弛,才能控制硬件設(shè)備的工作鸡典;

驅(qū)動(dòng)就是操作硬件的接口源请,為了支持Binder通信過(guò)程枪芒,Binder使用了一種“硬件”,因此這個(gè)模塊被稱之為驅(qū)動(dòng)

2. Binder通信模型

binder-model.png

其中Server谁尸,Client舅踪,SMgr運(yùn)行于用戶空間,驅(qū)動(dòng)運(yùn)行于內(nèi)核空間良蛮。

整個(gè)通信步驟如下:

I. SM建立(建立通信錄)抽碌;首先有一個(gè)進(jìn)程向驅(qū)動(dòng)提出申請(qǐng)為SM;驅(qū)動(dòng)同意之后决瞳,SM進(jìn)程負(fù)責(zé)管理Service(注意這里是Service而不是Server货徙,因?yàn)槿绻ㄐ胚^(guò)程反過(guò)來(lái)的話,那么原來(lái)的客戶端Client也會(huì)成為服務(wù)端Server)不過(guò)這時(shí)候通信錄還是空的皮胡,一個(gè)號(hào)碼都沒(méi)有痴颊。

II. 各個(gè)Server向SM注冊(cè)(完善通信錄);每個(gè)Server端進(jìn)程啟動(dòng)之后屡贺,向SM報(bào)告蠢棱,我是zhangsan, 要找我請(qǐng)返回0x1234(這個(gè)地址沒(méi)有實(shí)際意義锌杀,類比);其他Server進(jìn)程依次如此泻仙;這樣SM就建立了一張表糕再,對(duì)應(yīng)著各個(gè)Server的名字和地址;就好比B與A見(jiàn)面了玉转,說(shuō)存?zhèn)€我的號(hào)碼吧吟宦,以后找我撥打10086牍疏;

III. Client想要與Server通信,首先詢問(wèn)SM;請(qǐng)告訴我如何聯(lián)系z(mì)hangsan舰蟆,SM收到后給他一個(gè)號(hào)碼0x1234;Client收到之后声滥,開(kāi)心滴用這個(gè)號(hào)碼撥通了Server的電話呜叫,于是就開(kāi)始通信了。

binder-procedure.png
Binder驅(qū)動(dòng)保存了每一個(gè)跨越進(jìn)程的Binder對(duì)象的相關(guān)信息怠李;在驅(qū)動(dòng)中圾叼,Binder本地對(duì)象的代表是一個(gè)叫做binder_node的數(shù)據(jù)結(jié)構(gòu),
Binder代理對(duì)象是用binder_ref代表的捺癞。

3. java層Binder

1. AIDL過(guò)程分析

首先server端的aidl接口

interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
    void registerListener(in OnNewBookArrivedListener onNewBookArrivedListener);
    void unregisterListener(in OnNewBookArrivedListener onNewBookArrivedListener);
}

編譯之后系統(tǒng)生成IBookManager


package com.chehejia.aidlserver;

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

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

        /**
         * Cast an IBinder object into an com.chehejia.aidlserver.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.chehejia.aidlserver.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.chehejia.aidlserver.IBookManager))) {
                return ((com.chehejia.aidlserver.IBookManager) iin);
            }
            return new com.chehejia.aidlserver.IBookManager.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_getBookList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.chehejia.aidlserver.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.chehejia.aidlserver.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.chehejia.aidlserver.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_registerListener: {
                    data.enforceInterface(DESCRIPTOR);
                    com.chehejia.aidlserver.OnNewBookArrivedListener _arg0;
                    _arg0 = com.chehejia.aidlserver.OnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
                    this.registerListener(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_unregisterListener: {
                    data.enforceInterface(DESCRIPTOR);
                    com.chehejia.aidlserver.OnNewBookArrivedListener _arg0;
                    _arg0 = com.chehejia.aidlserver.OnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
                    this.unregisterListener(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.chehejia.aidlserver.IBookManager {
            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 java.util.List<com.chehejia.aidlserver.Book> getBookList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.chehejia.aidlserver.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.chehejia.aidlserver.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addBook(com.chehejia.aidlserver.Book book) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void registerListener(com.chehejia.aidlserver.OnNewBookArrivedListener onNewBookArrivedListener) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder((((onNewBookArrivedListener != null)) ? (onNewBookArrivedListener.asBinder()) : (null)));
                    mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void unregisterListener(com.chehejia.aidlserver.OnNewBookArrivedListener onNewBookArrivedListener) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder((((onNewBookArrivedListener != null)) ? (onNewBookArrivedListener.asBinder()) : (null)));
                    mRemote.transact(Stub.TRANSACTION_unregisterListener, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_unregisterListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
    }

    public java.util.List<com.chehejia.aidlserver.Book> getBookList() throws android.os.RemoteException;

    public void addBook(com.chehejia.aidlserver.Book book) throws android.os.RemoteException;

    public void registerListener(com.chehejia.aidlserver.OnNewBookArrivedListener onNewBookArrivedListener) throws android.os.RemoteException;

    public void unregisterListener(com.chehejia.aidlserver.OnNewBookArrivedListener onNewBookArrivedListener) throws android.os.RemoteException;
}

2. IBinder/IInterface/Binder/BinderProxy/Stub總結(jié)

  • IBinder是一個(gè)接口夷蚊,它代表了一種跨進(jìn)程傳輸?shù)哪芰Γ恢灰獙?shí)現(xiàn)了這個(gè)接口髓介,就能將這個(gè)對(duì)象進(jìn)行跨進(jìn)程傳遞惕鼓;這是驅(qū)動(dòng)底層支持的;在跨進(jìn)程數(shù)據(jù)流經(jīng)驅(qū)動(dòng)的時(shí)候唐础,驅(qū)動(dòng)會(huì)識(shí)別IBinder類型的數(shù)據(jù)箱歧,從而自動(dòng)完成不同進(jìn)程Binder本地對(duì)象以及Binder代理對(duì)象的轉(zhuǎn)換。

  • IBinder負(fù)責(zé)數(shù)據(jù)傳輸一膨,那么client與server端的調(diào)用契約(這里不用接口避免混淆)呢呀邢?這里的IInterface代表就是遠(yuǎn)程server對(duì)象具有什么能力。具體來(lái)說(shuō)豹绪,就是aidl里面的接口价淌。

  • Java層的Binder類,代表的其實(shí)就是Binder本地對(duì)象瞒津。BinderProxy類是Binder類的一個(gè)內(nèi)部類蝉衣,它代表遠(yuǎn)程進(jìn)程的Binder對(duì)象的本地代理;這兩個(gè)類都繼承自IBinder, 因而都具有跨進(jìn)程傳輸?shù)哪芰ο矧剑粚?shí)際上病毡,在跨越進(jìn)程的時(shí)候,Binder驅(qū)動(dòng)會(huì)自動(dòng)完成這兩個(gè)對(duì)象的轉(zhuǎn)換钓辆。

  • 在使用AIDL的時(shí)候剪验,編譯工具會(huì)給我們生成一個(gè)Stub的靜態(tài)內(nèi)部類肴焊;這個(gè)類繼承了Binder, 說(shuō)明它是一個(gè)Binder本地對(duì)象,它實(shí)現(xiàn)了IInterface接口功戚,表明它具有遠(yuǎn)程Server承諾給 Client的能力娶眷;Stub是一個(gè)抽象類,具體的IInterface的相關(guān)實(shí)現(xiàn)需要我們手動(dòng)完成啸臀,這里使用了策略模式届宠。

4 AIDL中的注意點(diǎn)

  • 客戶端注冊(cè)的listener如果服務(wù)端采用普通list存儲(chǔ)的話,會(huì)造成取消注冊(cè)失敗乘粒。原因是客戶端注冊(cè)的listener經(jīng)過(guò)Binder傳輸?shù)椒?wù)端后豌注,會(huì)在服務(wù)端生成新的listener,導(dǎo)致客戶端注冊(cè)的和解綁的不是同一個(gè)對(duì)象灯萍。那么我們應(yīng)該使用什么方式進(jìn)行取消注冊(cè)的操作呢轧铁?答案是 RemoteCallbackList。
  • 客戶端調(diào)用遠(yuǎn)程服務(wù)端的方法旦棉,被調(diào)用的方法運(yùn)行在服務(wù)端的 Binder線程池中齿风,同時(shí)客戶端當(dāng)前調(diào)用線程被掛起,如果服務(wù)端的方法有耗時(shí)的操作绑洛,那么客戶端的調(diào)用線程必須為子線程來(lái)避免ANR救斑。

  • Binder可能會(huì)意外的死亡,需要客戶端重新連接服務(wù)真屯。這里有兩種方法脸候,第一種是給Binder設(shè)置DeathRecipient監(jiān)聽(tīng),binderDied回調(diào)(非UI線程)绑蔫。第二種是onserverDisconnected回調(diào)(UI線程)运沦。

5. ServiceManager與Binder

在Android啟動(dòng)ServiceManager進(jìn)程的時(shí)候,都做了什么事晾匠?

  1. 利用BINDER_SET_CONTEXT_MGR命令茶袒,令自己成為上下文管理者梯刚,其實(shí)也就是成為ServiceManager凉馆。
  2. 將BINDER_SET_CONTEXT_MGR命令傳給Binder驅(qū)動(dòng)的時(shí)候,Binder驅(qū)動(dòng)就會(huì)為其在內(nèi)核空間中創(chuàng)建一個(gè)節(jié)點(diǎn)(binder_node)亡资,句柄為0澜共。(0號(hào)引用)在整個(gè)系統(tǒng)中,只會(huì)有一個(gè)binder_context_mgr_node锥腻,所以也只會(huì)有一個(gè)ServiceManager的進(jìn)程嗦董,那么對(duì)ServiceManager的訪問(wèn),驅(qū)動(dòng)就可以在系統(tǒng)中定義好其句柄瘦黑,也就是 0京革。
  3. 進(jìn)入一個(gè)無(wú)限循環(huán)奇唤,等待Client的請(qǐng)求到來(lái)。

server進(jìn)程注冊(cè)到serverManager

  1. 每一個(gè)提供服務(wù)的Server都會(huì)通過(guò)Binder驅(qū)動(dòng)匹摇,將自身給注冊(cè)到ServiceManager中咬扇。(本質(zhì)上,就是Server們會(huì)將自身作為一個(gè)對(duì)象廊勃,封裝在數(shù)據(jù)包中懈贺,將這些數(shù)據(jù)復(fù)制到內(nèi)核空間中,由Binder驅(qū)動(dòng)訪問(wèn)坡垫。)
  2. Binder驅(qū)動(dòng)讀取數(shù)據(jù)包的時(shí)候梭灿,如果發(fā)現(xiàn)其中有Binder實(shí)體,那么也會(huì)為對(duì)應(yīng)的Binder實(shí)體創(chuàng)建對(duì)應(yīng)的Binder節(jié)點(diǎn)(BinderNode)冰悠。
  3. Binder驅(qū)動(dòng)也會(huì)為這些服務(wù)分配句柄(大于0)堡妒,同時(shí)會(huì)將這些句柄也記錄在Binder驅(qū)動(dòng)中,然后再將這些句柄和名字發(fā)送給ServiceManager溉卓,由ServiceManager來(lái)維護(hù)涕蚤。

Client進(jìn)程與serverManager交互

  1. server服務(wù)的名字,加上一個(gè)句柄為 0 的值的诵,封裝為一個(gè)數(shù)據(jù)包万栅,打開(kāi)Binder設(shè)備文件,將這個(gè)數(shù)據(jù)發(fā)送給Binder驅(qū)動(dòng)西疤。
  2. Binder驅(qū)動(dòng)接收到句柄為0烦粒,就會(huì)將這數(shù)據(jù)包扔給ServiceManager。
  3. ServiceManager接收到這個(gè)數(shù)據(jù)包代赁,就會(huì)分析扰她,發(fā)現(xiàn)是要找某個(gè)名字的服務(wù),于是就找找找芭碍,然后將對(duì)應(yīng)服務(wù)的句柄發(fā)送回來(lái)(某大于0的句柄)徒役。
  4. 驅(qū)動(dòng)再server進(jìn)程句柄發(fā)回給Client。
  5. Client獲取句柄之后窖壕,就會(huì)再加上想要的服務(wù)忧勿,還有這個(gè)句柄,再發(fā)送給Binder驅(qū)動(dòng)瞻讽,Binder驅(qū)動(dòng)就會(huì)找到對(duì)應(yīng)的句柄鸳吸,然后調(diào)用server進(jìn)程相關(guān)服務(wù)。
Binder詳解.gif

參考
binder學(xué)習(xí)指南

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末速勇,一起剝皮案震驚了整個(gè)濱河市晌砾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烦磁,老刑警劉巖养匈,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哼勇,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡呕乎,警方通過(guò)查閱死者的電腦和手機(jī)猴蹂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)楣嘁,“玉大人磅轻,你說(shuō)我怎么就攤上這事≈鹦椋” “怎么了聋溜?”我有些...
    開(kāi)封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)叭爱。 經(jīng)常有香客問(wèn)我撮躁,道長(zhǎng),這世上最難降的妖魔是什么买雾? 我笑而不...
    開(kāi)封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任把曼,我火速辦了婚禮,結(jié)果婚禮上漓穿,老公的妹妹穿的比我還像新娘嗤军。我一直安慰自己,他們只是感情好晃危,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布叙赚。 她就那樣靜靜地躺著,像睡著了一般僚饭。 火紅的嫁衣襯著肌膚如雪震叮。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天鳍鸵,我揣著相機(jī)與錄音苇瓣,去河邊找鬼。 笑死偿乖,一個(gè)胖子當(dāng)著我的面吹牛击罪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播汹想,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼外邓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼撤蚊!你這毒婦竟也來(lái)了古掏?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤侦啸,失蹤者是張志新(化名)和其女友劉穎槽唾,沒(méi)想到半個(gè)月后丧枪,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡庞萍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年拧烦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钝计。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡恋博,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出私恬,到底是詐尸還是另有隱情债沮,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布本鸣,位于F島的核電站疫衩,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏荣德。R本人自食惡果不足惜闷煤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望涮瞻。 院中可真熱鬧鲤拿,春花似錦、人聲如沸署咽。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)艇抠。三九已至幕庐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間家淤,已是汗流浹背异剥。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留絮重,地道東北人冤寿。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像青伤,于是被迫代替她去往敵國(guó)和親督怜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

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