Android跨进程通信IPC之17——Binder之Framework层Java篇--注册服务

移步系列Android跨进程通信IPC系列

1 注册服务

注册服务在ServiceManager里面

//frameworks/base/core/java/android/os/ServiceManager.java     70行
public static void addService(String name, IBinder service, boolean allowIsolated) {
    try {
        //getIServiceManager()是获取ServiceManagerProxy对象
        // addService() 是执行注册服务操作
        getIServiceManager().addService(name, service, allowIsolated); 
    } catch (RemoteException e) {
        Log.e(TAG, "error in addService", e);
    }
}

2 getIServiceManager()方法

//frameworks/base/core/java/android/os/ServiceManager.java     70行
    private static IServiceManager getIServiceManager() {
        if (sServiceManager != null) {
            return sServiceManager;
        }
        // Find the service manager
        sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
        return sServiceManager;
    }

2.1 BinderInternal.getContextObject()方法

//frameworks/base/core/java/com/android/internal/os/BinderInternal.java  88行
    /**
     * Return the global "context object" of the system.  This is usually
     * an implementation of IServiceManager, which you can use to find
     * other services.
     */
    public static final native IBinder getContextObject();

可见BinderInternal.getContextObject()最终会调用JNI通过C层来实现,那我们就继续跟踪

2.1.1 android_os_BinderInternal_getContextObject)函数

// frameworks/base/core/jni/android_util_Binder.cpp     899行
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    return javaObjectForIBinder(env, b);  
}
  • 看到上面的代码 大家有没有熟悉的感觉,前面讲过了:对于ProcessState::self() -> getContextObject()
  • 对于ProcessState::self()->getContextObject()可以理解为new BpBinder(0)

2.1.2 javaObjectForIBinder()函数

// frameworks/base/core/jni/android_util_Binder.cpp         547行
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
    if (val == NULL) return NULL;
    //返回false
    if (val->checkSubclass(&gBinderOffsets)) { 
        jobject object = static_cast<JavaBBinder*>(val.get())->object();
        return object;
    }

    AutoMutex _l(mProxyLock);

    jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
    //第一次 object为null
    if (object != NULL) { 
        //查找是否已经存在需要使用的BinderProxy对应,如果有,则返回引用。
        jobject res = jniGetReferent(env, object);
        if (res != NULL) {
            return res;
        }
        android_atomic_dec(&gNumProxyRefs);
        val->detachObject(&gBinderProxyOffsets);
        env->DeleteGlobalRef(object);
    }

    //创建BinderProxy对象
    object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
    if (object != NULL) {
        // BinderProxy.mObject成员变量记录BpBinder对象
        env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());
        val->incStrong((void*)javaObjectForIBinder);

        jobject refObject = env->NewGlobalRef(
                env->GetObjectField(object, gBinderProxyOffsets.mSelf));
         //将BinderProxy对象信息附加到BpBinder的成员变量mObjects中
        val->attachObject(&gBinderProxyOffsets, refObject,
                jnienv_to_javavm(env), proxy_cleanup);

        sp<DeathRecipientList> drl = new DeathRecipientList;
        drl->incStrong((void*)javaObjectForIBinder);
         // BinderProxy.mOrgue成员变量记录死亡通知对象
        env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jlong>(drl.get()));

        android_atomic_inc(&gNumProxyRefs);
        incRefsCreated(env);
    }
    return object;
}

