BroadcastReceiver源码解析

一、原理介绍

从实现原理看上,Android中的广播使用了观察者模式,基于消息的发布/订阅事件模型。因此,从实现的角度来看,Android中的广播将广播的发送者和接受者极大程度上解耦,使得系统能够方便集成,更易扩展。对于不同的广播类型,以及不同的BroadcastReceiver注册方式,具体实现上会有不同。

1.1 模型角色
  • 消息订阅者BroadcastReceiver(广播接收者)
  • 消息发布者(广播发布者)
  • 消息中心AMS(Activity Manager Service)
1.2 实现流程

1、广播接收者 通过Binder机制向AMS进行注册;

  • 动态注册过程是注册Receiver并通知AMS,发送内容包括 IntentFilter、一个实现了 IIntentReceiver 接口的Binder对象、Activity所在的进程。
  • AMS 收到消息后,会把所有 Receiver 保存在一个列表中。
  • 静态 Receiver 是在App安装的时候注册,PMS 会解析 AndroidManifest 中的四大组件信息。
  • 动态 Receiver 和静态 Receiver 分别存在 AMS 不同的变量中,发送广播时,会把两种 Receiver 合并到一起,其中动态的排在静态的前面,所以动态 Receiver 永远优先于静态 Receiver 收到消息。

2、广播发送者 通过Binder机制向AMS发送广播;
3、AMS根据广播发送者的要求(IntentFilter/Permission等),在已注册列表中,寻找合适的广播接收者;
4、AMS将广播发送到合适的广播接收者相应的消息循环队列中;
5、广播接收者 通过消息循环拿到此广播,并回调BroadcastReceiver中的onReceive();

15821503-67a8daa0e9b103f3.png

二、源码分析

1、BroadcastReceiver动态注册

1.1 registerReceiver调用的是Context的registerReceiver

我们查看App进程发起注册,ContextImpl的registerReceiver

@Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        return registerReceiver(receiver, filter, null, null);
    }

 @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
            String broadcastPermission, Handler scheduler) {
        return registerReceiverInternal(receiver, getUserId(),
                filter, broadcastPermission, scheduler, getOuterContext(), 0);
    }

 private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context, int flags) {
        IIntentReceiver rd = null;
        if (receiver != null) {
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
            } else {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            }
        }
        try {
            final Intent intent = ActivityManager.getService().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
                    broadcastPermission, userId, flags);
            if (intent != null) {
                intent.setExtrasClassLoader(getClassLoader());
                intent.prepareToEnterProcess();
            }
            return intent;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

1、IIntentReceiver是一个Binder对象,传递给AMS用于AMS回调App进程,回调的方法是performReceive。一个Context和一个BroadcaseReceiver构成一个二元组,确定一个IIntentReceiver。

AMS -> IIntentReceiver -> ReceiverDispatcher -> BroadcastReceiver

2、mPackageInfo.getReceiverDispatcher 构建接收器

3、ActivityManager.getService().registerReceiver()通知AMS注册接收器

1.2 AMS端处理注册
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
           IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
           int flags) {
      ···

           ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
           if (rl == null) {
               rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                       userId, receiver);
               if (rl.app != null) {
                   rl.app.receivers.add(rl);
               }
               mRegisteredReceivers.put(receiver.asBinder(), rl);
           }
           BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                   permission, callingUid, userId, instantApp, visibleToInstantApps);
               rl.add(bf);
          mReceiverResolver.addFilter(bf);
       }
   }

1、 mRegisteredReceivers是个HashMap<IBinder, ReceiverList>,这个Binder就是IIntentReceiver,ReceiverList记录了所有的这个与IIntentReceiver对应的接收者,将注册接收者添加到Map中

2、普通广播发送后的动态接收

2.1 调用Context的sendBroadcast

最后会调用到

ContextImpl.java

 public void sendBroadcast(Intent intent) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
            intent.prepareToLeaveProcess(this);
            ActivityManager.getService().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                    getUserId());
    }
2.2 AMS处理broadcastIntent
ActivityManagerService.java

