Android四大組件——BroadCastReceiver的工作過程

BroadCastReceiver的工作過程分為包含兩方面的內(nèi)容:

  1. 注冊
  2. 發(fā)送和接收

使用BroadcastReceiver很簡單,只需要繼承BroadcastReceiver并重寫它的onReceive方法即可咬崔。

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //TODO something
    }
}

為了使BroadcastReceiver工作奢浑,就需要對它進(jìn)程注冊法焰。注冊BroadCastReceiver分為在AndroidManifest.xml靜態(tài)注冊和代碼中動態(tài)注冊氧卧。

靜態(tài)注冊示例:

<receiver android:name=".MyReceiver">
    <intent-filter>
        <action android:name="com.sososeen.09.demo.my.receiver"/>
    </intent-filter>
</receiver>

動態(tài)注冊也很簡單:

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.sososeen.09.demo.my.receiver");
registerReceiver(new MyReceiver(), intentFilter);

不過動態(tài)注冊別忘了反注冊亚亲,否則會造成內(nèi)存泄漏救拉。

發(fā)送廣播示例如下难审,通過給Intent設(shè)置action,對應(yīng)的廣播接收者的onReveive就會被回調(diào)了亿絮。

Intent intent = new Intent();
intent.setAction("com.sososeen.09.demo.my.receiver");
sendBroadcast(intent);

注冊廣播和發(fā)送廣播中間過程是怎么進(jìn)行的告喊,我們今天就來分析一下麸拄。由于靜態(tài)注冊牽涉到在應(yīng)用安裝時(shí)有系統(tǒng)自動完成注冊,確切的說是通過PackageManagerService完成注冊黔姜,相對比較復(fù)雜拢切。今天先來分析一下動態(tài)注冊的BroadCastReceiver的工作過程。

在本文的分析中秆吵,ActivityManagerService簡稱AMS淮椰。

動態(tài)注冊BroadCastReceiver

注冊廣播接收者是通過ContextWrapper來進(jìn)行的,但是類似啟動Service纳寂,真正干事的還是ContextImpl主穗,我們直接來看它的方法。2個參數(shù)的registerReceiver方法毙芜,會調(diào)用4個參數(shù)的registerReceiver方法忽媒,然后會調(diào)用registerReceiverInternal方法。

# android.app.ContextImpl
@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());
}

在registerReceiverInternal方法中主要干了兩件事:

  1. mPackageInfo指的是LoadedApk對象腋粥,通過調(diào)用LoadedApk的getReceiverDispatcher方法把BroadcastReceiver包裝為ReceiverDispatcher.InnerReceiver對象晦雨,這是因?yàn)锽roadcastReceiver是不具有跨進(jìn)程通信能力的,想要最終調(diào)用到BroadcastReceiver的onReceive方法灯抛,必須有一個Binder對象用來進(jìn)行IPC金赦,在這里就是ReceiverDispatcher.InnerReceiver對象。InnerReceiver對象持有ReceiverDispatcher對象的引用对嚼。當(dāng)AMS端調(diào)用InnerReceiver的接收廣播的方法夹抗,InnerReceiver對象會找到對應(yīng)的ReceiverDispatcher進(jìn)而找到BroadcastReceiver對象并調(diào)用其onReceive方法。這個過程我們后面再分析纵竖。這種方式與bind方式啟動Service的中包裝ServiceConnection是類似的漠烧,有興趣的可以看一下Android四大組件——Service的工作過程分析

  2. ActivityManagerNative.getDefault()獲取的是AMS在本地的代理靡砌,該代理通過IPC過程調(diào)用AMS的registerReceiver方法已脓。

