[Android]從一個(gè)簡(jiǎn)單的AIDL實(shí)現(xiàn)看binder原理(四)bindService調(diào)用過(guò)程中Binder的寫(xiě)入

參考鏈接:

從一個(gè)簡(jiǎn)單的AIDL實(shí)現(xiàn)看binder原理(一)簡(jiǎn)單的AIDL實(shí)現(xiàn)
從一個(gè)簡(jiǎn)單的AIDL實(shí)現(xiàn)看binder原理(二)bindService的調(diào)用過(guò)程
從一個(gè)簡(jiǎn)單的AIDL實(shí)現(xiàn)看binder原理(三)bindService調(diào)用過(guò)程中Binder的傳遞

在上一篇博文中缔杉,我們分析了在bindService的過(guò)程中binder對(duì)象是怎么從Service進(jìn)程傳遞到Activity所在進(jìn)程的矛物,但是為什么傳遞的時(shí)候是一個(gè)Binder,但最后接收到的是個(gè)BinderProxy并沒(méi)有分析到布疼,本篇講分析Binder對(duì)象是在什么時(shí)機(jī)轉(zhuǎn)換為BinderProxy對(duì)象的
首先看一下Binder和BinderProxy的類(lèi)結(jié)構(gòu):


Binder
BinderProxy

從上圖可以看出恨溜,Binder和BinderProxy是實(shí)現(xiàn)了IBinder接口的兩個(gè)類(lèi)止后,Binder和BinderProxy是不存在繼承關(guān)系的湘捎,所以不可能進(jìn)行強(qiáng)轉(zhuǎn)靶衍,最有可能的觸發(fā)節(jié)點(diǎn)就是在IPC的過(guò)程中,接下來(lái)我們驗(yàn)證一下這種猜測(cè)

我們繼續(xù)從ActivityThread#handleBindService方法說(shuō)起

private void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        if (DEBUG_SERVICE)
            Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                try {
                    if (!data.rebind) {
                        IBinder binder = s.onBind(data.intent);
                        ActivityManager.getService().publishService(
                                data.token, data.intent, binder);
                    } else {
                        s.onRebind(data.intent);
                        ActivityManager.getService().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            } catch (Exception e) {
                if (!mInstrumentation.onException(s, e)) {
                    throw new RuntimeException(
                            "Unable to bind to service " + s
                            + " with " + data.intent + ": " + e.toString(), e);
                }
            }
        }
    }

在代碼的第11行這里調(diào)用了Service的onBinder方法夫植,我們回顧一下第一篇博文中Service的實(shí)現(xiàn):

 @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        StudentStub stub = new StudentStub();
        Log.d("ipcLog", "stub = " + stub.getClass());
        return stub;
    }
    public class StudentStub extends IAIDLInterface.Stub {
        @Override
        public List<Student> getStudent() throws RemoteException {
            return studentList;
        }

        @Override
        public void setStudent(Student student) throws RemoteException {
            studentList.add(student);
        }
    }

從這里可以看出讹剔,onBind的返回時(shí)其實(shí)就是StudentStub 對(duì)象,也就是一個(gè)Binder對(duì)象详民,
我們繼續(xù)看第12行代碼:

ActivityManager.getService().publishService(
                                data.token, data.intent, binder);

