Android 廣播注冊發送與接收原理簡介與源碼分析

源碼基于Android 11 API 30

BroadcastReceiver是一種消息型組件,用于在不同的組件或者是不同的應用之間傳遞信息。同樣的BroadcastReceiver也不能被用戶感知,BroadcastReceiver也叫廣播,廣播的注冊有兩種方式:靜態注冊和動態注冊。靜態注冊是指在AndroidManifest中注冊廣播,這種廣播在應用安裝時會被系統解析,此種形式的廣播不需要應用啟動就可以收到相應的廣播。動態注冊廣播需要通過Context.registerReceiver()來實現,并且在不需要的時候要通過Context.unRegisterReceiver()來解除廣播,此種形態的廣播必須要應用啟動才能注冊并接收廣播,因為應用不啟動就無法注冊廣播,無法注冊廣播就無法收到相應的廣播。在實際開發中通過Context的一系列send方法來發送廣播,被發送的廣播會被系統發送給感興趣的廣播接收者,發送和接收過程的匹配是通過廣播接收者的<intent-filter>來描述的。可以發現,BroadcastReceiver組件可以用來實現低耦合的觀察者模式,觀察者和被觀察者之間可以沒有任何耦合。由于BroadcastReceiver的特性,它不適合用來執行耗時操作。BroadcastReceiver組件一般來說不需要停止,它也沒有停止的概念。
下面從廣播的注冊、發送與接收幾個過程展開介紹。

1. 廣播的注冊

廣播的注冊分為動態注冊與靜態注冊兩種。靜態注冊的注冊過程是在應用安裝時由PackageManager-Service完成注冊的。我們這里主要介紹動態注冊的過程。下面是一個典型的動態注冊、發送于接受廣播的代碼:

...
    //通常需要自定義一個BroadcastReceiver,這里只是示例避免代碼過長
    BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            //Do something
            abortBroadcast();
        }
    };
    IntentFilter intentFilter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_broadcast);
        intentFilter = new IntentFilter();
        intentFilter.addAction("com.example.myapplication.DEMOBROADCAST");
        registerReceiver(broadcastReceiver,intentFilter);

        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.myapplication.DEMOBROADCAST");
                sendBroadcast(intent);
            }
        });
    }
...

上面的代碼可以明顯的看到,廣播的著冊方法是registerReceiver,這個方法是在ContextWrapper中定義的,與Service的啟動與綁定類似,registerReceiver方法有調用了mBase的方法,mBase的實現是ContextImpl,ContextImpl的registerReceiver方法在嵌套調用后最后調用了registerReceiverInternal方法,具體看下代碼:

//ContextWrapper.java
...
 @Override
    public Intent registerReceiver(
        BroadcastReceiver receiver, IntentFilter filter) {
        return mBase.registerReceiver(receiver, filter);
    }
...

//ContextImpl.java
...
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context, int flags) {
        IIntentReceiver rd = null;
        if (receiver != null) {//判斷receiver是否存在
            
             //判斷mPackageInfo(LoadedApk類型)與Context是否都存在
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
              //獲取IIntentReceiver
                rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
            } else {
            //mPackageInfo不存在

                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
              //創建IIntentReceiver
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            }
        }
        try {
            //獲取AMS調用registerReceiverWithFeature方法
            final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
                    mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(), rd,
                    filter, broadcastPermission, userId, flags);
            if (intent != null) {
                intent.setExtrasClassLoader(getClassLoader());
                intent.prepareToEnterProcess();
            }
            return intent;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
...

最后可以看到調用了AMS的registerReceiverWithFeature,上面代碼中有一個重要變量rd,IIntentReceiver類型,這顯然是一個Binder接口,具體實現在LoadedApk.ReceiverDispatcher.InnerReceiver:

 static final class ReceiverDispatcher {

        final static class InnerReceiver extends IIntentReceiver.Stub {
            final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
            final LoadedApk.ReceiverDispatcher mStrongRef;

            InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
                mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
                mStrongRef = strong ? rd : null;
            }
...
}

回到AMS的registerReceiverWithFeature方法,方法比較長,核心處理添加了注釋:

public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
            String callerFeatureId, IIntentReceiver receiver, IntentFilter filter,
            String permission, int userId, int flags) {
        enforceNotIsolatedCaller("registerReceiver");
        ArrayList<Intent> stickyIntents = null;
        ProcessRecord callerApp = null;
        final boolean visibleToInstantApps
                = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
        int callingUid;
        int callingPid;
        boolean instantApp;
        synchronized(this) {
            if (caller != null) {
                //獲取ProcessRecord,其用于描述注冊的接收者Activity所在應用程序進程
                callerApp = getRecordForAppLocked(caller);
               ...
                callingUid = callerApp.info.uid;
                callingPid = callerApp.pid;
            } else {
                callerPackage = null;
                callingUid = Binder.getCallingUid();
                callingPid = Binder.getCallingPid();
            }

             ...
            //根據傳入的IntentFilter獲取Action列表
            Iterator<String> actions = filter.actionsIterator();
            if (actions == null) {
                ArrayList<String> noAction = new ArrayList<String>(1);
                noAction.add(null);
                actions = noAction.iterator();
            }

            // userIds表示應用程序的uid,對于Android系統來說,用戶就是一個個的應用程序。
            int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
            while (actions.hasNext()) {
                String action = actions.next();
                for (int id : userIds) {
                    //根據Action列表與應用程序列表(userIds)獲取所有粘性廣播(StickyBroadcast)
                    ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
                    if (stickies != null) {
                    //獲取所有粘性廣播的intent
                        ArrayList<Intent> intents = stickies.get(action);
                        if (intents != null) {
                            if (stickyIntents == null) {
                                stickyIntents = new ArrayList<Intent>();
                            }
                            //保存粘性廣播的intent
                            stickyIntents.addAll(intents);
                        }
                    }
                }
            }
        }

        ArrayList<Intent> allSticky = null;
        if (stickyIntents != null) {
            final ContentResolver resolver = mContext.getContentResolver();
            // 查找匹配的粘性廣播
            for (int i = 0, N = stickyIntents.size(); i < N; i++) {
                Intent intent = stickyIntents.get(i);
                // Don't provided intents that aren't available to instant apps. 
                if (instantApp &&
                        (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) {
                    continue;
                }
              ...
            }
        }

      ...

        synchronized (this) {
           ...
            //獲取ReceiverList
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) {
                //ReceiverList為空新建
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                if (rl.app != null) {
                  //判斷當前receiver是否超過允許的最的值
                    final int totalReceiversForApp = rl.app.receivers.size();
                    if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
                        throw new IllegalStateException("Too many receivers, total of "
                                + totalReceiversForApp + ", registered for pid: "
                                + rl.pid + ", callerPackage: " + callerPackage);
                    }
                    rl.app.receivers.add(rl);
                } else {
                   ...
                }
            }
            //創建BroadcastFilter用來記錄已注冊的廣播接收者信息
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
                    permission, callingUid, userId, instantApp, visibleToInstantApps);
            //判斷接收者列表ReceiverList中是否存在對應接收者,不存在則添加
            if (rl.containsFilter(filter)) {
                Slog.w(TAG, "Receiver with filter " + filter
                        + " already registered for pid " + rl.pid
                        + ", callerPackage is " + callerPackage);
            } else {
                rl.add(bf);
                if (!bf.debugCheck()) {
                    Slog.w(TAG, "==> For Dynamic broadcast");
                }
                //記錄接收者,AMS全局變量,
                //當AMS接收到廣播時會從mReceiverResolver找到所有的接收者,實現注冊的效果。
                mReceiverResolver.addFilter(bf);
            }

            // 廣播隊列中入隊所有將符合filter的粘性廣播
            if (allSticky != null) {
                ArrayList receivers = new ArrayList();
                receivers.add(bf);

                final int stickyCount = allSticky.size();
                for (int i = 0; i < stickyCount; i++) {
                    Intent intent = allSticky.get(i);
                    BroadcastQueue queue = broadcastQueueForIntent(intent);
                    BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                            null, null, -1, -1, false, null, null, OP_NONE, null, receivers,
                            null, 0, null, null, false, true, true, -1, false,
                            false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
                    queue.enqueueParallelBroadcastLocked(r);
                    queue.scheduleBroadcastsLocked();
                }
            }

            return sticky;
        }
    }

以上就是廣播注冊的過程,具體的步驟在注釋中一有所體現。

2. 廣播的發送與接收

2.1 ContextImp 調用AMS

我們知道廣播有標準廣播(無序廣播)、有序廣播和粘性廣播。下面還是以開始那個例子(標準廣播)展開介紹。與廣播的注冊類似,首先是調用了ContextWraper的sendBroadcast:

...
 @Override
    public void sendBroadcast(Intent intent) {
        mBase.sendBroadcast(intent);
    }
...

sendBroadcast調用了ContextImp的sendBroadcast方法:

...
@Override
    public void sendBroadcast(Intent intent) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try {
            intent.prepareToLeaveProcess(this);
            ActivityManager.getService().broadcastIntentWithFeature(
                    mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
                    null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,
                    false, getUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
...

上面的代碼,很明顯調用了AMS的broadcastIntentWithFeature方法:

public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle resultExtras,
            String[] requiredPermissions, int appOp, Bundle bOptions,
            boolean serialized, boolean sticky, int userId) {
        enforceNotIsolatedCaller("broadcastIntent");
        synchronized(this) {
            //驗證intent
            intent = verifyBroadcastLocked(intent);
            
            //獲取應用程序進程信息
            final ProcessRecord callerApp = getRecordForAppLocked(caller);
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();

            final long origId = Binder.clearCallingIdentity();
            try {
                return broadcastIntentLocked(callerApp,
                        callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
                        intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                        requiredPermissions, appOp, bOptions, serialized, sticky,
                        callingPid, callingUid, callingUid, callingPid, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
        }
    }

最后調用了AMS的broadcastIntentLocked方法,方法特別的長,這里這給出部分源碼:

 final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
            @Nullable String callerFeatureId, 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 realCallingUid,
            int realCallingPid, int userId, boolean allowBackgroundActivityStarts,
            @Nullable int[] broadcastWhitelist) {
            ...
            //獲取注冊的粘性廣播
            ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
            if (stickies == null) {
                stickies = new ArrayMap<>();
                mStickyBroadcasts.put(userId, stickies);
            }
            //找到符合當前Action的廣播
            ArrayList<Intent> list = stickies.get(intent.getAction());
            if (list == null) {
                list = new ArrayList<>();
                stickies.put(intent.getAction(), list);
            }
            ...
            //接收者存在
            if ((receivers != null && receivers.size() > 0)
                || resultTo != null) {
            //獲取廣播隊列,獲取BroadcastRecord信息
            BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
                    callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                    requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                    resultData, resultExtras, ordered, sticky, false, userId,
                    allowBackgroundActivityStarts, timeoutExempt);
              ...
             queue.enqueueOrderedBroadcastLocked(r);
            //執行廣播schedule
             queue.scheduleBroadcastsLocked();
              }
...
             return ActivityManager.BROADCAST_SUCCESS;
}

從上面的代碼可以看出,最后AMS調用了BroadcastQueue的scheduleBroadcastsLocked方法。

2.2 ContextImp 調用AMS調用BroadcastReceiver

接上一節的代碼繼續看,BroadcastQueue的scheduleBroadcastsLocked方法:

public void scheduleBroadcastsLocked() {
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
                + mQueueName + "]: current="
                + mBroadcastsScheduled);

        if (mBroadcastsScheduled) {
            return;
        }
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }

scheduleBroadcastsLocked方法內部,通過BroadcastHandler發送了一個消息:BROADCAST_INTENT_MSG,這肯定交由對應的handleMessage方法處理,我們再看下BroadcastHandler的handleMessage方法:

 @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case BROADCAST_INTENT_MSG: {
                    if (DEBUG_BROADCAST) Slog.v(
                            TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
                            + mQueueName + "]");
                    processNextBroadcast(true);
                } break;
                case BROADCAST_TIMEOUT_MSG: {
                    synchronized (mService) {
                        broadcastTimeoutLocked(true);
                    }
                } break;
            }
        }

上面代碼很明顯,處理BROADCAST_INTENT_MSG類型消息后,調用了processNextBroadcast方法,然后又調用了processNextBroadcastLocked方法,方法比較長,特殊處理也很多,這里只關注下標準廣播的處理:

final void processNextBroadcast(boolean fromMsg) {
        synchronized (mService) {
            processNextBroadcastLocked(fromMsg, false);
        }
    }
...
 final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
  ...   
   if (fromMsg) {//已經處理了BROADCAST_INTENT_MSG類型的消息
            mBroadcastsScheduled = false;
        }

        // 處理無序廣播,也就是標準的廣播
        while (mParallelBroadcasts.size() > 0) {
          //獲取廣播
            r = mParallelBroadcasts.remove(0);
           ...
            for (int i=0; i<N; i++) {
                Object target = r.receivers.get(i);
                if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                        "Delivering non-ordered on [" + mQueueName + "] to registered "
                        + target + ": " + r);
              //廣播發送給相應的接收者
                deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
            }
            addBroadcastToHistoryLocked(r);
            if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
                    + mQueueName + "] " + r);
        }
...
}

下面繼續看下deliverToRegisteredReceiverLocked方法:

private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
            BroadcastFilter filter, boolean ordered, int index) {

            ....//省略一堆驗證
        
        // 有序廣播的處理.
        if (ordered) {
            r.receiver = filter.receiverList.receiver.asBinder();
            r.curFilter = filter;
            filter.receiverList.curBroadcast = r;
            r.state = BroadcastRecord.CALL_IN_RECEIVE;
            if (filter.receiverList.app != null) {
                r.curApp = filter.receiverList.app;
                filter.receiverList.app.curReceivers.add(r);
                mService.updateOomAdjLocked(r.curApp, true,
                        OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
            }
        }
        try {
            if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
                    "Delivering to " + filter + " : " + r);
            if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
                //如果是有序廣播,繼續傳給下一個接收者.
                if (ordered) {
                    skipReceiverLocked(r);
                }
            } else {
                r.receiverTime = SystemClock.uptimeMillis();
                maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
                //各種檢查及驗證后最終調用的方法
                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                        new Intent(r.intent), r.resultCode, r.resultData,
                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
               
                if (r.allowBackgroundActivityStarts && !r.ordered) {
                    postActivityStartTokenRemoval(filter.receiverList.app, r);
                }
            }
            if (ordered) {
                r.state = BroadcastRecord.CALL_DONE_RECEIVE;
            }
        } catch (RemoteException e) {
            ...
        }
}

繼續看performReceiveLocked方法:

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) {//驗證應用程序進程是否已運行
                // If we have an app thread, do the call through that so it is
                // 應用程序進程存在,執行一個單向請求
                try {
                    app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                            data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
                } catch (RemoteException ex) {
                   ...
                }
            } else {
                throw new RemoteException("app.thread must not be null");
            }
        } else {
            receiver.performReceive(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
        }
    }

上面的應用程序進程app自然是ApplicationThread,繼續看其方法scheduleRegisteredReceiver:

        //這個方法是為了保證所有接收者的分發是正確有序的
        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);
            receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
                    sticky, sendingUser);
        }

上面代碼很簡潔,調用了IIntentReceiver的performReceive方法,IIntentReceiver是通過LoadedApk中的InnerReceiver實現的:

final static class InnerReceiver extends IIntentReceiver.Stub {
...
@Override
            public void performReceive(Intent intent, int resultCode, String data,
                    Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                final LoadedApk.ReceiverDispatcher rd;
                if (intent == null) {
                    Log.wtf(TAG, "Null intent received");
                    rd = null;
                } else {
                    rd = mDispatcher.get();
                }
              ...
                if (rd != null) {
                  //ReceiverDispatcher的調用
                    rd.performReceive(intent, resultCode, data, extras,
                            ordered, sticky, sendingUser);
                } else {
                ...
                }
            }
...
}

上面的IIntentReceiver通過AIDL實現進程間通信,最后調用了ReceiverDispatcher的performReceive方法:

public void performReceive(Intent intent, int resultCode, String data,
                Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
            //封裝廣播的intent信息
            final Args args = new Args(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
            if (intent == null) {
                Log.wtf(TAG, "Null intent received");
            } else {
               ...
            }
            if (intent == null || !mActivityThread.post(args.getRunnable())) {

                if (mRegistered && ordered) {
                    IActivityManager mgr = ActivityManager.getService();
                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                            "Finishing sync broadcast to " + mReceiver);
                    args.sendFinished(mgr);
                }
            }
        }

mActivityThread.post(args.getRunnable())的mActivityThread是個Handler,具體實現是H類,這里將args.getRunnable()作為參數傳入Handler的post方法,看下Args的getRunnable方法:

public final Runnable getRunnable() {
                return () -> {
                    final BroadcastReceiver receiver = mReceiver;
                    final boolean ordered = mOrdered;

                   ...

                    //獲取AMS
                    final IActivityManager mgr = ActivityManager.getService();
                    final Intent intent = mCurIntent;
                   ...
                    try {
                        //創建BroadcastReceiver
                        ClassLoader cl = mReceiver.getClass().getClassLoader();
                        intent.setExtrasClassLoader(cl);
                        intent.prepareToEnterProcess();
                        setExtrasClassLoader(cl);
                        receiver.setPendingResult(this);
                        //調用onReceive方法
                        receiver.onReceive(mContext, intent);
                    } catch (Exception e) {
                      ...
                    }
                 ...
                };
            }
        }

代碼最后創建了一個BroadcastReceiver并調用了onReceive方法。

3.總結

3.1 廣播注冊 ContextWraper到啟動AMS的過程
image.png
3.2 廣播發送 ContextWraper到啟動AMS的過程
image.png
3.3廣播發送與接收 AMS到啟動BroadcastReceiver的過程
image.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容