IPC機(jī)制之Binder機(jī)制

前言

Binder是Android的一個(gè)類嘁字,它實(shí)現(xiàn)了IBinder接口码倦。從IPC角度來(lái)說(shuō)咽块,Binder是Android中的一種跨進(jìn)程通信方式忽洛;從Android應(yīng)用層來(lái)說(shuō)腻惠,Binder是客戶端和服務(wù)端進(jìn)行通信的媒介,當(dāng)bindService的時(shí)候欲虚,服務(wù)端會(huì)返回一個(gè)包含了服務(wù)端業(yè)務(wù)調(diào)用的Binder對(duì)象集灌,通過(guò)這個(gè)Binder對(duì)象,客戶端就可以獲取服務(wù)端提供的服務(wù)或者數(shù)據(jù)复哆,這里的服務(wù)包括普通服務(wù)和基于AIDL的服務(wù)欣喧。

在Android開發(fā)中,Binder主要用在Service中梯找,包括AIDL(Android Interface Defination Language)和Messenger唆阿,其中普通Service中的Binder不涉及進(jìn)程間通信,所以較為簡(jiǎn)單锈锤,無(wú)法觸及Binder的核心驯鳖,而Messenger的底層其實(shí)是AIDL,所以這里選擇AIDL來(lái)分析Binder的工作機(jī)制久免。

使用示例

新建三個(gè)文件浅辙,Book.java、Book.aidl和IBookManager.aidl阎姥,代碼如下:
Book.java

package com.example.runningh.myapplication.aidl;

import android.os.Parcel;
import android.os.Parcelable;

public class Book implements Parcelable {
    public int bookId;
    public String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }

    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {

        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    private Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }
}

Book .java是一個(gè)表示圖書信息的類记舆,實(shí)現(xiàn)了Parcelable接口。

Book.aidl:

package com.example.runningh.myapplication.aidl;

parcelable Book;

Book.aidl是Book類在AIDL中的聲明呼巴。

IBookManager.aidl:

package com.example.runningh.myapplication.aidl;

import com.example.runningh.myapplication.aidl.Book;
import com.example.runningh.myapplication.aidl.IOnNewBookArrivedListener;

interface IBookManager {

    List<Book> getBookList();

    void addBook(in Book book);
}

IBookManager.adil是我們定義的一個(gè)接口泽腮,里面有兩個(gè)方法:getBookList和addBook,其中g(shù)etBookList用于從遠(yuǎn)程服務(wù)端獲取圖書列表衣赶,而addBook用于往圖書列表中添加一本書盛正。

注意:在IBookManager.aidl中要手動(dòng)導(dǎo)入Book類,不支持自動(dòng)導(dǎo)入屑埋,這是AIDL的特殊之處豪筝。

通過(guò)執(zhí)行build操作后,系統(tǒng)為IBookManager.aidl生成的Binder類摘能,在gen目錄下的source文件夾下可找到IBookManager.java類续崖。

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/RunningH/Documents/TencentGame/ThirdParty/ThemeSkinning/MyApplication2/app/src/main/aidl/com/example/runningh/myapplication/aidl/IBookManager.aidl
 */
package com.example.runningh.myapplication.aidl;
public interface IBookManager extends android.os.IInterface {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.example.runningh.myapplication.aidl.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.example.runningh.myapplication.aidl.IBookManager";
        
        /** Construct the stub at attach it to the interface. */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
        