public final int broadcastIntent(IApplicationThread caller,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle resultExtras,
            String[] requiredPermissions, int appOp, Bundle bOptions,
            boolean serialized, boolean sticky, int userId) {
            ···
            int res = broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null,
                    intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                    requiredPermissions, appOp, bOptions, serialized, sticky,
                    callingPid, callingUid, userId);
            ···
        }
    }

final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, Intent intent, String resolvedType,
            IIntentReceiver resultTo, int resultCode, String resultData,
            Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
            boolean ordered, boolean sticky, int callingPid, int callingUid, int userId){
           ···
           //1 找到匹配的Receiver列表
            registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false /*defaultOnly*/, userId); 
            int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
            //是否是有序广播,并且存在接收者
             if (!ordered && NR > 0) {
            //取出普通任务queue
            final BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                    requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
                    resultCode, resultData, resultExtras, ordered, sticky, false, userId);
            //添加到并行分发queue
            queue.enqueueParallelBroadcastLocked(r);
            //开始分发广播
            queue.scheduleBroadcastsLocked();
            registeredReceivers = null;
            NR = 0;
        }        
 }
2.3 AMS发送到队列处理广播

queue.scheduleBroadcastsLocked();会发送BROADCAST_INTENT_MSG,调用processNextBroadcast(true);

BroadcastQueue.java

final void processNextBroadcast(boolean fromMsg) {
        synchronized (mService) {
            processNextBroadcastLocked(fromMsg, false);
        }
    }

 final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj){
     while (mParallelBroadcasts.size() > 0) {
            r = mParallelBroadcasts.remove(0);

            final int N = r.receivers.size();
            //依次取出分发
            for (int i=0; i<N; i++) {
                Object target = r.receivers.get(i);
                //调用
                deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
            }
        }
 }

private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
            BroadcastFilter filter, boolean ordered, int index) {
       ···      //分发广播到客户端
                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                        new Intent(r.intent), r.resultCode, r.resultData,
                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
         ····

    }

 void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
            Intent intent, int resultCode, String data, Bundle extras,
            boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
        // Send the intent to the receiver asynchronously using one-way binder calls.
        //判断应是否为空
        if (app != null) {
            if (app.thread != null) {
                    //通知客户端分发广播,Binder采用 oneway 异步 应用端会自动串行化
                    app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                            data, extras, ordered, sticky, sendingUser, app.repProcState);
            }
        } else {
            receiver.performReceive(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
        }
    }
2.4 客户端处理receiver

AMS端的app.thread.scheduleRegisteredReceiver(),会调用到注册时注册的InnerReceiver的performReceive方法

ActivityThread.java

public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
                int resultCode, String dataStr, Bundle extras, boolean ordered,
                boolean sticky, int sendingUser, int processState) throws RemoteException {
            updateProcessState(processState, false);
            //1
            receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
                    sticky, sendingUser);
        }
InnerReceiver.java

public void performReceive(Intent intent, int resultCode, String data,
                    Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                final LoadedApk.ReceiverDispatcher rd;
                ···
                if (rd != null) {
                  //调用LoadedApk.ReceiverDispatcher的performReceive
                    rd.performReceive(intent, resultCode, data, extras,
                            ordered, sticky, sendingUser);
                ···
                }
            }
LoadedApk.ReceiverDispatcher.java

public void performReceive(Intent intent, int resultCode, String data,
                Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
            final Args args = new Args(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
            ···
            //向ActivityThread发送一个Runnable
            if (intent == null || !mActivityThread.post(args.getRunnable())) {

            }
        }
Args.java

public final Runnable getRunnable() {
                return () -> {
                    final BroadcastReceiver receiver = mReceiver;
                    final boolean ordered = mOrdered;
=
                    final IActivityManager mgr = ActivityManager.getService();
                    ···
                        receiver.setPendingResult(this);
                        //这里触发了BoradcastReciever回调
                        receiver.onReceive(mContext, intent);
                    ···
                };
            }

3、动态注册解析总结

1、首先动态注册BroadcastReceiver到AMS,将他们存储在一个Map中,Map的key是IIntentReceiver用于回调注册端,value是一个ReceiverList。

2、发送这发送Intent到AMS,筛选出匹配的ReceiverList,遍历通知注册端。

4、BroadcastReceiver静态注册接收

4.1 PMS解析清单文件获取注册信息
PackageParser.java

private boolean parseBaseApplication(Package owner, Resources res,
         XmlResourceParser parser, int flags, String[] outError){

       else if (tagName.equals("receiver")) {
             Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
                     true, false);
             owner.receivers.add(a);

         }  
}