上面的大致流程如下:

  • 1、第二个入参val在有些时候指向BpBinder,有些时候指向JavaBBinder
  • 2、至于是BpBinder还是JavaBBinder是通过if (val->checkSubclass(&gBinderOffsets)) 这个函数来区分的,如果是JavaBBinder,则为true,则就会通过成员函数object(),返回一个Java对象,这个对象就是Java层的Binder对象。由于我们这里是BpBinder,所以是 返回false
  • 3、如果是BpBinder,会先判断是不是第一次
    • 如果是第一次,下面的object为null。jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
    • 如果不是第一次,就会先查找是否已经存在需要使用的BinderProxy对象,如果找到就会返回引用
  • 4、如果没有找到可用的引用,就new一个BinderProxy对象
  • 所以主要是根据BpBinder(C++) 生成BinderProxy(Java对象),主要工作是创建BinderProxy对象,并把BpBinder对象地址保存到BinderProxy.mObject成员变量。
  • 到此,可知ServiceManagerNative.asInterface(BinderInternal.getContextObject()) 等价于
    ServiceManagerNative.asInterface(new BinderProxy())

2.2 ServiceManagerNative.asInterface()方法

//frameworks/base/core/java/android/os/ServiceManagerNative.java      33行
    /**
     * Cast a Binder object into a service manager interface, generating
     * a proxy if needed.
     * 将Binder对象转换service manager interface,如果需要,生成一个代理。
     */
    static public IServiceManager asInterface(IBinder obj)
    {
        //obj为 BpBinder
        // 如果 obj为null 则直接返回
        if (obj == null) {
            return null;
        }
        // 由于是BpBinder,所以BpBinder的queryLocalInterface(descriptor) 默认返回null
        IServiceManager in =
            (IServiceManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }
        return new ServiceManagerProxy(obj);
    }

我们看下这个obj.queryLocalInterface(descriptor)方法,其实他是调用的IBinder的native方法如下

public interface IBinder {
    .....
    /**
     * Attempt to retrieve a local implementation of an interface
     * for this Binder object.  If null is returned, you will need
     * to instantiate a proxy class to marshall calls through
     * the transact() method.
     */
    public IInterface queryLocalInterface(String descriptor);
    .....
}
  • 通过注释我们知道,queryLocalInterface是查询本地的对象
  • 本地对象,这里的本地对象是指,如果进行IPC调用,如果是两个进程是同一个进程,即对象是本地对象;如果两个进程是两个不同的进程,则返回的远端的代理类。
  • 所以在BBinder的子类BnInterface中,重载了这个方法,返回this,而在BpInterface并没有重载这个方法。又因为queryLocalInterface 默认返回的是null,所以obj.queryLocalInterface=null。
  • 所以最后结论是 return new ServiceManagerProxy(obj);

2.2.1 ServiceManagerProxy

PS:ServiceManagerProxy是ServiceManagerNative类的内部类

//frameworks/base/core/java/android/os/ServiceManagerNative.java    109行
class ServiceManagerProxy implements IServiceManager {
    public ServiceManagerProxy(IBinder remote) {
        mRemote = remote;
    }
}
  • mRemote为BinderProxy对象,该BinderProxy对象对应于BpBinder(0),其作为binder代理端,指向native的层的Service Manager。

所以说:

  • ServiceManager.getIServiceManager最终等价于new ServiceManagerProxy(new BinderProxy())。所以
 getIServiceManager().addService()

等价于

ServiceManagerNative.addService();
  • framework层的ServiceManager的调用实际的工作确实交给了ServiceManagerProxy的成员变量BinderProxy;
  • 而BinderProxy通过JNI的方式,最终会调用BpBinder对象;可见上层binder结构的核心功能依赖native架构的服务来完成的。

3 addService()方法详解

上面已经知道了

getIServiceManager().addService(name, service, allowIsolated); 

等价于

ServiceManagerProxy..addService(name, service, allowIsolated);

所以让我们来看下ServiceManagerProxy的addService()方法

3.1 ServiceManagerProxy的addService()

//frameworks/base/core/java/android/os/ServiceManagerNative.java     142行
    public void addService(String name, IBinder service, boolean allowIsolated)
            throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        //是个常量是 “android.os.IServiceManager"
        data.writeInterfaceToken(IServiceManager.descriptor);
        data.writeString(name);
        data.writeStrongBinder(service);
        data.writeInt(allowIsolated ? 1 : 0);
        mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
        reply.recycle();
        data.recycle();
    }