這是一個(gè)IPC過(guò)程延欠,我們回顧一下第一篇博文中IPC的發(fā)起:

 @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_start:
                bindService(new Intent(this, RemoteService.class), connection, BIND_AUTO_CREATE);
                break;

            case R.id.btn_add:
                int code = Integer.valueOf(codeEt.getText().toString().trim());
                String name = nameEt.getText().toString().trim();
                try {
                    proxy.setStudent(new Student(code, name));
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;

            case R.id.btn_get:
                try {
                    List<Student> students = proxy.getStudent();
                    if (students != null && students.size() > 0) {
                        StringBuilder sb = new StringBuilder();
                        for (Student student : students) {
                            sb.append(student.toString());
                            sb.append("\n");
                        }
                        studentListText.setText(sb.toString());
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;

            default:
                break;
        }
    }

從上述代碼可以看到,在我們發(fā)起IPC的請(qǐng)求時(shí)沈跨,使用的是BinderBorxy的對(duì)象由捎,同理我們可以得出,在跨進(jìn)程調(diào)用ActivityManagerService中的方法時(shí)饿凛,也是使用AMS的BinderProxy對(duì)象狞玛,這個(gè)對(duì)象的實(shí)現(xiàn)是在IActivityManager的Proxy類(lèi)中,Proxy類(lèi)是Stub的內(nèi)部類(lèi)涧窒,而Stub是IActivityManager的內(nèi)部類(lèi),其類(lèi)文件結(jié)構(gòu)如圖所示:


image.png

看到這里是不是有一種豁然開(kāi)朗的感覺(jué)心肪?原來(lái)大名鼎鼎的ActivityMangerService的實(shí)現(xiàn)原理和我們普通的AIDL是一模一樣的!
我們繼續(xù)跟進(jìn)publishService方法的實(shí)現(xiàn)纠吴,首先看發(fā)起端硬鞍,即IActivityManager$Stub中Proxy的實(shí)現(xiàn):

@Override
            public void publishService(android.os.IBinder token, android.content.Intent intent, android.os.IBinder service) 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(token);
                    if ((intent != null)) {
                        _data.writeInt(1);
                        intent.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    _data.writeStrongBinder(service);
                    mRemote.transact(Stub.TRANSACTION_publishService, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

方法的第三個(gè)參數(shù)就是我們要傳遞的RemoteService中的Binder,在這里將他傳入了Parcel的writeStrongBinder方法的參數(shù)里戴已,繼續(xù)跟進(jìn)Parcel的writeStrongBinder方法:

public final void writeStrongBinder(IBinder val) {
        nativeWriteStrongBinder(mNativePtr, val);
    }

這里轉(zhuǎn)入了jni層的nativeWriteStrongBinder方法繼續(xù)執(zhí)行固该,具體實(shí)現(xiàn)在android_os_Parcel.cpp中:

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
}

這里轉(zhuǎn)到了Parcel.cpp的writeStrongBinder方法中,其傳遞的參數(shù)是android_util_Binder.cpp的ibinderForJavaObject方法的返回值:

// android_util_Binder.cpp
sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{
    if (obj == NULL) return NULL;

    //Java層的Binder對(duì)象
    if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
        JavaBBinderHolder* jbh = (JavaBBinderHolder*)
            env->GetLongField(obj, gBinderOffsets.mObject);
        return jbh != NULL ? jbh->get(env, obj) : NULL; //【見(jiàn)3.5.3】
    }
    //Java層的BinderProxy對(duì)象
    if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
        return (IBinder*)env->GetLongField(obj, gBinderProxyOffsets.mObject);
    }
    return NULL;
}

這個(gè)方法的主要工作是根據(jù)Binde(Java)生成JavaBBinderHolder(C++)對(duì)象. 主要工作是創(chuàng)建JavaBBinderHolder對(duì)象,并把JavaBBinderHolder對(duì)象地址保存到Binder.mObject成員變量.

// android_util_Binder.cpp
sp<JavaBBinder> get(JNIEnv* env, jobject obj)
{
    AutoMutex _l(mLock);
    sp<JavaBBinder> b = mBinder.promote();
    if (b == NULL) {
        //首次進(jìn)來(lái)糖儡,創(chuàng)建JavaBBinder對(duì)象【見(jiàn)3.5.4】
        b = new JavaBBinder(env, obj);
        mBinder = b;
    }
    return b;
}

JavaBBinder的初始化在這里:

// android_util_Binder.cpp
JavaBBinder(JNIEnv* env, jobject object)
    : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object))
{
    android_atomic_inc(&gNumLocalRefs);
    incRefsCreated(env);
}

創(chuàng)建JavaBBinder伐坏,該對(duì)象繼承于BBinder對(duì)象。

data.writeStrongBinder(service)最終等價(jià)于parcel->writeStrongBinder(new JavaBBinder(env, obj));
也就是將java的Binder對(duì)象轉(zhuǎn)成了C++的BBinder對(duì)象

分析過(guò)參數(shù)后握联,繼續(xù)分析Parcel.cpp的writeStrongBinder方法:

// Parcel.cpp
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}

status_t flatten_binder(const sp<ProcessState>& /*proc*/,
    const sp<IBinder>& binder, Parcel* out)
{
    flat_binder_object obj;

    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    if (binder != NULL) {
        IBinder *local = binder->localBinder();
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.type = BINDER_TYPE_HANDLE; //遠(yuǎn)程Binder
            obj.binder = 0;
            obj.handle = handle;
            obj.cookie = 0;
        } else {
            obj.type = BINDER_TYPE_BINDER; //本地Binder桦沉,進(jìn)入該分支
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);
        }
    } else {
        obj.type = BINDER_TYPE_BINDER;  //本地Binder
        obj.binder = 0;
        obj.cookie = 0;
    }
    return finish_flatten_binder(binder, obj, out);
}

對(duì)于Binder實(shí)體,則cookie記錄Binder實(shí)體的指針拴疤;
對(duì)于Binder代理永部,則用handle記錄Binder代理的句柄;

binder->localBinder()呐矾,可以看Binder.cpp苔埋,binder->binder->remoteBinder(),可以看BpBinder.cpp

// Binder.cpp
BBinder* BBinder::localBinder()
{
    return this;
}

BpBinder* IBinder::remoteBinder()
{
    return NULL;
}



// BpBinder.cpp
BBinder* IBinder::localBinder()
{
    return NULL;
}
BpBinder* BpBinder::remoteBinder()
{
    return this;
}

從這里可以看出蜒犯,如果是BBinder對(duì)象组橄,locaBinder方法會(huì)返回其自身荞膘,如果是BpBinder對(duì)象,remoteBinder方法會(huì)返回其自身玉工,因?yàn)槲覀冊(cè)贐inder傳遞過(guò)程中羽资,傳遞的是Binder的實(shí)體,即Java的Binder對(duì)象遵班、C++的BBinder對(duì)象屠升,因此Parcel.cpp的flatten_binder方法會(huì)走入下面的分支:

obj.type = BINDER_TYPE_BINDER; //本地Binder,進(jìn)入該分支
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);

到這里狭郑,Binder對(duì)象的扁平化處理就完成了

接下來(lái)接著看IActivityManager#publishService方法腹暖,在執(zhí)行完成_data.writeStrongBinder(service)后繼續(xù)執(zhí)行了 mRemote.transact(Stub.TRANSACTION_publishService, _data, _reply, 0),這個(gè)mRemote是BinderProxy的一個(gè)實(shí)例翰萨,具體為啥可參考第一篇博文脏答,即AIDL的實(shí)現(xiàn)。
因此亩鬼,這里的執(zhí)行就轉(zhuǎn)到了Binder.java$BinderProxy#transact中:

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");

        if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)) {
            // For now, avoid spamming the log by disabling after we've logged
            // about this interface at least once
            mWarnOnBlocking = false;
            Log.w(Binder.TAG, "Outgoing transactions from this process must be FLAG_ONEWAY",
                    new Throwable());
        }

        final boolean tracingEnabled = Binder.isTracingEnabled();
        if (tracingEnabled) {
            final Throwable tr = new Throwable();
            Binder.getTransactionTracker().addTrace(tr);
            StackTraceElement stackTraceElement = tr.getStackTrace()[1];
            Trace.traceBegin(Trace.TRACE_TAG_ALWAYS,
                    stackTraceElement.getClassName() + "." + stackTraceElement.getMethodName());
        }
        try {
            return transactNative(code, data, reply, flags);
        } finally {
            if (tracingEnabled) {
                Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
            }
        }
    }

