Android Binder 應(yīng)用層調(diào)用過(guò)程分析

Android 上的多進(jìn)程情景

Android 中每個(gè)應(yīng)用的進(jìn)程都 fork 自 Zygote 進(jìn)程蚂维, Zygote 進(jìn)程在啟動(dòng)時(shí)自己會(huì)創(chuàng)建一個(gè)虛擬機(jī),從而也就讓每個(gè)應(yīng)用擁有了自己的虛擬機(jī)地消。
當(dāng)應(yīng)用涉及多進(jìn)程時(shí)速梗,想當(dāng)于啟動(dòng)了多個(gè)虛擬機(jī)良蒸,在單進(jìn)程情況下的一些情景將會(huì)失效:

  • 靜態(tài)變量: 由于靜態(tài)變量是位于虛擬機(jī)內(nèi)存的方法區(qū)舟扎,每個(gè)虛擬機(jī)彼此獨(dú)立遥巴,多個(gè)進(jìn)程訪問(wèn)的也就不會(huì)是同一個(gè)變量
  • 單利模式:?jiǎn)卫彩腔谝粋€(gè)虛擬機(jī)的,多個(gè)虛擬機(jī)將失效
  • 線程同步機(jī)制:同步鎖基于同一進(jìn)程
  • SharedPerfrence 不再可靠: SP 內(nèi)部是以讀寫 xml 文件的方式享幽,且只有線程鎖铲掐。多進(jìn)程環(huán)境下,文件同時(shí)讀寫將不可靠值桩。
  • Application 類的 onCreate 會(huì)在每個(gè)進(jìn)程啟動(dòng)時(shí)被調(diào)用: 在含有多進(jìn)程的應(yīng)用里摆霉,需要在 Application 類的 onCreate 里區(qū)分當(dāng)前的進(jìn)程,避免多個(gè)進(jìn)程都執(zhí)行了重復(fù)的代碼奔坟。

如何開啟多進(jìn)程

AndroidManifest 中携栋,給四大組件設(shè)置單獨(dú)的 android:process 屬性。
這種方式咳秉,有兩種情況:

  1. 當(dāng)前應(yīng)用私有的進(jìn)程婉支,聲明 process 屬性帶 : 號(hào),其他應(yīng)用的組件不能運(yùn)行在該進(jìn)程中澜建。
<activity android:name=".AbcActivity" android:process=":remote"/>
  1. 不帶 : 號(hào)的全局進(jìn)程磅摹。其他應(yīng)用可以通過(guò) SharedUID 方式跑在該進(jìn)程中。
<activity android:name=".AbcActivity" android:process="remote"/>

序列化和反序列化

Java Serializable 接口

讓對(duì)象支持序列化霎奢,只需實(shí)現(xiàn) Serializable接口,并聲明一個(gè)serialVersionUID饼灿。serialVersionUID不是必需的幕侠,但是如果不聲明會(huì)對(duì)反序列化過(guò)程產(chǎn)生影響。序列化后的數(shù)據(jù)中的serialVersionUID只有和當(dāng)前類的serialVersionUID相同時(shí)碍彭,才能被正常地反序列化晤硕。

  • 靜態(tài)屬性屬于類,不會(huì)被序列化
  • transitent 聲明的屬性不會(huì)被序列化

Android Parcelable 接口

Android 提供的序列化接口庇忌,相比 Serializable 性能更好舞箍,因?yàn)樗饕糜谠趦?nèi)存上序列化和反序列化。實(shí)現(xiàn)方式就是類實(shí)現(xiàn) Parcelable 接口皆疹,并實(shí)現(xiàn) createFromParcelwriteToParcel 等方法疏橄。