里面代码都比较容易理解,这里重点说下data.writeStrongBinder(service); 和 mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);

3.2 Parcel.writeStrongBinder()

//frameworks/base/core/java/android/os/Parcel.java     583行
    /**
     * Write an object into the parcel at the current dataPosition(),
     * growing dataCapacity() if needed.
     */
    public final void writeStrongBinder(IBinder val) {
        nativeWriteStrongBinder(mNativePtr, val);
    }

注释:在当前的dataPosition()的位置上写入一个对象,如果空间不足,则增加空间

3.2.1 nativeWriteStrongBinder()方法

/frameworks/base/core/java/android/os/Parcel.java      265行
    private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);

3.2.2 android_os_Parcel_writeStrongBinder()函数

//frameworks/base/core/jni/android_os_Parcel.cpp    298行
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
    //将java层Parcel转换为native层Parcel
    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);
        }
    }
}

这里主要涉及的两个重要的函数

  • writeStrongBinder()函数
  • ibinderForJavaObject()函数

3.2.3 ibinderForJavaObject()函数

// frameworks/base/core/jni/android_util_Binder.cpp   603行
sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{
    if (obj == NULL) return NULL;

    //Java层的Binder对象
    //mClass指向Java层中的Binder class
    if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
        JavaBBinderHolder* jbh = (JavaBBinderHolder*)
            env->GetLongField(obj, gBinderOffsets.mObject);
        //get()返回一个JavaBBinder,继承自BBinder
        return jbh != NULL ? jbh->get(env, obj) : NULL;
    }
    //Java层的BinderProxy对象
    // mClass 指向Java层的BinderProxy class
    if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
        //返回一个 BpBinder,mObject 是它的地址值
        return (IBinder*)env->GetLongField(obj, gBinderProxyOffsets.mObject);
    }
    return NULL;
}

根据Binder(Java)生成JavaBBinderHolder(C++)对象,主要工作是创建JavaBBinderHolder对象,并把JavaBBinder对象保存在到Binder.mObject成员变量。

  • 这个函数,本质就是根据传进来的Java对象找到对应的C++对象,这里的obj可能会指向两种对象:Binder对象和BinderProxy对象。
  • 如果传进来的是Binder对象,则会把gBinderOffsets.mObject转化为JavaBBinderHolder,并从中获得一个JavaBBinder对象(JavaBBinder继承自BBinder)。
  • 如果是BinderProxy对象,会返回一个BpBinder,这个BpBinder的地址值保存在gBinderProxyOffsets.mObject中

在上面的代码里面调用了get()函数,如下图

JavaBBinderHolder* jbh = (JavaBBinderHolder*)
env->GetLongField(obj, gBinderOffsets.mObject);
//get()返回一个JavaBBinder,继承自BBinder
return jbh != NULL ? jbh->get(env, obj) : NULL;

那我们就来研究下JavaBBinderHolder.get()函数

3.2.4 JavaBBinderHolder.get()函数

// frameworks/base/core/jni/android_util_Binder.cpp      316行
sp<JavaBBinder> get(JNIEnv* env, jobject obj)
{
    AutoMutex _l(mLock);
    sp<JavaBBinder> b = mBinder.promote();
    if (b == NULL) {
        //首次进来,创建JavaBBinder对象
        b = new JavaBBinder(env, obj);
        mBinder = b;
    }
    return b;
}

JavaBBinderHolder有一个成员变量mBinder,保存当前创建的JavaBBinder对象,这是一个wp类型的,可能会被垃圾回收器给回收的,所以每次使用前都需要先判断是否存在。

那我们再来看看下JavaBBinder的初始化

3.2.5 JavaBBinder的初始化

JavaBBinder(JNIEnv* env, jobject object)
    : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object))
{
    ALOGV("Creating JavaBBinder %p\n", this);
    android_atomic_inc(&gNumLocalRefs);
    incRefsCreated(env);
}