        /**
         * Cast an IBinder object into an com.example.runningh.myapplication.aidl.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.example.runningh.myapplication.aidl.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.example.runningh.myapplication.aidl.IBookManager))) {
                return ((com.example.runningh.myapplication.aidl.IBookManager)iin);
            }
            return new com.example.runningh.myapplication.aidl.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.example.runningh.myapplication.aidl.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.example.runningh.myapplication.aidl.Book _arg0;
                    if ((0!=data.readInt())) {
                        _arg0 = com.example.runningh.myapplication.aidl.Book.CREATOR.createFromParcel(data);
                    }
                    else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
    
        private static class Proxy implements com.example.runningh.myapplication.aidl.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.example.runningh.myapplication.aidl.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.example.runningh.myapplication.aidl.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.example.runningh.myapplication.aidl.Book.CREATOR);
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
            
            @Override public void addBook(com.example.runningh.myapplication.aidl.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();
                }
            }
        }
    }


    static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    
    public java.util.List<com.example.runningh.myapplication.aidl.Book> getBookList() throws android.os.RemoteException;
    public void addBook(com.example.runningh.myapplication.aidl.Book book) throws android.os.RemoteException;
}

首先這個(gè)類聲明了兩個(gè)方法getBookList和addBook,顯然這就是我們?cè)贗BookMananger.aidl中所聲明的方法团搞,同時(shí)它還聲明了兩個(gè)整型的id分別用于標(biāo)識(shí)這兩個(gè)方法严望,這兩個(gè)id用于標(biāo)識(shí)在transact過(guò)程中客戶端所請(qǐng)求的到底是哪個(gè)方法。接著逻恐,它聲明了一個(gè)內(nèi)部類Stub像吻,這個(gè)Stub就是一個(gè)Binder類峻黍,當(dāng)客戶端和服務(wù)端都位于同一個(gè)進(jìn)程時(shí),方法調(diào)用不會(huì)跨進(jìn)程的transact過(guò)程拨匆,而當(dāng)兩者位于不同進(jìn)程時(shí)姆涩,方法調(diào)用需要走transact過(guò)程,這個(gè)邏輯由Stub的內(nèi)部代理Proxy來(lái)完成惭每。

我們看一下怎么使用這個(gè)類骨饿,通過(guò)調(diào)用過(guò)程來(lái)熟悉其中的方法。

我們新建一個(gè)工程并在新建BookManagerActivity台腥、BookManagerService并將BookManagerService放在另外一個(gè)進(jìn)程中(在manifest中聲明)宏赘。在BookManagerActivity通過(guò)BindService的方式和BookManagerService綁定。

package com.example.runningh.myapplication.aidl;

import android.app.Activity;
import android.app.ListActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

import com.example.runningh.myapplication.R;

import java.util.List;

/**
 * Created by RunningH on 2017/11/26.
 */

public class BookManagerActivity extends Activity {
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager iBookManager = IBookManager.Stub.asInterface(service); //注意這里調(diào)用了Stub的asInterface方法黎侈,通過(guò)服務(wù)端的IBinder對(duì)象找到
            try {
                List<Book> list = iBookManager.getBookList();
                Log.i("ABC", "query book list, list type:" + list.getClass().getCanonicalName());
                Log.i("ABC", "query book list:" + list.toString());
                Book newBook = new Book(3, "HTML");
                iBookManager.addBook(newBook);
                Log.i("ABC", "add book:" + newBook);
                List<Book> newList = iBookManager.getBookList();
                Log.i("ABC", "query book list:" + newList.toString());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConnection);
    }
}

BookManagerService.java:

package com.example.runningh.myapplication.aidl;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by RunningH on 2017/11/26.
 */

public class BookManagerService extends Service {
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

