Android Handler机制5--消息发送

移步Android Handler机制详解

  • 1、Handler发送消息
  • 2、Handler的send方案
  • 3、Handler的post方案

1 Handler发送消息

send方案.png

post方案.png
  • send方案发送消息(需要回调才能接收消息)
  1. sendMessage(Message) 立即发送Message到消息队列
  2. sendMessageAtFrontOfQueue(Message) 立即发送Message到队列,而且是放在队列的最前面
  3. sendMessageAtTime(Message,long) 设置时间,发送Message到队列
  4. sendMessageDelayed(Message,long) 延时若干毫秒后,发送Message到队列
  • post方案 立即发送Message到消息队列
  1. post(Runnable) 立即发送Message到消息队列
  2. postAtFrontOfQueue(Runnable) 立即发送Message到队列,而且是放在队列的最前面
  3. postAtTime(Runnable,long) 设置时间,发送Message到队列
  4. postDelayed(Runnable,long) 在延时若干毫秒后,发送Message到队列

2 Handler的send方案

以Handler的sendMessage(Message msg)为例子

2.1 sendMessage(Message msg)方法

代码在Handler.java 505行

/**
     * Pushes a message onto the end of the message queue after all pending messages
     * before the current time. It will be received in {@link #handleMessage},
     * in the thread attached to this handler.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
  • 在当前时间,在所有待处理消息之后,将消息推送到消息队列的末尾。在和当前线程关联的的Handler里面的handleMessage将收到这条消息

2.1.1 boolean sendMessageDelayed(Message msg, long delayMillis)

代码在Handler.java 565行

/**
     * Enqueue a message into the message queue after all pending messages
     * before (current time + delayMillis). You will receive it in
     * {@link #handleMessage}, in the thread attached to this handler.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

该方法内部就做了两件事

  • 1、判断delayMillis是否小于0
  • 2、调用了public boolean sendMessageAtTime(Message msg, long uptimeMillis)方法

2.1.2 boolean sendMessageAtTime(Message msg, long uptimeMillis)

代码在Handler.java 592行

/**
     * Enqueue a message into the message queue after all pending messages
     * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     * You will receive it in {@link #handleMessage}, in the thread attached
     * to this handler.
     * 
     * @param uptimeMillis The absolute time at which the message should be
     *         delivered, using the
     *         {@link android.os.SystemClock#uptimeMillis} time-base.
     *         
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
  • 以android系统的SystemClock的uptimeMillis()为基准,以毫秒为基本单位的绝对时间下,在所有待处理消息后,将消息放到消息队列中。
  • 深度睡眠中的时间将会延迟执行的时间,你将在和当前线程办的规定的Handler中的handleMessage中收到该消息。
  • 因为通常我们理解的异步是指新开一个线程,但是这里不是,因为异步的也是发送到looper所绑定的消息队列中,这里的异步主要是针对Message中的障栅(Barrier)而言的,当出现障栅(Barrier)的时候,同步的会被阻塞,而异步的则不会。所以这个异步仅仅是一个标记而已。

该方法内部就做了两件事

  • 1、获取消息队列,并对该消息队列做非空判断,如果为null,直接返回false,表示消息发送失败
  • 2、调用了boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)方法

2.1.3 boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

代码在Handler.java 626行

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

本方法内部做了三件事

    1. 设置msg的target变量,并将target指向自己
    1. 如果Handler的mAsynchronous值为true(默认为false,即不设置),则设置msg的flags值,让是否异步在Handler和Message达成统一。
    1. 调用MessageQueue的enqueueMessage()方法

2.1.4 boolean enqueueMessage(Message msg, long when)方法

代码在MessageQueue.java 533行

boolean enqueueMessage(Message msg, long when) {
        //第一步
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        // 第二步
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }
        // 第三步
        synchronized (this) {
             // 第四步
            //判断消息队列是否正在关闭
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }
             // 第五步
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
             // 第六步
            //根据when的比较来判断要添加的Message是否应该放在队列头部,当第一个添加消息的时候,
            // 测试队列为空,所以该Message也应该位于头部。
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                // 把msg的下一个元素设置为p
                msg.next = p;
                // 把msg设置为链表的头部元素
                mMessages = msg;
                 // 如果有阻塞,则需要唤醒
                needWake = mBlocked;
            } else {
                 // 第七步
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                //除非消息队列的头部是障栅(barrier),或者消息队列的第一个消息是异步消息,
                //否则如果是插入到中间位置,我们通常不唤醒消息队列,
                 // 第八步
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                  // 第九步
                 // 不断遍历消息队列,根据when的比较找到合适的插入Message的位置。
                for (;;) {
                    prev = p;
                    p = p.next;
                    // 第十步
                    if (p == null || when < p.when) {
                        break;
                    }
                    // 第十一 步
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                // 第十二步
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
              // 第十三步
            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
         // 第十四步
        return true;
    }
  • 第1步骤、 判断msg的target变量是否为null,如果为null,则为障栅(barrier),而障栅(barrier)入队则是通过postSyncBarrier()方法入队,所以msg的target一定有值
  • 第2步骤、 判断msg的标志位,因为此时的msg应该是要入队,意味着msg的标志位应该显示还未被使用。如果显示已使用,明显有问题,直接抛异常。
  • 第3步骤、 加入同步锁。
  • 第4步骤、 判断消息队列是否正在被关闭,如果是正在被关闭,则return false告诉消息入队是失败,并且回收消息
  • 第5步骤、 设置msg的when并且修改msg的标志位,msg标志位显示为已使用
  • 第6步骤、 如果p==null则说明消息队列中的链表的头部元素为null;when == 0 表示立即执行;when< p.when 表示 msg的执行时间早与链表中的头部元素的时间,所以上面三个条件,那个条件成立,都要把msg设置成消息队列中链表的头部是元素
  • 第7步骤、 如果上面三个条件都不满足则说明要把msg插入到中间的位置,不需要插入到头部
  • 第8步骤、 如果头部元素不是障栅(barrier)或者异步消息,而且还是插入中间的位置,我们是不唤醒消息队列的。
  • 第9步骤、 进入一个死循环,将p的值赋值给prev,前面的带我们知道,p指向的是mMessage,所以这里是将prev指向了mMessage,在下一次循环的时候,prev则指向了第一个message,一次类推。接着讲p指向了p.next也就是mMessage.next,也就是消息队列链表中的第二个元素。这一步骤实现了消息指针的移动,此时p表示的消息队列中第二个元素。
  • 第10步骤、 p==null,则说明没有下一个元素,即消息队列到头了,跳出循环;p!=null&&when < p.when 则说明当前需要入队的这个message的执行时间是小于队列中这个任务的执行时间的,也就是说这个需要入队的message需要比队列中这个message先执行,则说明这个位置刚刚是适合这个message的,所以跳出循环。 如果上面的两个条件都不满足,则说明这个位置还不是放置这个需要入队的message,则继续跟链表中后面的元素,也就是继续跟消息队列中的下一个消息进行对比,直到满足条件或者到达队列的末尾。
  • 第11步骤、 因为没有满足条件,说明队列中还有消息,不需要唤醒。
  • 第12步骤、 跳出循环后主要做了两件事:事件A,将入队的这个消息的next指向循环中获取到的应该排在这个消息之后message。事件B,将msg前面的message.next指向了msg。这样就将一个message完成了入队。
  • 第13步骤、 如果需要唤醒,则唤醒,具体请看后面的Handler中的Native详解。
  • 第14步骤、 返回true,告知入队成功。

插入消息后,有条件地执行nativeWake去唤醒epoll。needWake的值依赖mBlocked——当进入next()方法时没有待处理的消息,mBlock为true,有消息并返回到looper时,mBlock为false。

结合next()和enqueueMessage()方法,得知nativeWake被调用的条件为:

  1. next()方法在等待新消息,且新插入消息的为链表头时。needWake为true
  2. 设置了Sync Barrier,且插入的消息是“异步”的。needWake为true

2.2 boolean sendMessageAtFrontOfQueue(Message msg)

代码在Handler.java 615行

/**
     * Enqueue a message at the front of the message queue, to be processed on
     * the next iteration of the message loop.  You will receive it in
     * {@link #handleMessage}, in the thread attached to this handler.
     * <b>This method is only for use in very special circumstances -- it
     * can easily starve the message queue, cause ordering problems, or have
     * other unexpected side-effects.</b>
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, 0);
    }
  • 在消息队列的最前面插入一个消息,在消息循环的下一次迭代中进行处理。
  • 由于它可以轻松的解决消息队列的排序问题和其他的意外副作用。

方法内部的实现和boolean sendMessageAtTime(Message msg, long uptimeMillis)大体上一致,唯一的区别就是该方法在调用enqueueMessage(MessageQueue, Message, long)方法的时候,最后一个参数是0而已。

2.3 boolean sendEmptyMessage(int what)

代码在Handler.java 517行

/**
     * Sends a Message containing only the what value.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

发送一个仅有what的Message
boolean sendEmptyMessageDelayed(int what, long delayMillis)
代码在Handler.java 531行

/**
     * Sends a Message containing only the what value, to be delivered
     * after the specified amount of time elapses.
     * @see #sendMessageDelayed(android.os.Message, long) 
     * 
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
  • 发送一个仅有what的Message,并且延迟特定的时间发送
  • 这个方法内部主要就是做了3件事

1、调用Message.obtain();从消息对象池中获取一个空的Message。
2、设置这个Message的what值
3、调用sendMessageDelayed(Message,long) 将这个消息方法

2.4 boolean sendEmptyMessageAtTime(int what, long uptimeMillis)

代码在Handler.java 547行

/**
     * Sends a Message containing only the what value, to be delivered 
     * at a specific time.
     * @see #sendMessageAtTime(android.os.Message, long)
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */

    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }
  • 发送一个仅有what的Message,并且在特定的时间发送
  • 这个方法内部主要就是做了3件事
  • 1、调用Message.obtain();从消息对象池中获取一个空的Message。
  • 2、设置这个Message的what值
  • 3、调用sendMessageAtTime(Message,long) 将这个消息方法