创建JavaBBinder,该对象继承于BBinder对象。

3.2.6 总结

所以说 data.writeStrongBinder(Service)最终等价于parcel->writeStringBinder(new JavaBBinder(env, obj));

3.2.7 writeStrongBinder() 函数

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

我们看到writeStrongBinder()函数 实际上是调用的flatten_binder()函数

3.2.8 writeStrongBinder() 函数

//frameworks/native/libs/binder/Parcel.cpp    205行
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) {
            //如果不是本地Binder
            BpBinder *proxy = binder->remoteBinder();
            const int32_t handle = proxy ? proxy->handle() : 0;
            //远程Binder
            obj.type = BINDER_TYPE_HANDLE; 
            obj.binder = 0; 
            obj.handle = handle;
            obj.cookie = 0;
        } else {
            //如果是本地Binder
            obj.type = BINDER_TYPE_BINDER; 
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);
        }
    } else {
        //本地Binder
        obj.type = BINDER_TYPE_BINDER;  
        obj.binder = 0;
        obj.cookie = 0;
    }
    return finish_flatten_binder(binder, obj, out);
}

将Binder对象扁平化,转换成flat_binder_object对象

  • 对于Binder实体,则cookie记录Binder实体指针
  • 对于Binder代理,则用handle记录Binder代理的句柄

关于localBinder,在Binder.cpp里面

BBinder* BBinder::localBinder()
{
    return this;
}

BBinder* IBinder::localBinder()
{
    return NULL;

在最后面调用了finish_flatten_binder()函数,那我们再研究下finish_flatten_binder()函数

3.2.9 finish_flatten_binder() 函数

//frameworks/native/libs/binder/Parcel.cpp    199行
inline static status_t finish_flatten_binder(
    const sp<IBinder>& , const flat_binder_object& flat, Parcel* out)
{
    return out->writeObject(flat, false);
}

这个大家看明白了吧,就是写入一个object。

3.3 IBinder.transact()

ServiceManagerProxy的addService()中的mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);里面的mRemote的类型是BinderProxy的,所以调用是BinderProxy的transact()方法,那我们就进去看看

3.2.3.1 BinderProxy.transact()

温馨提示:BinderProxy类是Binder类的内部类
他其实是重写的IBinder的里面的transact()方法,那让我们看下IBinder里面

// frameworks/base/core/java/android/os/IBinder.java  223行
    /**
     * Perform a generic operation with the object.
     * 
     * @param code The action to perform.  This should
     * be a number between {@link #FIRST_CALL_TRANSACTION} and
     * {@link #LAST_CALL_TRANSACTION}.
     * @param data Marshalled data to send to the target.  Must not be null.
     * If you are not sending any data, you must create an empty Parcel
     * that is given here.
     * @param reply Marshalled data to be received from the target.  May be
     * null if you are not interested in the return value.
     * @param flags Additional operation flags.  Either 0 for a normal
     * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
     */
    public boolean transact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException;

主要是这个注释

  • 用对象执行一个操作
  • 参数code 为操作码,是介于FIRST_CALL_TRANSACTION和LAST_CALL_TRANSACTION之间
  • 参数data 是要发往目标的数据,一定不能null,如果你没有数据要发送,你也要创建一个Parcel,哪怕是空的。
  • 参数reply 是从目标发过来的数据,如果你对这个数据没兴趣,这个数据是可以为null的。
  • 参数flags 一个操作标志位,要么是0代表普通的RPC,要么是FLAG_ONEWAY代表单一方向的RPC即不管返回值

这时候我们再回来看

/frameworks/base/core/java/android/os/Binder.java   501行
final class BinderProxy implements IBinder {
    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
        if (Binder.isTracingEnabled()) { Binder.getTransactionTracker().addTrace(); }
        return transactNative(code, data, reply, flags);
    }
}

3.3.2 Binder.checkParcel()