Binder

  • 從 IPC 角度,Binder 是 Android 的一種跨進(jìn)程通信方式
  • 從 Android Framework 角度略就,Binder 是 ServiceManager 連接各種 Manager 和相應(yīng) ManagerService 的橋梁
  • 從 Android 應(yīng)用層捎迫,Binder 是客戶端和服務(wù)端進(jìn)行通信的媒介,當(dāng) bindService 時(shí)表牢,服務(wù)端會(huì)返回一個(gè)包含了服務(wù)端業(yè)務(wù)調(diào)用的 Binder 對(duì)象窄绒,通過(guò)這個(gè) Binder 對(duì)象,客戶端就可以像調(diào)用客戶端本地方法一樣調(diào)用服務(wù)端的方法崔兴。
  • 普通 Service 中的 Binder 不涉及進(jìn)程間通信
  • 多進(jìn)程的應(yīng)用會(huì)較多涉及 Binder彰导,Binder 也主要用在 Service 上

Android 提供了 AIDL 描述語(yǔ)言來(lái)方便開發(fā)者創(chuàng)建 Binder 類蛔翅,也可以自己手寫實(shí)現(xiàn) Binder 類。

模擬一個(gè)數(shù)據(jù)類 User位谋,并實(shí)現(xiàn) Parcelable 接口山析,使用跨進(jìn)程的方式,從遠(yuǎn)程 Service 中獲取 User倔幼。

public class User implements Parcelable {
    String name;
    int age;

    protected User(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            // 從 Parcel 中構(gòu)造 User 對(duì)象
            return new User(in);
        }

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // 將屬性寫入 Parcel 對(duì)象中
        dest.writeString(name);
        dest.writeInt(age);
    }
}

使用 AIDL盖腿,自動(dòng)生成 Binder 類

  1. 創(chuàng)建 User.aidl
// User.aidl
package com.jy.app2;

parcelable User;
  1. 創(chuàng)建 IUserManagerInterface.aidl
// IUserManagerInterface.aidl
package com.jy.app2;

import com.jy.app2.User;

interface IUserManagerInterface {
User getUser();
void setUser(in User user);
}

需要注意,User.aidl 的文件名和內(nèi)部聲明的 pracelable 都要和 Java 類 User一致损同,且 User.aidl 的包路徑也要和 Java User類一致翩腐。

  1. 編譯生成的 IUserManagerInterface Java 接口
package com.jy.app2;

// 所有可以在 Binder 中傳輸?shù)慕涌冢夹枰^承 IInterface
public interface IUserManagerInterface extends android.os.IInterface {