    private Binder mBinder = new IBookManager.Stub() {

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "IOS"));
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}
  • BookManagerService中new了一個(gè)Stub對(duì)象察署,并重寫addBook和getBookList方法。這樣Stub的構(gòu)造方法被調(diào)用峻汉,構(gòu)造方法中調(diào)用了Binder的attachInterface將該Binder對(duì)象(Stub就是一個(gè)Binder對(duì)象)和DESCRIPTOR(Binder的唯一標(biāo)識(shí)贴汪,一般用Binder的類名表示)聯(lián)系起來(lái)。
  • 上面生成的Stub對(duì)象(也就是Binder對(duì)象)通過(guò)BinderService的方式傳遞給客戶端俱济,也就是BookManagerActivity的mConnection的onServiceConnected方法中service對(duì)象嘶是。通過(guò)調(diào)用IBookManager.Stub.asInterface方法得到客戶端所需要的AIDL接口類型對(duì)象钙勃。這是區(qū)分進(jìn)程的蛛碌,如果客戶端和服務(wù)端處于同一個(gè)進(jìn)程,那么執(zhí)行queryLocalInterface(DESCRIPTOR)返回就不為空(還記得上面生成Stub對(duì)象的時(shí)候?qū)⒃搶?duì)象和DESCRIPTOR綁定嗎辖源?這里就是取出來(lái))蔚携,所以此方法返回的就是服務(wù)端的Stub對(duì)象本身。如果是不同的進(jìn)程則返回的是系統(tǒng)封裝或的Stub.proxy對(duì)象克饶。對(duì)應(yīng)于下面的則是new了一個(gè)Stub.Proxy對(duì)象酝蜒。
public static com.example.runningh.myapplication.aidl.IBookManager asInterface(android.os.IBinder obj) {
     if ((obj==null)) {
         return null;
     }
     android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
     if (((iin!=null)&&(iin instanceof com.example.runningh.myapplication.aidl.IBookManager))) {
          return ((com.example.runningh.myapplication.aidl.IBookManager)iin);
    }
    return new com.example.runningh.myapplication.aidl.IBookManager.Stub.Proxy(obj);
}
  • 先看如果處于同一個(gè)進(jìn)程的情況,得到服務(wù)端的Stub對(duì)象后矾湃,直接調(diào)用getBookList和addBook方法亡脑。因?yàn)镾tub對(duì)象實(shí)現(xiàn)了IBookManager接口,并實(shí)現(xiàn)了其中的getBookList和addBook方法邀跃,所以這里實(shí)際調(diào)用的是getBookList和addBook方法霉咨。這樣就實(shí)現(xiàn)了客戶端和服務(wù)端的交互。
  • 再來(lái)看不同進(jìn)程的情況拍屑,我們得到的是一個(gè)Stub.Proxy對(duì)象途戒。在客戶端調(diào)用getBookList和addBook方法,實(shí)際上調(diào)用的是Stub.Proxy對(duì)象的getBookList和addBook方法僵驰。
private static class Proxy implements com.example.runningh.myapplication.aidl.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.example.runningh.myapplication.aidl.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.example.runningh.myapplication.aidl.Book> _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
            _reply.readException();
            _result = _reply.createTypedArrayList(com.example.runningh.myapplication.aidl.Book.CREATOR);
        }
        finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    @Override public void addBook(com.example.runningh.myapplication.aidl.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();
        }
    }
}
  • getBookList方法中調(diào)用了mRemote.transact方法喷斋,第一個(gè)參數(shù)標(biāo)識(shí)調(diào)用哪一個(gè)方法唁毒,第二個(gè)參數(shù)標(biāo)識(shí)需要傳遞進(jìn)去的參數(shù),第三個(gè)參數(shù)標(biāo)識(shí)需要返回的數(shù)據(jù)星爪,第四個(gè)參數(shù)一般為0浆西。調(diào)用transact方法發(fā)起RPC(遠(yuǎn)程過(guò)程調(diào)用)請(qǐng)求,同時(shí)當(dāng)前線程掛起(等待數(shù)據(jù)返回)移必,然后服務(wù)端的onTransact方法會(huì)被調(diào)用室谚,直到RPC過(guò)程返回后,當(dāng)前線程繼續(xù)執(zhí)行崔泵,并從_reply中取出RPC過(guò)程的返回結(jié)果秒赤。
  • addBook方法調(diào)用了mRemote.transact方法,參數(shù)的意思同上一樣憎瘸。由于addBook不需要返回值入篮,所以它不需要從_reply中取出返回值。
  • 上面兩個(gè)方法都調(diào)用了transact方法幌甘,那么該方法被調(diào)用后會(huì)觸發(fā)服務(wù)端的Stub對(duì)象的onTransact方法被調(diào)用潮售。