在方法的結(jié)尾殖告,有一次轉(zhuǎn)入了jni層執(zhí)行transactNative方法,這個(gè)方法的實(shí)現(xiàn)是android_util_Binder.cpp的android_os_BinderProxy_transact方法:

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{

    ...
    // java Parcel對(duì)象轉(zhuǎn)為native Parcel對(duì)象
    Parcel* data = parcelForJavaObject(env, dataObj);
    ...
    // java Parcel對(duì)象轉(zhuǎn)為native Parcel對(duì)象
    Parcel* reply = parcelForJavaObject(env, replyObj);
  
    //java BinderProxy對(duì)象(即mRemote)轉(zhuǎn)為native BinderProxy對(duì)象
    IBinder* target = (IBinder*)env->GetLongField(obj, gBinderProxyOffsets.mObject);
     ...
    status_t err = target->transact(code, *data, reply, flags);
    ...
}

這段代碼主要是對(duì)java層的對(duì)象進(jìn)行了一次轉(zhuǎn)換后轉(zhuǎn)入native層進(jìn)行調(diào)用雳锋,因?yàn)閙Remote是BinderProxy對(duì)象黄绩,因此target是一個(gè)BpBinder對(duì)象,最終 target->transact轉(zhuǎn)入BpBinder.cpp->transact方法中:

// BpBinder.cpp
status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

其中data就是我們要傳遞的Parcel對(duì)象玷过。

在這里又繼續(xù)調(diào)用了IPCThreadState::self()->transact方法宝与,從本篇開(kāi)始到現(xiàn)在,這些邏輯一直在RemoteService所在進(jìn)程中執(zhí)行冶匹,一直沒(méi)有看到有跨進(jìn)程調(diào)用的跡象,當(dāng)我們看到IPC開(kāi)頭的這個(gè)類(lèi)名時(shí)咆瘟,我們知道嚼隘,離真正開(kāi)始跨進(jìn)程調(diào)用不遠(yuǎn)了!OK袒餐,接下來(lái)繼續(xù)分析IPCThreadState.cpp的transact方法:

// IPCThreadState.cpp
tatus_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err = data.errorCheck();

    flags |= TF_ACCEPT_FDS;

    IF_LOG_TRANSACTIONS() {
        TextOutput::Bundle _b(alog);
        alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
            << handle << " / code " << TypeCode(code) << ": "
            << indent << data << dedent << endl;
    }

    if (err == NO_ERROR) {
        LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
            (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }

    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }

    if ((flags & TF_ONE_WAY) == 0) {
        #if 0
        if (code == 4) { // relayout
            ALOGI(">>>>>> CALLING transaction 4");
        } else {
            ALOGI(">>>>>> CALLING transaction %d", code);
        }
        #endif
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        #if 0
        if (code == 4) { // relayout
            ALOGI("<<<<<< RETURNING transaction 4");
        } else {
            ALOGI("<<<<<< RETURNING transaction %d", code);
        }
        #endif

        IF_LOG_TRANSACTIONS() {
            TextOutput::Bundle _b(alog);
            alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
                << handle << ": ";
            if (reply) alog << indent << *reply << dedent << endl;
            else alog << "(none requested)" << endl;
        }
    } else {
        err = waitForResponse(NULL, NULL);
    }

    return err;
}

其中調(diào)用writeTransactionData即是寫(xiě)入要傳輸數(shù)據(jù)的過(guò)程飞蛹,它的實(shí)現(xiàn)如下:

// IPCThreadState.cpp
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr;
    tr.target.ptr = 0;
    tr.target.handle = handle; // handle = 0
    tr.code = code;            // code = ADD_SERVICE_TRANSACTION
    tr.flags = binderFlags;    // binderFlags = 0
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;

    // data為記錄Media服務(wù)信息的Parcel對(duì)象
    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();  // mDataSize
        tr.data.ptr.buffer = data.ipcData(); //mData
        tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t); //mObjectsSize
        tr.data.ptr.offsets = data.ipcObjects(); //mObjects
    } else if (statusBuffer) {
        ...
    } else {
        return (mLastError = err);
    }

    mOut.writeInt32(cmd);         //cmd = BC_TRANSACTION
    mOut.write(&tr, sizeof(tr));  //寫(xiě)入binder_transaction_data數(shù)據(jù)
    return NO_ERROR;
}

這里的handle是BpBinder對(duì)象所持有mHandle,即BpBinder所對(duì)應(yīng)的BBinder,也就是Binder的本地實(shí)體灸眼,
binder_transaction_data結(jié)構(gòu)體是binder驅(qū)動(dòng)通信的數(shù)據(jù)結(jié)構(gòu)卧檐,該過(guò)程最終是把Binder請(qǐng)求碼BC_TRANSACTION和binder_transaction_data結(jié)構(gòu)體寫(xiě)入到mOut

接下來(lái)會(huì)執(zhí)行到IPCThreadState.cpp->waitForResponse方法:

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;
    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
        ...
        if (mIn.dataAvail() == 0) continue;

        cmd = mIn.readInt32();
        switch (cmd) {
            case BR_TRANSACTION_COMPLETE: ...
            case BR_DEAD_REPLY: ...
            case BR_FAILED_REPLY: ...
            case BR_ACQUIRE_RESULT: ...
            case BR_REPLY: ...
                goto finish;

            default:
                err = executeCommand(cmd); 
                if (err != NO_ERROR) goto finish;
                break;
        }
    }
    ...
    return err;
}

注意看talkWithDriver方法,在這里會(huì)正式進(jìn)入Android內(nèi)核焰宣,即Binder驅(qū)動(dòng)中進(jìn)行跨進(jìn)程傳遞和轉(zhuǎn)換霉囚。
具體的細(xì)節(jié)篇幅有限,我們下一篇再講匕积。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盈罐,一起剝皮案震驚了整個(gè)濱河市榜跌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盅粪,老刑警劉巖钓葫,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異票顾,居然都是意外死亡础浮,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)奠骄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)豆同,“玉大人,你說(shuō)我怎么就攤上這事戚揭∮崭妫” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵民晒,是天一觀(guān)的道長(zhǎng)精居。 經(jīng)常有香客問(wèn)我,道長(zhǎng)潜必,這世上最難降的妖魔是什么靴姿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮磁滚,結(jié)果婚禮上佛吓,老公的妹妹穿的比我還像新娘。我一直安慰自己垂攘,他們只是感情好维雇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著晒他,像睡著了一般吱型。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上陨仅,一...
    開(kāi)封第一講書(shū)人閱讀 51,488評(píng)論 1 302
  • 那天津滞,我揣著相機(jī)與錄音,去河邊找鬼灼伤。 笑死触徐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的狐赡。 我是一名探鬼主播撞鹉,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了孔祸?” 一聲冷哼從身側(cè)響起隆敢,我...
    開(kāi)封第一講書(shū)人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎崔慧,沒(méi)想到半個(gè)月后拂蝎,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡惶室,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年温自,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片皇钞。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡悼泌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出夹界,到底是詐尸還是另有隱情馆里,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布可柿,位于F島的核電站鸠踪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏复斥。R本人自食惡果不足惜营密,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望目锭。 院中可真熱鬧评汰,春花似錦、人聲如沸痢虹。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)奖唯。三九已至编振,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間臭埋,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工臀玄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瓢阴,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓健无,卻偏偏與公主長(zhǎng)得像荣恐,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

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