1、将receiver解析成一个Activity(这个Activity表示的是一个组件)对象,添加到receivers的列表中,receivers是一个ArrayList。

5、普通广播发送后的静态接收

5.1 调用Context的sendBroadcast

最后会调用到

ContextImpl.java

 public void sendBroadcast(Intent intent) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
            intent.prepareToLeaveProcess(this);
            ActivityManager.getService().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                    getUserId());
    }
5.2 AMS处理broadcastIntent
ActivityManagerService.java

public final int broadcastIntent(IApplicationThread caller,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle resultExtras,
            String[] requiredPermissions, int appOp, Bundle bOptions,
            boolean serialized, boolean sticky, int userId) {
            ···
            int res = broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null,
                    intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                    requiredPermissions, appOp, bOptions, serialized, sticky,
                    callingPid, callingUid, userId);
            ···
        }
    }

final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, Intent intent, String resolvedType,
            IIntentReceiver resultTo, int resultCode, String resultData,
            Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
            boolean ordered, boolean sticky, int callingPid, int callingUid, int userId){
           ···

            //获取接收静态广播
            receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
            //通过Intent查找能接受的动态广播
             registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false /*defaultOnly*/, userId);
            int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
            if (!ordered && NR > 0) {
                //处理动态广播,添加到并行分发队列,OneWay发送binder处理是串行的
            }
          //给没有处理完的动态receiver(order是true),跟静态receiver合并到一起

          if ((receivers != null && receivers.size() > 0){
                //处理剩下的receiver。加到串行分发队列
                //获取queue
                BroadcastQueue queue = broadcastQueueForIntent(intent);
                BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                    requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                    resultData, resultExtras, ordered, sticky, false, userId);
                //添加到串行分发queue
                queue.enqueueOrderedBroadcastLocked(r);
                //处理分发
                queue.scheduleBroadcastsLocked();
          }
          return ActivityManager.BROADCAST_SUCCESS;
 }
5.3 AMS发送到队列处理广播

queue.scheduleBroadcastsLocked();会发送BROADCAST_INTENT_MSG,调用processNextBroadcast(true);

BroadcastQueue.java

final void processNextBroadcast(boolean fromMsg) {
        synchronized (mService) {
            processNextBroadcastLocked(fromMsg, false);
        }
    }

 final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj){
     while (mParallelBroadcasts.size() > 0) {
            r = mParallelBroadcasts.remove(0);

            final int N = r.receivers.size()
            for (int i=0; i<N; i++) {
                Object target = r.receivers.get(i);
                //先并行分发,然后接下来分发串行广播
                deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
            }
        }
     //如果有pedding广播,先直接返回,这个广播在等待应用进程启动
     //如果当前广播分发超时了,废弃这个广播,处理下一个广播
      broadcastTimeoutLocked(false);
     //如果没有超时,并且在分发中,就先返回。什么也不做
     //如果当前的广播已经分发完一个receiver,就继续分发下一个receiver
     //如果这个receiver是动态注册的receiver就直接分发
     //如果这个receiver是静态注册的receiver,先看进程启动没有
     //如果进程启动了,就直接分发
       processCurBroadcastLocked(r, app, skipOomAdj);
     //没启动的话就先启动进程,然后给广播标记为pedding
     //进程启动后attachApplication时继续处理这个pending的广播
 }
BroadcastQueue.java
 //处理超时
 final void broadcastTimeoutLocked(boolean fromMsg) {

        BroadcastRecord r = mOrderedBroadcasts.get(0);
        Object curReceiver = r.receivers.get(r.nextReceiver-1);
        //找到当前分发receiver对应的进程

        if (mPendingBroadcast == r) {
            mPendingBroadcast = null;
        }

        // Move on to the next receiver.
        finishReceiverLocked(r, r.resultCode, r.resultData,
                r.resultExtras, r.resultAbort, false);
        //发送消息处理下一个receiver
        scheduleBroadcastsLocked();

        if (!debugging && anrMessage != null) {
            // Post the ANR to the handler since we do not want to process ANRs while
            // potentially holding our lock.
            //超时显示ANR
            mHandler.post(new AppNotResponding(app, anrMessage));
        }
    }