2.5 小结

这些send方案都会从这里或者那里最终走到boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)


5713484-e56c82c4e36768f5.png

3 Handler的post方案

3.1 boolean post(Runnable r)方法

代码在Handler.java 324行

/**
     * Causes the Runnable r to be added to the message queue.
     * The runnable will be run on the thread to which this handler is 
     * attached. 
     *  
     * @param r The Runnable that will be executed.
     * 
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
  • 将一个Runnable添加到消息队列中,这个runnable将会在和当前Handler关联的线程中被执行。

  • 就是调用了sendMessageDelayed(Message, long);这个方法,所以可见boolean post(Runnable r)这个方法最终还是走到上面说到的send的流程中

  • 最终调用boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)。

3.1.1 Message getPostMessage(Runnable r)方法

代码在Handler.java 725行

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

代码很简单,主要是做了两件事

  • 通过Message.obtain()从消息对象池中获取一个空的Message
  • 将这空的Message的callback变量指向Runnable

最后返回这个Message m。

3.1.2 小结

  • boolean post(Runnable r)方法的内置也是通过Message.obtain()来获取一个Message对象m,然后仅仅把m的callback指向参数r而已。
  • 最后最终通过调用send方案的某个流程最终调用到boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

3.2 boolean postAtTime(Runnable r, long uptimeMillis)方法

代码在Handler.java 347行

/**
     * Causes the Runnable r to be added to the message queue, to be run
     * at a specific time given by <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     * The runnable will be run on the thread to which this handler is attached.
     *
     * @param r The Runnable that will be executed.
     * @param uptimeMillis The absolute time at which the callback should run,
     *         using the {@link android.os.SystemClock#uptimeMillis} time-base.
     *  
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the Runnable will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public final boolean postAtTime(Runnable r, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }
  • 将一个Runnable添加到消息队列中,这个runnable将会一个特定的时间被执行,这个时间是以android.os.SystemClock.uptimeMillis()为基准。
  • 如果在深度睡眠下,会推迟执行的时间,这个Runnable将会在和当前Hander关联的线程中被执行。
  • 方法内部也是先是调用getPostMessage(Runnable)来获取一个Message,这个Message的callback字段指向了这个Runnable,然后调用sendMessageAtTime(Message,long)。

3.3 boolean postAtTime(Runnable r, Object token, long uptimeMillis)方法

代码在Handler.java 372行

/**
     * Causes the Runnable r to be added to the message queue, to be run
     * at a specific time given by <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     * The runnable will be run on the thread to which this handler is attached.
     *
     * @param r The Runnable that will be executed.
     * @param uptimeMillis The absolute time at which the callback should run,
     *         using the {@link android.os.SystemClock#uptimeMillis} time-base.
     * 
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the Runnable will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     *         
     * @see android.os.SystemClock#uptimeMillis
     */
    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }
  • 这个方法和上个方法唯一不同就是多了Object参数,而这个参数仅仅是把Message.obtain();获取的Message的obj字段的指向第二个入参token而已。最后也是调用sendMessageAtTime(Message,long)。