    // 一個(gè) Binder 類膏燃,當(dāng)客戶端和服務(wù)端在同一個(gè)進(jìn)程時(shí)不會(huì)走 onTransact 過(guò)程
    // 當(dāng)客戶端和服務(wù)端不在同一個(gè)進(jìn)程時(shí)茂卦,會(huì)走 onTransact 過(guò)程,并且邏輯有內(nèi)部類 Proxy 完成
    public static abstract class Stub extends android.os.Binder implements com.jy.app2.IUserManagerInterface {
        // Binder 的唯一標(biāo)識(shí)
        private static final java.lang.String DESCRIPTOR = "com.jy.app2.IUserManagerInterface";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /*
         * 用于將服務(wù)端的 Binder 對(duì)象组哩,轉(zhuǎn)換成客戶端所需的 IInterface 接口對(duì)象(使用 AIDL 生成的)等龙。
         * 這個(gè)過(guò)程是區(qū)分進(jìn)程的:如果客戶端和服務(wù)端在同一個(gè)進(jìn)程,此方法返回服務(wù)端的 Stub 對(duì)象本身伶贰;否則
         * 就返回 Stub 的內(nèi)部類 Proxy 對(duì)象
        */
        public static com.jy.app2.IUserManagerInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.jy.app2.IUserManagerInterface))) {
                return ((com.jy.app2.IUserManagerInterface) iin);
            }
            return new com.jy.app2.IUserManagerInterface.Stub.Proxy(obj);
        }

        // 此方法返回當(dāng)前的 Binder 對(duì)象
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        // 此方法運(yùn)行在服務(wù)端的 Binder 線程池中蛛砰,當(dāng)客戶端發(fā)起跨進(jìn)程請(qǐng)求時(shí),遠(yuǎn)程請(qǐng)求會(huì)通過(guò)系統(tǒng)底層封裝之后黍衙,交給該方法執(zhí)行
        // 該方法中會(huì)服務(wù)端根據(jù) code 參數(shù)確定應(yīng)該執(zhí)行的目標(biāo)方法泥畅,接著從 data 中取出目標(biāo)方法需要的參數(shù)(如果目標(biāo)參數(shù)需要傳入?yún)?shù)),目標(biāo)方法執(zhí)行完成后琅翻,將結(jié)果寫入 reply 中(如果目標(biāo)方法有返回值)位仁。
        // 如果該方法返回 false,代表客戶端請(qǐng)求失敗方椎。所以可以在這里面加自己的業(yè)務(wù)聂抢,比如權(quán)限驗(yàn)證,當(dāng)不通過(guò)時(shí)直接返回 false
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_getUser: {
                    data.enforceInterface(descriptor);
                    com.jy.app2.User _result = this.getUser();
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
                case TRANSACTION_setUser: {
                    data.enforceInterface(descriptor);
                    com.jy.app2.User _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.jy.app2.User.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.setUser(_arg0);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.jy.app2.IUserManagerInterface {
            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;
            }

            // 此方法運(yùn)行在客戶端棠众,當(dāng)客戶端遠(yuǎn)程調(diào)用此方法時(shí)琳疏,先創(chuàng)建輸入和輸出 Parcel _data 和 _reply
            // 然后調(diào)用 transact 發(fā)起 RPC 遠(yuǎn)程調(diào)用,同時(shí)線程掛起闸拿;然后服務(wù)端的 onTransact 被調(diào)用轿亮,直到
            // RPC 結(jié)果返回,客戶端線程繼續(xù)運(yùn)行胸墙,并從 _reply 中取出 RPC 的返回結(jié)果我注,最后返回結(jié)果
            @Override
            public com.jy.app2.User getUser() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.jy.app2.User _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getUser, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = com.jy.app2.User.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            // 此方法同上面,只是多了將參數(shù)寫入到 _data 迟隅,由于該方法沒(méi)有返回值但骨,所以不會(huì)從 _reply 中取結(jié)果
            @Override
            public void setUser(com.jy.app2.User user) 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 ((user != null)) {
                        _data.writeInt(1);
                        user.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_setUser, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        //兩個(gè)整形励七,用于標(biāo)識(shí)客戶端請(qǐng)求的方法
        static final int TRANSACTION_getUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_setUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    // 服務(wù)端 Binder 需要實(shí)現(xiàn)的方法
    public com.jy.app2.User getUser() throws android.os.RemoteException;
    public void setUser(com.jy.app2.User user) throws android.os.RemoteException;
}

手寫實(shí)現(xiàn) Binder 類

先定義一個(gè)繼承了IInterface的接口

public interface IUserManagerInterface extends IInterface {

    public String DESCRIPTION = "com.jy.app2.IUserManagerInterface";

    public void setUser(String token, User user) throws RemoteException;

    public User getUser(String token) throws RemoteException;

    public int Method_setUser = IBinder.FIRST_CALL_TRANSACTION;
    public int Method_getUser = IBinder.FIRST_CALL_TRANSACTION + 1;
}

實(shí)現(xiàn)接口,并繼承Binder

package com.jy.app2;

import android.os.*;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;

abstract class IUserManagerInterfaceImpl extends Binder implements IUserManagerInterface {

    IUserManagerInterfaceImpl() {
        attachInterface(this, DESCRIPTION);
    }
    
    @Override
    public IBinder asBinder() {
        return this;
    }

    // 當(dāng)不是跨進(jìn)程時(shí)奔缠,直接返回服務(wù)端本身的 Binder
    // 當(dāng)是跨進(jìn)程時(shí)掠抬,返回代理對(duì)象
    public static IUserManagerInterface asInterface(IBinder object) {
        if (object == null) {
            return null;
        }
        IInterface iin = object.queryLocalInterface(DESCRIPTION);
        if ((iin != null) && (iin instanceof IUserManagerInterface)) {
            return (IUserManagerInterface) iin;
        }
        return new Proxy(object);
    }

    @Override
    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {

        switch (code) {
            case Method_getUser:
                if (!auth(data)) {
                    return false;
                }
                User user = this.getUser(data.readString());
                reply.writeNoException();
                if (user != null) {
                    reply.writeInt(1);
                    user.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                } else {
                    reply.writeInt(0);
                }
                return true;
            case Method_setUser:
                if (!auth(data)) {
                    return false;
                }
                String token = data.readString();
                User arg1 = null;
                if ((0 != data.readInt())) {
                    arg1 = User.CREATOR.createFromParcel(data);
                }
                this.setUser(token, arg1);
                reply.writeNoException();
                return true;
        }
        return super.onTransact(code, data, reply, flags);
    }

    private boolean auth(Parcel data) {
        data.enforceInterface(DESCRIPTION);
        // 模擬權(quán)限驗(yàn)證
        String token = data.readString();
        return !TextUtils.equals(token, "123");
    }


    static class Proxy implements IUserManagerInterface {

        IBinder remote;

        Proxy(IBinder remote) {
            this.remote = remote;
        }

        @Override
        public User getUser(String token) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            User result = null;
            try {
                data.writeInterfaceToken(DESCRIPTION);
                data.writeString(token);
                boolean success = remote.transact(IUserManagerInterface.Method_getUser, data, reply, 0);
                reply.readException();
                if ((0 != reply.readInt())) {
                    result = User.CREATOR.createFromParcel(reply);
                }
            } finally {
                data.recycle();
                reply.recycle();
            }
            return result;
        }

        @Override
        public void setUser(String token, User user) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                data.writeInterfaceToken(DESCRIPTION);
                data.writeString(token);
                if (user != null) {
                    data.writeInt(1);
                    user.writeToParcel(data, 0);
                } else {
                    data.writeInt(0);
                }
                boolean success = remote.transact(IUserManagerInterface.Method_getUser, data, reply, 0);
                reply.readException();
            } finally {
                data.recycle();
                reply.recycle();
            }
        }

        @Override
        public IBinder asBinder() {
            return remote;
        }
    }
}