BroadcastQueue.java

//接收进程存在,通知接收进程
 private final void processCurBroadcastLocked(BroadcastRecord r,
            ProcessRecord app, boolean skipOomAdj) throws RemoteException {
        ···
            app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                    mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
                    r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
                    app.repProcState);
         ···
    }
5.4 接收进程接收广播

5.4.1 进程存在直接处理

ActivityThread.java

 public final void scheduleReceiver(Intent intent, ActivityInfo info,
                CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
                boolean sync, int sendingUser, int processState) {
            updateProcessState(processState, false);
            ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
                    sync, false, mAppThread.asBinder(), sendingUser);
            r.info = info;
            r.compatInfo = compatInfo;
            sendMessage(H.RECEIVER, r);
        }

 private void handleReceiver(ReceiverData data) {
        sCurrentBroadcastIntent.set(data.intent);
        receiver.setPendingResult(data);
        //执行BroadcastReceiver
        //依据Application的Context创建的ContextWarpper,目的是不允许BroadcastReceiver在注册接收器,也不允许启动服务
        receiver.onReceive(context.getReceiverRestrictedContext(),
                    data.intent);

        if (receiver.getPendingResult() != null) {
            //通知AMS分发结束
            data.finish();
        }
    }
BroadcastReceiver.java

   public final void finish() {
            if (mType == TYPE_COMPONENT) {
                final IActivityManager mgr = ActivityManager.getService();
                if (QueuedWork.hasPendingWork()) {
                    QueuedWork.queue(new Runnable() {
                        @Override public void run() {
                            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                    "Finishing broadcast after work to component " + mToken);
                            sendFinished(mgr);
                        }
                    }, false);
                } else {
                    sendFinished(mgr);
                }
            } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
                final IActivityManager mgr = ActivityManager.getService();
                sendFinished(mgr);
            }
        }

  public void sendFinished(IActivityManager am) {
      //通知AMS分发结束
            am.finishReceiver(mToken, 0, null, null, false, mFlags);

        }

5.4.2 进程不存在

在attachApplication中处理PenndingReceiver

ActivityManagerService.java

 private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
            ···
            sendPendingBroadcastsLocked(app);
            ···

 }

  boolean sendPendingBroadcastsLocked(ProcessRecord app) {
        boolean didSomething = false;
        for (BroadcastQueue queue : mBroadcastQueues) {
            didSomething |= queue.sendPendingBroadcastsLocked(app);
        }
        return didSomething;
  }
BroadcastQueue.java

  public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
       ···
                processCurBroadcastLocked(br, app, false);
       ···
    }

   private final void processCurBroadcastLocked(BroadcastRecord r,
            ProcessRecord app, boolean skipOomAdj) throws RemoteException {
       ···
          //通知客户端调用接收方法
            app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                    mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
                    r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
                    app.repProcState);
        ···
    }

app.thread.scheduleReceiver,发送RECEIVER消息给ActivityThread,调用到ActivityThread的handleReceiver

ActivityThread.java

private void handleReceiver(ReceiverData data) {
        ···

            sCurrentBroadcastIntent.set(data.intent);
            receiver.setPendingResult(data);
            //回调BroadcastReceiver
            receiver.onReceive(context.getReceiverRestrictedContext(),
                    data.intent);
        } catch (Exception e) {
            data.sendFinished(mgr);
        } finally {
            sCurrentBroadcastIntent.set(null);
        }

        if (receiver.getPendingResult() != null) {
            //通知分发结束
            data.finish();
        }
    }

6、静态态注册解析总结

1、首先要解析清单文件的receivers

2、发送Intent到AMS,AMS串行处理Intent,AMS需要收到客户端的分发结果在分发下一个,如果超时报ANR异常

3、如果分发进程不存在启动进程后在分发,如果存在直接分发

三、时序图

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

推荐阅读更多精彩内容