# android.app.ContextImpl
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
                                        IntentFilter filter, String broadcastPermission,
                                        Handler scheduler, Context context) {
    IIntentReceiver rd = null;
    if (receiver != null) {
        if (mPackageInfo != null && context != null) {
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();
            }
            // 1 創(chuàng)建ReceiverDispatcher.InnerReceiver對象
            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 {
        // 2 IPC過程調(diào)用AMS的registerReceiver方法
        final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
                mMainThread.getApplicationThread(), mBasePackageName,
                rd, filter, broadcastPermission, userId);
        if (intent != null) {
            intent.setExtrasClassLoader(getClassLoader());
            intent.prepareToEnterProcess();
        }
        return intent;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

我們先來看一下LoadedApk的getReceiverDispatcher方法,就是創(chuàng)建一個ReceiverDispatcher對象并將其保存起來通殃,在ReceiverDispatcher的構(gòu)造方法中創(chuàng)建InnerReceiver對象度液,ReceiverDispatcher對象中保存了BroadcastReceiver和InnerReceiver對象。


public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
                                             Context context, Handler handler,
                                             Instrumentation instrumentation, boolean registered) {
    synchronized (mReceivers) {
        LoadedApk.ReceiverDispatcher rd = null;
        ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
        if (registered) {
            map = mReceivers.get(context);
            if (map != null) {
                rd = map.get(r);
            }
        }
        if (rd == null) {
            rd = new ReceiverDispatcher(r, context, handler,
                    instrumentation, registered);
            if (registered) {
                if (map == null) {
                    map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
                    mReceivers.put(context, map);
                }
                map.put(r, rd);
            }
        } else {
            rd.validate(context, handler);
        }
        rd.mForgotten = false;
        return rd.getIIntentReceiver();
    }
}

我們來看一下AMS的registerReceiver方法画舌,該方法很長堕担,我們選取一些關(guān)鍵的地方。

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
                               IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
    enforceNotIsolatedCaller("registerReceiver");
    ArrayList<Intent> stickyIntents = null;
    ProcessRecord callerApp = null;
      ...
        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);
            } else {
                try {
                    receiver.asBinder().linkToDeath(rl, 0);
                } catch (RemoteException e) {
                    return sticky;
                }
                rl.linkedToDeath = true;
            }
            mRegisteredReceivers.put(receiver.asBinder(), rl);
        } 
        ...
        BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                permission, callingUid, userId);
        rl.add(bf);
       ...
        mReceiverResolver.addFilter(bf);
 ...
    }
}

在上面的方法中
mRegisteredReceivers 是一個HashMap對象曲聂,用于追蹤客戶端BroadcastReceiver對應(yīng)的ReceiverDispatcher.InnerReceiver對象霹购。ReceiverList是一個ArrayList集合,用于存放BroadcastFilter朋腋,BroadcastFilter用于包裝IntentFilter齐疙。我們需要知道一點(diǎn)膜楷,同一個BroadcastReceiver是可以對應(yīng)多個IntentFilter的,只要匹配一個就能夠接收消息贞奋。

final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();

說明: BroadcastFilter中封裝了ReceiverList對象赌厅,而ReceiverList對象中包含了客戶端BroadcastReceiver對應(yīng)的IIntentReceiver對象(實(shí)際上就是ReceiverDispatcher.InnerReceiver對象),將來在發(fā)送廣播的階段忆矛,通過Intent找到所有匹配到的動態(tài)注冊的BroadcastFilter集合察蹲。

到此,BroadcastReceiver的動態(tài)注冊過程就完畢了催训。

廣播的發(fā)送和接收過程

廣播的發(fā)送分為普通廣播洽议、有序廣播和粘性廣播。有序廣播是按照廣播接收者的優(yōu)先級漫拭,從高優(yōu)先級到低優(yōu)先級依次接收亚兄,而且在高優(yōu)先級的廣播接收者中是可以打斷廣播,造成低優(yōu)先級的廣播接收不到采驻。而粘性廣播表示想要當(dāng)發(fā)送廣播的時(shí)候审胚,BroadcastReceiver還沒有創(chuàng)建,當(dāng)BroadcastReceiver創(chuàng)建后礼旅,它的onReveive方法會被立即調(diào)用膳叨。

