android binder深入分析-承上啟下的native層(客戶端部分)

image.png

相信大家對(duì)上面這一副binder跨進(jìn)程通信的圖都應(yīng)該比較熟悉天吓,圖中的主要有4個(gè)角色:
1鲫凶、客戶端(A進(jìn)程)
2许布、SerivceManager
3、服務(wù)端(B進(jìn)程)
4箍铭、binder驅(qū)動(dòng)
平時(shí)一問到binder是怎么跨進(jìn)程通信的泊柬?那么同學(xué)們肯定大部分回答:binder最后調(diào)到底層還是通過binder驅(qū)動(dòng)來實(shí)現(xiàn)跨進(jìn)程通信的。那么這里就要問一下:請(qǐng)問android中app的java層binder調(diào)用诈火,是怎么一步步調(diào)用到了底層binder驅(qū)動(dòng)呢兽赁?
image.png

這個(gè)中間問號(hào)部分也就本節(jié)要給大家重點(diǎn)分享的部分。

1.ServiceManager方法調(diào)用也是binder跨進(jìn)程通信

跨進(jìn)程通信那么必然會(huì)出現(xiàn)有兩端冷守,即客戶端和服務(wù)端這樣的C/S模型刀崖,那么問題是C端怎么知道的S端?那么這就會(huì)引出一個(gè)重要的角色ServiceManager拍摇,ServiceManager本身工作相對(duì)簡(jiǎn)單亮钦,其功能:查詢和注冊(cè)服務(wù)〕浠睿客戶端通過ServiceManager查詢到了注冊(cè)服務(wù)后蜂莉,才可以進(jìn)行跨進(jìn)程通信,這里就是常說的ServiceManager像個(gè)DNS服務(wù)器混卵。但問題來了映穗,ServiceManager本身自己就是一個(gè)獨(dú)立進(jìn)程,那么客戶端去SerivceManager中查詢服務(wù)也肯定是跨進(jìn)程通信幕随,所以本節(jié)分析跨進(jìn)程通信時(shí)就拿ServiceManager.getService這個(gè)跨進(jìn)程調(diào)用來展開分析蚁滋。

2.ServiceManager接口獲取分析

平時(shí)系統(tǒng)中獲取某一個(gè)服務(wù),一般都是最后都是ServiceManager.getService這種方式合陵,來看看它的源碼:

//ServiceManager.java中
    public static IBinder getService(String name) {
        try {
            //緩存中獲取枢赔,第一次緩存沒有
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                //Binder.allowBlocking僅僅是為了提示是否為阻塞行接口,沒有實(shí)際干活
                return Binder.allowBlocking(getIServiceManager().getService(name));
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }

這里大家可以看到實(shí)際真正干活的是getIServiceManager().getService(name)拥知,這里首先來看getIServiceManager()方法:

//ServiceManager.java中
   private static IServiceManager getIServiceManager() {
      //這里有一個(gè)sServiceManager來緩存踏拜,但是第一次肯定還為null
        if (sServiceManager != null) {
            return sServiceManager;
        }
        // 這里用調(diào)用了ServiceManagerNative類的asInterface方法和BinderInternal.getContextObject()方法
        sServiceManager = ServiceManagerNative
                .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
        return sServiceManager;
    }

這里又調(diào)用到了ServiceManagerNative類的asInterface方法這方法本身應(yīng)該在應(yīng)用寫aidl生成的java文件中也經(jīng)常見到:

//ServiceManagerNative.java
  static public IServiceManager asInterface(IBinder obj)
    {
        if (obj == null) {
            return null;
        }
        IServiceManager in =
            (IServiceManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }
        //其實(shí)就是 new ServiceManagerProxy代理對(duì)象,參數(shù)是IBinder 類型的對(duì)象
        return new ServiceManagerProxy(obj);
    }

它的核心就是new ServiceManagerProxy低剔,但是真正干活的還是IBinder參數(shù)速梗,參數(shù)getIServiceManager方法中可以看出獲取其實(shí)是核心是BinderInternal.getContextObject()方法,但getContextObject是個(gè)native的方法襟齿,所以就得看對(duì)應(yīng)的jni方法:

//android_util_Binder.cpp
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
   //調(diào)用getContextObject構(gòu)造出一個(gè)IBinder類型對(duì)象
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    //把C++層面的IBinder類型對(duì)象轉(zhuǎn)成java成的IBinder對(duì)象
    return javaObjectForIBinder(env, b);
}