2.4 boolean postDelayed(Runnable r, long delayMillis)方法

代码在Handler.java 396行

/**
     * Causes the Runnable r to be added to the message queue, to be run
     * after the specified amount of time elapses.
     * The runnable will be run on the thread to which this handler
     * is attached.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     *  
     * @param r The Runnable that will be executed.
     * @param delayMillis The delay (in milliseconds) until the Runnable
     *        will be executed.
     *        
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the Runnable will be processed --
     *         if the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public final boolean postDelayed(Runnable r, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

这个方法也很简单,就是依旧是通过getPostMessage(Runnable)来获取一个Message,最后调用sendMessageDelayed(Message,long)而已。

3.5 boolean postAtFrontOfQueue(Runnable r)方法

代码在Handler.java 416行

/**
     * Posts a message to an object that implements Runnable.
     * Causes the Runnable r to executed on the next iteration through the
     * message queue. The runnable will be run on the thread to which this
     * handler is attached.
     * <b>This method is only for use in very special circumstances -- it
     * can easily starve the message queue, cause ordering problems, or have
     * other unexpected side-effects.</b>
     *  
     * @param r The Runnable that will be executed.
     * 
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean postAtFrontOfQueue(Runnable r)
    {
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }

就是依旧是通过getPostMessage(Runnable)来获取一个Message,最后调用sendMessageAtFrontOfQueue(Message)而已。

3.6 小结

  • Handler的post方案:都是通过Message getPostMessage(Runnable )中调用Message m = Message.obtain();来获取一个空的Message,然后把这个Message的callback变量指向了Runnable,最终调用相应的send方案而已。

所以我们可以这样说:
Handler的发送消息(障栅除外),无论是通过send方案还是pos方案最终都会做走到 boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 中

5713484-5d5c06c67b925a3d.png

参考

Android Handler机制7之消息发送

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

推荐阅读更多精彩内容