在這里我們分析一下普通廣播的發(fā)送和接收流程。

調(diào)用Context的方法發(fā)送廣播痘系,實(shí)際上還是會調(diào)用ContextImpl的方法菲嘴,來一下ContextImpl的sendBroadcast方法

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

從這個過程中看,通過一個IPC過程調(diào)用到AMS的broadcastIntent方法汰翠,在該方法又調(diào)用了AMS的broadcastIntentLocked方法龄坪。

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) {
    enforceNotIsolatedCaller("broadcastIntent");
    synchronized(this) {
        intent = verifyBroadcastLocked(intent);

        final ProcessRecord callerApp = getRecordForAppLocked(caller);
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        int res = broadcastIntentLocked(callerApp,
                callerApp != null ? callerApp.info.packageName : null,
                intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                requiredPermissions, appOp, bOptions, serialized, sticky,
                callingPid, callingUid, userId);
        Binder.restoreCallingIdentity(origId);
        return res;
    }
}

broadcastIntentLocked方法非常長,我們盡量選取一些關(guān)鍵的地方來分析复唤,在方法的開始會有如下代碼:

intent = new Intent(intent);

// By default broadcasts do not go to stopped apps.
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

表示會重新創(chuàng)建一個Intent對象用于包裹傳遞過來的的Intent對象健田,并且為Intent添加了一個flag,Intent.FLAG_EXCLUDE_STOPPED_PACKAGES佛纫,表示默認(rèn)情況下廣播不會發(fā)送給已經(jīng)停止運(yùn)行的App妓局,這也是為了方式一些App想要利用廣播來啟動進(jìn)程。

從Android3.1開始呈宇,添加了兩個標(biāo)記

// 表示不包含已經(jīng)停止的應(yīng)用跟磨,如果廣播設(shè)置這個標(biāo)記,就不會發(fā)送給已經(jīng)停止的應(yīng)用
public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 0x00000010;
 
// 表示包含已經(jīng)停止的應(yīng)用攒盈,如果廣播設(shè)置這個標(biāo)記,就會發(fā)送給已經(jīng)停止的應(yīng)用
public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020;

從Android 3.1開始哎榴,默認(rèn)情況下都是添加一個FLAG_EXCLUDE_STOPPED_PACKAGES標(biāo)記型豁,表示不會發(fā)送給停止的應(yīng)用僵蛛,如果確實(shí)需要的話,需要給廣播顯式的設(shè)置FLAG_INCLUDE_STOPPED_PACKAGES標(biāo)記迎变。

在broadcastIntentLocked內(nèi)部充尉,會根據(jù)IntentFilter查找出匹配的BroadcastReceiver,經(jīng)過一系列條件的篩選和過濾衣形,將滿足條件的接收者放在BroadcastQueue中驼侠,接下來BroadcastQueue就會將廣播發(fā)送出去。注意在這個過程中會先把找出來的動態(tài)注冊的廣播發(fā)出去谆吴,然后再把靜態(tài)注冊的廣播發(fā)出去倒源。因此,動態(tài)注冊的廣播優(yōu)先級是高于靜態(tài)注冊的廣播的句狼。而且笋熬,如果BroadcastReceiver同時(shí)注冊了靜態(tài)廣播和動態(tài)廣播,其onReceive方法會被調(diào)用兩次腻菇。

if ((receivers != null && receivers.size() > 0)
        || resultTo != null) {
    BroadcastQueue queue = broadcastQueueForIntent(intent);
    BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
            callerPackage, callingPid, callingUid, resolvedType,
            requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
            resultData, resultExtras, ordered, sticky, false, userId);

    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
            + ": prev had " + queue.mOrderedBroadcasts.size());
    if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
            "Enqueueing broadcast " + r.intent.getAction());

    boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
    if (!replaced) {
        queue.enqueueOrderedBroadcastLocked(r);
        queue.scheduleBroadcastsLocked();
    }