這里一共才2行代碼姻锁,那就一行行深入分析,首先來看ProcessState::self()->getContextObject:

//ProcessState.cpp
//這里直接就沒有使用傳遞來的參數(shù)猜欺,本身傳遞來的也是個(gè)NULL
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    return getStrongProxyForHandle(0);
}

ProcessState這里又調(diào)用了getStrongProxyForHandle方法位隶,而且傳遞了一個(gè)參數(shù)0:

//ProcessState.cpp
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);
   //根據(jù)handle查詢handle_entry對(duì)象,handle沒有初始化過則構(gòu)造一個(gè)handle_entry空殼出來
    handle_entry* e = lookupHandleLocked(handle);
  //因?yàn)閔andle_entry已經(jīng)有了开皿,只不過是一個(gè)空殼里面內(nèi)容都為空
    if (e != NULL) {
        IBinder* b = e->binder;
      //這里e->binder第一次肯定為空
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
          //如果傳入handle為0
            if (handle == 0) {
                Parcel data;
               //這里會(huì)調(diào)用 IPCThreadState::self()->transact方法來傳遞一個(gè)PING_TRANSACTION涧黄,主要目的
               //是為了試探ServiceManager是否還存活
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }
            //利用handle值構(gòu)造出一個(gè)BpBinder對(duì)象顿肺,最后返回也是這個(gè)BpBinder對(duì)象
            b = new BpBinder(handle); 
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }
    return result;
}

到這里我們就看到了最后其實(shí)就是new BpBinder(handle);授滓,這里handle就是我們經(jīng)常說 “引用”,即對(duì)遠(yuǎn)程對(duì)象的引用,這里因?yàn)镾erviceManager的的特殊性闰围,所以android系統(tǒng)中默認(rèn)把任何進(jìn)程對(duì)ServiceManager的引用handle值都設(shè)置為固定的0帆卓。那么到這里就分析完了ProcessState::self()->getContextObject(NULL)了纫骑,返回的其實(shí)就是個(gè)new BpBinder(0)衙熔,但是這里還是C++層的BpBinder對(duì)象,而我們是要獲取Java層的IBinder類型的對(duì)象月帝,那接下來就要分析上面剩下的 javaObjectForIBinder(env, b):

//android_util_Binder.cpp
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
        //省略部分
    //這里構(gòu)造出了gBinderProxyOffsets的java對(duì)象
    object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
    if (object != NULL) {
        // The proxy holds a reference to the native object.
      //這里吧val這個(gè)c++的對(duì)象指針設(shè)置到gBinderProxyOffsets.mObject這個(gè)java屬性中
        env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());
        val->incStrong((void*)javaObjectForIBinder);

        // The native object needs to hold a weak reference back to the
        // proxy, so we can retrieve the same proxy if it is still active.
        jobject refObject = env->NewGlobalRef(
                env->GetObjectField(object, gBinderProxyOffsets.mSelf));
        //前面只是基于val建立對(duì)應(yīng)的java對(duì)象躏惋,及java對(duì)象可以直接拿到val指針
       //但是我們進(jìn)程也需要val指針可以獲取到j(luò)ava對(duì)象
        val->attachObject(&gBinderProxyOffsets, refObject,
                jnienv_to_javavm(env), proxy_cleanup);

     //省略部分
    }

    return object;
}

這里面gBinderProxyOffsets相關(guān)的是什么呢?這里再來看它的對(duì)應(yīng)初始化代碼:

//android_util_Binder.cpp
const char* const kBinderProxyPathName = "android/os/BinderProxy";

static int int_register_android_os_BinderProxy(JNIEnv* env)
{
    jclass clazz = FindClassOrDie(env, "java/lang/Error");
    gErrorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
  //通過jni找出android/os/BinderProxy這個(gè)java類
    clazz = FindClassOrDie(env, kBinderProxyPathName);
    gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
  //通過jni找出android/os/BinderProxy這個(gè)java類的構(gòu)造方法
    gBinderProxyOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "<init>", "()V");
    gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
            "(Landroid/os/IBinder$DeathRecipient;)V");
///通過jni找出android/os/BinderProxy這個(gè)java類的mObject 屬性
    gBinderProxyOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
//省略部分
}