分析 Binder 的調(diào)用過(guò)程

創(chuàng)建一個(gè) Service

public class UserService extends Service {
    User mUser;

    public UserService() {
        mUser = new User();
        mUser.name = "Stefan";
        mUser.age = 13;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new IUserManagerInterfaceImpl() {
            @Override
            public void setUser(String token, User user) throws RemoteException {
                mUser = user;
            }

            @Override
            public User getUser(String token) throws RemoteException {
                return mUser;
            }
        };
    }
}

然后bindService

IUserManagerInterface userManagerInterface;
ServiceConnection connection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        userManagerInterface = IUserManagerInterfaceImpl.asInterface(service);
        onServiceConnect();
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {}
    };
    bindService(new Intent(this, UserService.class), connection, BIND_AUTO_CREATE);
}

private void onServiceConnect() {
    try {
        User user = userManagerInterface.getUser("token_1234");
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

當(dāng) bindService成功之后,會(huì)首先調(diào)用asInterface方法獲得Binder對(duì)象校哎,所以在 asInterface方法處斷點(diǎn)看下

不跨進(jìn)程分析

Service 不單獨(dú)聲明 process屬性

binder-local

可以看到两波,調(diào)用直接返回了 queryLocalInterface返回的IInterface對(duì)象,該對(duì)象其實(shí)就是在上面 Service 的onBind方法中創(chuàng)建的 IUserManagerInterfaceImpl匿名內(nèi)部類闷哆⊙埽客戶端調(diào)用的直接是那個(gè)onBind返回的對(duì)象的方法。

跨進(jìn)程分析

Service 單獨(dú)聲明 process屬性

binder-remote

這時(shí)候就返回了代理對(duì)象抱怔,然后接著就是調(diào)用getUser方法劣坊。

binder-proxy

走到了 Proxy 的 getUser,這是還沒(méi)有發(fā)生跨進(jìn)程的調(diào)用屈留,下一行remote.transact就會(huì)發(fā)起跨進(jìn)程請(qǐng)求局冰,將我們請(qǐng)求的方法編號(hào)、輸入數(shù)據(jù)灌危、輸出數(shù)據(jù)作為參數(shù)傳入康二,接下來(lái)的調(diào)用就會(huì)走到另一個(gè)進(jìn)程里,同時(shí)客戶端這邊線程會(huì)被掛起等待勇蝙。Debug 也需要 attach 到另一個(gè)進(jìn)程上沫勿。onTransact將執(zhí)行在服務(wù)端進(jìn)程上:

binder-onTransact

onTransact里根據(jù)方法編號(hào)調(diào)用對(duì)應(yīng)的方法,這里的this是在 Service 的 onBind中返回的對(duì)象浅蚪。在這里會(huì)將結(jié)果寫到 replay中,然后結(jié)束烫罩,程序執(zhí)行切換會(huì)客戶端進(jìn)程惜傲。

proxy-getUser

Proxy 繼續(xù)執(zhí)行,從 reply中取出結(jié)果贝攒,最后返回盗誊。

總結(jié)

  • Proxy 中的邏輯是運(yùn)行在客戶端進(jìn)程的,且默認(rèn)在主線程隘弊,需要注意阻塞問(wèn)題哈踱,所以bindService成功之后,可以通過(guò)單開線程來(lái)做 IPC 調(diào)用
  • onTransact 運(yùn)行在服務(wù)端進(jìn)程中梨熙,且運(yùn)行在 Binder 線程池中开镣,Binder 中的邏輯無(wú)論耗時(shí)都應(yīng)該使用同步實(shí)現(xiàn)

參考 《Android 藝術(shù)探索讀書筆記》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市咽扇,隨后出現(xiàn)的幾起案子邪财,更是在濱河造成了極大的恐慌陕壹,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件树埠,死亡現(xiàn)場(chǎng)離奇詭異糠馆,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)怎憋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門又碌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人绊袋,你說(shuō)我怎么就攤上這事毕匀。” “怎么了愤炸?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵荡澎,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我剧罩,道長(zhǎng)枚冗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任诞仓,我火速辦了婚禮缤苫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘墅拭。我一直安慰自己活玲,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布谍婉。 她就那樣靜靜地躺著舒憾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪穗熬。 梳的紋絲不亂的頭發(fā)上镀迂,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音唤蔗,去河邊找鬼探遵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛妓柜,可吹牛的內(nèi)容都是我干的箱季。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼棍掐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼藏雏!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起作煌,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤诉稍,失蹤者是張志新(化名)和其女友劉穎蝠嘉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杯巨,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蚤告,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了服爷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杜恰。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖仍源,靈堂內(nèi)的尸體忽然破棺而出心褐,到底是詐尸還是另有隱情,我是刑警寧澤笼踩,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布逗爹,位于F島的核電站,受9級(jí)特大地震影響嚎于,放射性物質(zhì)發(fā)生泄漏掘而。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一于购、第九天 我趴在偏房一處隱蔽的房頂上張望袍睡。 院中可真熱鬧,春花似錦肋僧、人聲如沸斑胜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)止潘。三九已至,卻和暖如春辫诅,著一層夾襖步出監(jiān)牢的瞬間凭戴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工泥栖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留簇宽,地道東北人勋篓。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓吧享,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親譬嚣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钢颂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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