BroadcastQueue發(fā)送廣播的實(shí)現(xiàn)如下胳螟,通過Handler發(fā)送一個BROADCAST_INTENT_MSG類型的消息,Handler收到消息之后會調(diào)用BroadcastQueue的processNextBroadcast方法筹吐。

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;
}

BroadcastQueue的processNextBroadcast方法很長糖耸,我們選取發(fā)送普通廣播的部分來看,可以看到無序廣播存放在mParallelBroadcasts集合中丘薛,通過遍歷這個集合來發(fā)送廣播給BroadcastReceiver嘉竟。具體的發(fā)送過程是deliverToRegisteredReceiverLocked方法。

final void processNextBroadcast(boolean fromMsg) {
       ...
        // First, deliver any non-serialized broadcasts right away.
        while (mParallelBroadcasts.size() > 0) {
            r = mParallelBroadcasts.remove(0);
            r.dispatchTime = SystemClock.uptimeMillis();
            r.dispatchClockTime = System.currentTimeMillis();
            final int N = r.receivers.size();
            if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
                    + mQueueName + "] " + r);
            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);
        }

再來看一下BroadcastQueue的deliverToRegisteredReceiverLocked方法榔袋,該方法負(fù)責(zé)將一個特定的廣播發(fā)送給特定的接收者周拐,具體的發(fā)送過程是調(diào)用了performReceiveLocked方法。

performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                        new Intent(r.intent), r.resultCode, r.resultData,
                        r.resultExtras, r.ordered, r.initialSticky, r.userId);

來看一下performReceiveLocked方法凰兑,app.thread指的就是ApplicationThread在本地的代理對象妥粟,app.thread != null 條件滿足,會通過IPC過程調(diào)用ApplicationThread的scheduleRegisteredReceiver方法吏够。

# com.android.server.am.BroadcastQueue

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
            // correctly ordered with other one-way calls.
            try {
                app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                        data, extras, ordered, sticky, sendingUser, app.repProcState);
              ...
            } catch (RemoteException ex) {
              ...
            }
        } else {
            // Application has died. Receiver doesn't exist.
            throw new RemoteException("app.thread must not be null");
        }
    } else {
        receiver.performReceive(intent, resultCode, data, extras, ordered,
                sticky, sendingUser);
    }
}

在ApplicationThread的scheduleRegisteredReceiver方法中調(diào)用了receiver的performReceive方法勾给,而這個receiver我們知道,實(shí)際上就是ReceiverDispatcher.InnerReceiver對象锅知。

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);
}

來看一下ReceiverDispatcher.InnerReceiver對象的performReceive方法播急,在該方法中首先獲取InnerReceiver持有的ReceiverDispatcher對象,如果ReceiverDispatcher對象不為空的話就會調(diào)用它的performReceive方法售睹。

# android.app.LoadedApk.ReceiverDispatcher.InnerReceiver
@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) {
        rd.performReceive(intent, resultCode, data, extras,
                ordered, sticky, sendingUser);
    } else {
  ...
    }
}

在ReceiverDispatcher的performReceive方法中桩警,會封裝一個Args對象,Args是ReceiverDispatcher的非靜態(tài)內(nèi)部類昌妹,因此持有ReceiverDispatcher的引用捶枢,可以訪問ReceiverDispatcher的參數(shù)和方法握截,并且它實(shí)現(xiàn)了Runnable接口。mActivityThread是一個Handler烂叔,通過post方法谨胞,把調(diào)用切換到主線程中來,Args的run方法會被執(zhí)行蒜鸡。

# android.app.LoadedApk.ReceiverDispatcher
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);
    if (intent == null) {
        Log.wtf(TAG, "Null intent received");
    } else {
      ...
    }
    if (intent == null || !mActivityThread.post(args)) {
        if (mRegistered && ordered) {
            IActivityManager mgr = ActivityManagerNative.getDefault();
            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                    "Finishing sync broadcast to " + mReceiver);
            args.sendFinished(mgr);
        }
    }
}