這里面可以看出其實(shí)都是通過jni相關(guān)方法嫁赏,讓native的相關(guān)變量與java層的BinderProxy相綁定一起其掂,到這里就可以看出在BinderProxy是在native層面被構(gòu)造出來的,設(shè)置了它的mObject值就是c++層的BpBinder指針潦蝇,那么到這里就已經(jīng)分析完了開始的getIServiceManager方法款熬,這個(gè)時(shí)候就有了ServiceManager的Proxy代理,總結(jié)以下幾點(diǎn)核心部分:
1攘乒、ServiceManager特殊贤牛,它的handle固定為0,native層直接進(jìn)行new BpBinder(0)
2则酝、native層面的BpBinder需要轉(zhuǎn)換成Java層的BinderProxy殉簸,這個(gè)需要native通過jni構(gòu)造出java層對(duì)應(yīng)的BinderProxy,且BinderProxy的mObject值就是BpBinder(0)的指針沽讹,mObject也是Java層面與native層面進(jìn)行互通的最關(guān)鍵紐帶

image.png

3.getService獲取遠(yuǎn)程服務(wù)代理的源碼分析

首先來看ServiceManagerNative.java中的ServiceManagerProxy類中的getService

//ServiceManagerNative.java
    public IBinder getService(String name) throws RemoteException {
         //準(zhǔn)備輸入和返回Parcel對(duì)象
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IServiceManager.descriptor);
        data.writeString(name);
      //這里已經(jīng)知道m(xù)Remote其實(shí)就是前面分析構(gòu)造出來的BinderProxy對(duì)象
        mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
      //讀取返回的IBinder數(shù)據(jù)
        IBinder binder = reply.readStrongBinder();
        reply.recycle();
        data.recycle();
        return binder;
    }

這里其實(shí)核心就是 mRemote.transact和 reply.readStrongBinder兩個(gè)部分般卑,這里先來分析transact部分,
這個(gè)mRemote對(duì)象是BinderProxy對(duì)象爽雄,所以來看BinderProxy類的transact方法蝠检,注意這里BinderProxy沒有單獨(dú)java文件,它在Binder.java文件中:

//Binder.java
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
//省略
        try {
            return transactNative(code, data, reply, flags);
        } finally {
           //省略
        }
    }

這里其實(shí)又是調(diào)用了一個(gè)native方法transactNative挚瘟,注意這里transactNative到android_os_BinderProxy_transact是有一個(gè)轉(zhuǎn)換關(guān)系的叹谁,這個(gè)在分析android系統(tǒng)源碼時(shí)候很常見:


image.png
//android_util_Binder.cpp
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
   //省略部分
//這里從BinderProxy類型的java對(duì)象中獲取mObject屬性,前面分析過它的值就是BpBinder對(duì)象指針
    IBinder* target = (IBinder*)env->GetLongField(obj, gBinderProxyOffsets.mObject);
   //省略部分
//調(diào)用BpBinder對(duì)象的transact方法
    status_t err = target->transact(code, *data, reply, flags);
//省略部分
}

這里就可以看出前面講的mObject為啥是“重要紐帶”乘盖,那么接下來看看BpBinder的transact方法:

//BpBinder.cpp
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    if (mAlive) {
       //本質(zhì)是調(diào)用了IPCThreadState的transact方法
        status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }
    return DEAD_OBJECT;
}

那么接下來看看IPCThreadState的transact方法:

//IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
 //省略部分
    if (err == NO_ERROR) {
      //準(zhǔn)備好與驅(qū)動(dòng)通信的數(shù)據(jù)格式結(jié)構(gòu)體
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }
//如果不是TF_ONE_WAY即oneway類型的調(diào)用
    if ((flags & TF_ONE_WAY) == 0) {
        if (reply) {
          //這里需要等待答復(fù)
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
    } else {
//即oneway類型的調(diào)用
        err = waitForResponse(NULL, NULL);
    }
    return err;
}