/frameworks/base/core/java/android/os/Binder.java   415行
    static void checkParcel(IBinder obj, int code, Parcel parcel, String msg) {
        if (CHECK_PARCEL_SIZE && parcel.dataSize() >= 800*1024) {
            // Trying to send > 800k, this is way too much
            StringBuilder sb = new StringBuilder();
            sb.append(msg);
            sb.append(": on ");
            sb.append(obj);
            sb.append(" calling ");
            sb.append(code);
            sb.append(" size ");
            sb.append(parcel.dataSize());
            sb.append(" (data: ");
            parcel.setDataPosition(0);
            sb.append(parcel.readInt());
            sb.append(", ");
            sb.append(parcel.readInt());
            sb.append(", ");
            sb.append(parcel.readInt());
            sb.append(")");
            Slog.wtfStack(TAG, sb.toString());
        }
    }
  • 这段代码很简单,主要是检查Parcel大小是否大于800K
  • 执行完Binder.checkParcel后,直接调用了transactNative()方法,那我们就来看看transactNative()方法

3.3.3 transactNative()方法

// frameworks/base/core/java/android/os/Binder.java   507行
    public native boolean transactNative(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;

我们看到他是一个native函数,后面肯定经过JNI调用到了native层,根据包名,它对应的方法应该是"android_os_BinderProxy_transact"函数,那我们继续跟踪

3.3.4 android_os_BinderProxy_transact()函数

// frameworks/base/core/jni/android_util_Binder.cpp     1083行
   static jboolean android_os_BinderProxy_transact(JNIEnv*env, jobject obj,
                                                    jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
    {
        if (dataObj == NULL) {
            jniThrowNullPointerException(env, NULL);
            return JNI_FALSE;
        }
        // 将 java Parcel转化为native Parcel
        Parcel * data = parcelForJavaObject(env, dataObj);
        if (data == NULL) {
            return JNI_FALSE;
        }
        Parcel * reply = parcelForJavaObject(env, replyObj);
        if (reply == NULL && replyObj != NULL) {
            return JNI_FALSE;
        }
        // gBinderProxyOffsets.mObject中保存的是new BpBinder(0)对象
        IBinder * target = (IBinder *)
        env -> GetLongField(obj, gBinderProxyOffsets.mObject);
        if (target == NULL) {
            jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
            return JNI_FALSE;
        }

        ALOGV("Java code calling transact on %p in Java object %p with code %"PRId32"\n",
                target, obj, code);


        bool time_binder_calls;
        int64_t start_millis;
        if (kEnableBinderSample) {
            // Only log the binder call duration for things on the Java-level main thread.
            // But if we don't
            time_binder_calls = should_time_binder_calls();

            if (time_binder_calls) {
                start_millis = uptimeMillis();
            }
        }

        //printf("Transact from Java code to %p sending: ", target); data->print();
         // 此处便是BpBinder:: transact(),经过native层,进入Binder驱动。
        status_t err = target -> transact(code, * data, reply, flags);
        //if (reply) printf("Transact from Java code to %p received: ", target); reply->print();

        if (kEnableBinderSample) {
            if (time_binder_calls) {
                conditionally_log_binder_call(start_millis, target, code);
            }
        }

        if (err == NO_ERROR) {
            return JNI_TRUE;
        } else if (err == UNKNOWN_TRANSACTION) {
            return JNI_FALSE;
        }
        signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data -> dataSize());
        return JNI_FALSE;
    }

通过上面的代码我们知道,Java层BinderProxy.transact()最终交由Native层的BpBinder::transact()完成。这部分之前代码讲解过了,我这里就不详细说明了。不过注意,该方法可能会抛出RemoteException。

参考

Android跨进程通信IPC之10——Binder之Framework层Java篇

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,552评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,666评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,519评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,180评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,205评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,344评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,781评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,449评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,635评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,467评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,515评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,217评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,775评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,851评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,084评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,637评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,204评论 2 341