Args的run方法如下胯努,最后會調(diào)用BroadcastReceiver的onReceive方法。

# android.app.LoadedApk.ReceiverDispatcher.Args
public void run() {
    final BroadcastReceiver receiver = mReceiver;
...
    ClassLoader cl =  mReceiver.getClass().getClassLoader();
    intent.setExtrasClassLoader(cl);
    intent.prepareToEnterProcess();
    setExtrasClassLoader(cl);
    receiver.setPendingResult(this);
    receiver.onReceive(mContext, intent);
...
}

到此逢防,廣播的發(fā)送和接收過程也分析完畢了叶沛。

總結(jié)

  1. 注冊廣播的時(shí)候是把BroadcastReceiver封裝到ReceiverDispatcher中,并且把與此對應(yīng)的ReceiverDispatcher.InnerReceiver作為一個Binder對象傳遞到AMS端胞四,AMS端會通過一個Map集合來存放ReceiverDispatcher.InnerReceiver對象和對應(yīng)的IntentFilter包裝類恬汁。

  2. 發(fā)送廣播的時(shí)候,通過一個IPC過程調(diào)用到AMS端辜伟,AMS端會找到匹配的廣播接收者并添加到BroadcastQueue中氓侧,在BroadcastQueue中進(jìn)程處理之后通過一個IPC過程調(diào)用到ApplicationThread的scheduleRegisteredReceiver方法,然后會調(diào)用ReceiverDispatcher.InnerReceiver的方法导狡,再經(jīng)過ReceiverDispatcher约巷、以及它的內(nèi)部類Args,最終調(diào)用到BroadcastReceiver的onReveive方法旱捧。

  3. 動態(tài)注冊的廣播優(yōu)先級是高于靜態(tài)注冊的廣播的独郎。而且,如果BroadcastReceiver同時(shí)注冊了靜態(tài)廣播和動態(tài)廣播枚赡,其onReceive方法會被調(diào)用兩次氓癌。

  4. 從過程中也可以看到,AMS端如果想要與客戶端打交道的話都是通過ApplicationThread贫橙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贪婉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子卢肃,更是在濱河造成了極大的恐慌疲迂,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件莫湘,死亡現(xiàn)場離奇詭異尤蒿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)幅垮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門腰池,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事巩螃⊙菰酰” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵避乏,是天一觀的道長。 經(jīng)常有香客問我甘桑,道長拍皮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任跑杭,我火速辦了婚禮铆帽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘德谅。我一直安慰自己爹橱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布窄做。 她就那樣靜靜地躺著愧驱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪椭盏。 梳的紋絲不亂的頭發(fā)上组砚,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天,我揣著相機(jī)與錄音掏颊,去河邊找鬼糟红。 笑死,一個胖子當(dāng)著我的面吹牛乌叶,可吹牛的內(nèi)容都是我干的盆偿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼准浴,長吁一口氣:“原來是場噩夢啊……” “哼事扭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起兄裂,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤句旱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后晰奖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谈撒,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年匾南,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了啃匿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖溯乒,靈堂內(nèi)的尸體忽然破棺而出夹厌,到底是詐尸還是另有隱情,我是刑警寧澤裆悄,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布矛纹,位于F島的核電站,受9級特大地震影響光稼,放射性物質(zhì)發(fā)生泄漏或南。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一艾君、第九天 我趴在偏房一處隱蔽的房頂上張望采够。 院中可真熱鬧,春花似錦冰垄、人聲如沸蹬癌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逝薪。三九已至,卻和暖如春写烤,著一層夾襖步出監(jiān)牢的瞬間翼闽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工洲炊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留感局,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓暂衡,卻偏偏與公主長得像询微,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子狂巢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355

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