這里面分為兩個(gè)大塊writeTransactionData和waitForResponse焰檩,writeTransactionData主要目的就是把數(shù)據(jù)組裝成和驅(qū)動(dòng)一致的binder_transaction_data結(jié)構(gòu)體,然后在是waitForResponse:

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;

    while (1) {
        //與驅(qū)動(dòng)進(jìn)行通信订框,這里主要通過相關(guān)ioctl相關(guān)方法
        if ((err=talkWithDriver()) < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        //得到驅(qū)動(dòng)返回cmd
        cmd = (uint32_t)mIn.readInt32();
        switch (cmd) {
        case BR_REPLY:
            {
                binder_transaction_data tr;
              //得到驅(qū)動(dòng)返回?cái)?shù)據(jù)
                err = mIn.read(&tr, sizeof(tr));
                //省略部分
         }
//省略部分
}

可以看出主要就是talkWithDriver與驅(qū)動(dòng)進(jìn)行通信并且一直等待返回驅(qū)動(dòng)相應(yīng)結(jié)果析苫,這里涉及驅(qū)動(dòng)部分就不給大家在這里講解,大家把驅(qū)動(dòng)暫時(shí)當(dāng)作黑盒既可以。
這個(gè)地方簡(jiǎn)單看看ServiceManager這個(gè)服務(wù)端衩侥,面對(duì)客戶端的getService做了什么呢浪腐?
在servicemanager代碼中如果接受到客戶端請(qǐng)求,最后都會(huì)調(diào)用到svcmgr_handler來負(fù)責(zé)處理:

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    struct svcinfo *si;
    uint16_t *s;
    size_t len;
    uint32_t handle;
    uint32_t strict_policy;
    int allow_isolated;
//..省略部分
    switch(txn->code) {
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
     //根據(jù)獲取到service的name
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
      //根據(jù)name從已經(jīng)存在集合中找出對(duì)應(yīng)的handle即索引
        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
        if (!handle)
            break;
      //把handle獲取到了后寫入reply
        bio_put_ref(reply, handle);
        return 0;
//..省略部分
    default:
        ALOGE("unknown code %d\n", txn->code);
        return -1;
    }

    bio_put_uint32(reply, 0);
    return 0;
}

這里面根據(jù)name從集合中尋找出了服務(wù)的handle,注意這里只是找出了一個(gè)int類型的handle顿乒,然后在調(diào)用bio_put_ref進(jìn)行寫入reply,再通過binder驅(qū)動(dòng)傳遞給客戶端泽谨,這里看看bio_put_ref方法:

void bio_put_ref(struct binder_io *bio, uint32_t handle)
{
//構(gòu)造出一個(gè)binder結(jié)構(gòu)體
    struct flat_binder_object *obj;

    if (handle)
        obj = bio_alloc_obj(bio);
    else
        obj = bio_alloc(bio, sizeof(*obj));

    if (!obj)
        return;
//給flat_binder_object 結(jié)構(gòu)體對(duì)應(yīng)屬性賦值
    obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
 //設(shè)置類型為BINDER_TYPE_HANDLE
    obj->type = BINDER_TYPE_HANDLE;
   //把handle賦值給flat_binder_object 的handle屬性
    obj->handle = handle;
    obj->cookie = 0;
}

這里大概我們就清除了原來servicemanager這一邊做的事情就是根據(jù)name查找到這個(gè)name對(duì)應(yīng)的handle,再把這個(gè)handle值包裝到flat_binder_object 結(jié)構(gòu)體中璧榄,再把結(jié)構(gòu)體傳遞到驅(qū)動(dòng),驅(qū)動(dòng)再傳遞到客戶端吧雹,具體傳遞過程就不在這一節(jié)詳細(xì)講述骨杂,分析這里相當(dāng)于客戶端調(diào)用transact方法后,服務(wù)端servicemanager的數(shù)據(jù)經(jīng)過binder驅(qū)動(dòng)進(jìn)行了返回雄卷。但是返回后的數(shù)據(jù)是怎么一步步又變成IBinder對(duì)象的呢搓蚪?這里我們接下來看 IBinder getService(String name)的 IBinder binder = reply.readStrongBinder()方法了,這里看著就是從Parcel類型的reply中調(diào)用了readStrongBinder方法就返回了IBinder對(duì)象丁鹉。注意啦Parcel這里是java層的妒潭,但是最后調(diào)用它又會(huì)調(diào)用到Parcel.cpp中,先看Parcel.java:

//Parcel.java
  public final IBinder readStrongBinder() {
        return nativeReadStrongBinder(mNativePtr);
    }

這里其實(shí)就只調(diào)用了nativeReadStrongBinder方法揣钦,它又是一個(gè)jni方法雳灾,所以這個(gè)方法后就會(huì)調(diào)用到native層,具體看一下這個(gè)方法在native層映射成了哪個(gè)方法:

//android_os_Parcel.cpp
    {"nativeReadStrongBinder",    "(J)Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder},

這里被映射成了android_os_Parcel_readStrongBinder方法:

//android_os_Parcel.cpp
static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        return javaObjectForIBinder(env, parcel->readStrongBinder());
    }
    return NULL;
}