@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.example.runningh.myapplication.aidl.Book> _result = this.getBookList();
            reply.writeNoException();
            reply.writeTypedList(_result);
            return true;
        }
        case TRANSACTION_addBook: {
            data.enforceInterface(DESCRIPTOR);
            com.example.runningh.myapplication.aidl.Book _arg0;
            if ((0!=data.readInt())) {
                _arg0 = com.example.runningh.myapplication.aidl.Book.CREATOR.createFromParcel(data);
            }
            else {
                _arg0 = null;
            }
            this.addBook(_arg0);
            reply.writeNoException();
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}
  • 通過(guò)onTransact方法,我們通過(guò)code參數(shù)作為標(biāo)識(shí)具體調(diào)用的哪一個(gè)方法锅风,比如TRANSACTION_getBookList就是調(diào)用getBookList方法酥诽,這里做的操作是調(diào)用Stub對(duì)象的getBookList方法并且將結(jié)果寫入到reply(如果該操作需要結(jié)果返回的話)中然后返回。

總結(jié):上述就是Binder的工作機(jī)制皱埠,我們需要注意的是當(dāng)客戶端發(fā)起遠(yuǎn)程請(qǐng)求時(shí)肮帐,由于當(dāng)前線程會(huì)被掛起直至服務(wù)器端進(jìn)程返回?cái)?shù)據(jù),所以如果一個(gè)遠(yuǎn)程方法是很耗時(shí)的边器,那么不能在UI線程中發(fā)起遠(yuǎn)程請(qǐng)求训枢;其次由于服務(wù)端的Binder方法運(yùn)行在Binder的線程池中,所以Binder方法不管是否耗時(shí)都是應(yīng)該采用同步的方法去實(shí)現(xiàn)忘巧,因?yàn)樗呀?jīng)運(yùn)行在一個(gè)線程中了恒界。

參考:這篇文章是對(duì)《Android開發(fā)藝術(shù)探索》中IPC機(jī)制一章中的Binder內(nèi)容進(jìn)行的總結(jié)。這本書的作者任玉剛是一個(gè)大牛砚嘴,這本書也是很通俗易懂十酣,對(duì)Android開發(fā)的進(jìn)階有非常大的幫助。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末际长,一起剝皮案震驚了整個(gè)濱河市耸采,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌也颤,老刑警劉巖洋幻,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異翅娶,居然都是意外死亡文留,警方通過(guò)查閱死者的電腦和手機(jī)好唯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)燥翅,“玉大人骑篙,你說(shuō)我怎么就攤上這事∩椋” “怎么了靶端?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)凛膏。 經(jīng)常有香客問(wèn)我杨名,道長(zhǎng),這世上最難降的妖魔是什么猖毫? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任台谍,我火速辦了婚禮,結(jié)果婚禮上吁断,老公的妹妹穿的比我還像新娘趁蕊。我一直安慰自己,他們只是感情好仔役,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布掷伙。 她就那樣靜靜地躺著,像睡著了一般又兵。 火紅的嫁衣襯著肌膚如雪任柜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天寒波,我揣著相機(jī)與錄音乘盼,去河邊找鬼升熊。 笑死俄烁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的级野。 我是一名探鬼主播页屠,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蓖柔!你這毒婦竟也來(lái)了辰企?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤况鸣,失蹤者是張志新(化名)和其女友劉穎牢贸,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體镐捧,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡潜索,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年臭增,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竹习。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡誊抛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出整陌,到底是詐尸還是另有隱情拗窃,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布泌辫,位于F島的核電站随夸,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏震放。R本人自食惡果不足惜逃魄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望澜搅。 院中可真熱鬧伍俘,春花似錦、人聲如沸勉躺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)饵溅。三九已至妨退,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蜕企,已是汗流浹背咬荷。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留轻掩,地道東北人幸乒。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像唇牧,于是被迫代替她去往敵國(guó)和親罕扎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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