這里又會(huì)先調(diào)用parcel->readStrongBinder()獲取native層的BpBinder對(duì)象作為參數(shù)冯凹,然后再調(diào)用javaObjectForIBinder轉(zhuǎn)為java對(duì)象谎亩,這個(gè)前面已經(jīng)講過了哦。

//Parcel.cpp
sp<IBinder> Parcel::readStrongBinder() const
{
    sp<IBinder> val;
//這里又調(diào)用了readNullableStrongBinder
    readNullableStrongBinder(&val);
    return val;
}

接下來看看readNullableStrongBinder:

status_t Parcel::readNullableStrongBinder(sp<IBinder>* val) const
{
    return unflatten_binder(ProcessState::self(), *this, val);
}

這里又調(diào)用到了unflatten_binder:

status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);

    if (flat) {
        switch (flat->type) {
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
              //這里核心又是獲取了handle后調(diào)用getStrongProxyForHandle
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

這里我們前面分析servicemanager時(shí)候就知道type類型為BINDER_TYPE_HANDLE宇姚,所以根據(jù)flat->handle的handle值調(diào)用getStrongProxyForHandle匈庭,這個(gè)getStrongProxyForHandle方法前面已經(jīng)分析過他就是new BpBinder(handle),所以這里就明白了這個(gè)getService方法最后也是根據(jù)servicemanager返回的handle(這里大家要注意這個(gè)客戶端的handle值和servicemanager中寫入的handle值不一定相等浑劳,而是各種進(jìn)程獨(dú)立阱持,但是在binder驅(qū)動(dòng)可以關(guān)聯(lián)上,具體區(qū)別會(huì)binder驅(qū)動(dòng)中進(jìn)行剖析)呀洲。


image.png

getService部分的總結(jié):
1紊选、獲取遠(yuǎn)端Serivce整體依然是new BpBinder(handle) --> javaObjectForIBinder變成BinderProxy的過程
2、但這里的handle不再像ServiceManager那么簡(jiǎn)單的直接寫死為0道逗,而是需要把name傳遞到SeriveManager查詢對(duì)應(yīng)的服務(wù)兵罢,ServiceManager把對(duì)應(yīng)服務(wù)flat_binder_object對(duì)象通過binder驅(qū)動(dòng)傳遞到客戶端,客戶端再?gòu)膄lat_binder_object獲取handle

至此我們就分析完了客戶端發(fā)起一個(gè)調(diào)用到獲取驅(qū)動(dòng)數(shù)據(jù)返回的整個(gè)過程滓窍,那么接下要分析的是作為一個(gè)服務(wù)端即Binder實(shí)現(xiàn)端卖词。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子此蜈,更是在濱河造成了極大的恐慌即横,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件裆赵,死亡現(xiàn)場(chǎng)離奇詭異东囚,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)战授,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門页藻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人植兰,你說我怎么就攤上這事份帐。” “怎么了楣导?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵废境,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我筒繁,道長(zhǎng)噩凹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任膝晾,我火速辦了婚禮栓始,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘血当。我一直安慰自己幻赚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布臊旭。 她就那樣靜靜地躺著落恼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪离熏。 梳的紋絲不亂的頭發(fā)上佳谦,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音滋戳,去河邊找鬼钻蔑。 笑死,一個(gè)胖子當(dāng)著我的面吹牛奸鸯,可吹牛的內(nèi)容都是我干的咪笑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼娄涩,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼窗怒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤扬虚,失蹤者是張志新(化名)和其女友劉穎努隙,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辜昵,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡荸镊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了堪置。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贷洲。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖晋柱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情诵叁,我是刑警寧澤雁竞,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站拧额,受9級(jí)特大地震影響碑诉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜侥锦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一进栽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧恭垦,春花似錦快毛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至玄柏,卻和暖如春襟衰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背粪摘。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工瀑晒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人徘意。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓苔悦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親映砖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子间坐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

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