FrameWork源碼解析(5)-廣播處理過程源碼解析

主目錄見:Android高級進(jìn)階知識(這是總目錄索引)

這篇文章主要是接著上面兩篇文章的:
1.注冊廣播接收者的源碼分析
2.廣播發(fā)送過程源碼解析
到這里這篇,廣播也就完成了,下一篇我們會來說說ContentProvider的相關(guān)源碼,然后四大組件的源碼分析也就完成了,接著我們會來分析資源的加載,類的加載過程等,這些插件化必需的知識,最后我們會來剖析一個插件化的框架,大家敬請期待哈。

一.源碼解析

緊接著上一篇講到BroadcastQueue#processNextBroadcast()方法,我們就從這里開始出發(fā)。這個方法很長,我們一步一步來看。

1.處理并行廣播

            // First, deliver any non-serialized broadcasts right away.
           //馬上處理掉所有的并行廣播
            while (mParallelBroadcasts.size() > 0) {
                r = mParallelBroadcasts.remove(0);
                r.dispatchTime = SystemClock.uptimeMillis();
                r.dispatchClockTime = System.currentTimeMillis();
                //并行廣播的receivers中是BroadcastFilter類型對象
                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);
                    //這是發(fā)送廣播,這里的target是BroadcastFilter類型
                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
                }
                addBroadcastToHistoryLocked(r);
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
                        + mQueueName + "] " + r);
            }

之前的文章我們知道,我們的動態(tài)注冊的廣播都是放在并行隊列里面,所以程序這里會取出所有的并行隊列中的BrocastRecord類型對象,然后取出對象里面的receivers,這里是一個BroadcastFilter集合對象。然后一一發(fā)送。這里我們具體來看下deliverToRegisteredReceiverLocked()方法的具體處理流程。

2.動態(tài)注冊廣播發(fā)送

private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
            BroadcastFilter filter, boolean ordered, int index) {
        boolean skip = false;
      //這里省略了權(quán)限檢查代碼。如果設(shè)置了權(quán)限的話,這里就會檢查發(fā)送者是否有權(quán)限,有的話,就會判斷接收者是否有發(fā)送者的權(quán)限,
      //沒有的話就會skip設(shè)置為true,如果有的話會調(diào)用noteOperation判斷是否權(quán)限被禁止了
     .........
        
        //有序廣播處理邏輯,因為我們這里是無序廣播,所以不會進(jìn)入這個操作
        if (ordered) {
            //這里的filter.receiverList.receiver.asBinder()得到的就是客戶端ReceiverDispatcher中的InnerReceiver這個Binder實體
            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.curReceiver = r;
                mService.updateOomAdjLocked(r.curApp);
            }
        }
        try {
            if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
           ......
            } else {
               //我們程序會走到這里,這里就是處理廣播的地方
                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                        new Intent(r.intent), r.resultCode, r.resultData,
                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
            }
            if (ordered) {
                r.state = BroadcastRecord.CALL_DONE_RECEIVE;
            }
        } catch (RemoteException e) {
        .......      
        }
    }

如果對上面的權(quán)限判斷不是很了解,這里推薦一篇Android權(quán)限管理原理,這篇文章還是非常不錯的,講的很清楚。我們看到我們無序廣播處理的地方是performReceiveLocked()方法,里面filter.receiverList.receiver就是對應(yīng)的客戶端ReceiverDispatcher中的InnerReceiver這個Binder實體。

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) {//判斷app.thread(這里是ActivityThread的ApplicationThreadProxy)是否不為空
                try {
                  //實際就是調(diào)用ActivityThread的方法
                    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);
        }
    }

我們看到這個方法會調(diào)用到ActivityThread中間的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);
        }

這里的receiver我們在注冊廣播接收者的源碼分析這篇文章中可以知道,receiver是LoadedApk#ReceiverDispatcher#InnerReceiver對象。所以這里會調(diào)用到InnerReceiverperformReceive()方法:

            @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 (ActivityThread.DEBUG_BROADCAST) {
                    int seq = intent.getIntExtra("seq", -1);
                 }
                if (rd != null) {
                    rd.performReceive(intent, resultCode, data, extras,
                            ordered, sticky, sendingUser);
                } else {
                    IActivityManager mgr = ActivityManagerNative.getDefault();
                    try {
                        if (extras != null) {
                            extras.setAllowFds(false);
                        }
                        mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
            }

這里看到我們這個方法內(nèi)部會調(diào)用ReceiverDispatcherperformReceive()方法,所以我們繼續(xù)往下跟:

        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 (ActivityThread.DEBUG_BROADCAST) {
                    int seq = intent.getIntExtra("seq", -1);
                    Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction()
                            + " seq=" + seq + " to " + mReceiver);
                }
            }
            //這里處理消息的方式是通過Handler.post函數(shù)進(jìn)行的,post函數(shù)的參數(shù)是Runnable類型的,這個消息最終會調(diào)用這個這個參數(shù)的run成員函數(shù)來處理
            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);
                    //發(fā)送完成本次廣播處理,用來進(jìn)行下次的廣播處理
                    args.sendFinished(mgr);
                }
            }
        }

我們看到這個方法里面會調(diào)用mActivityThread即一個Handler的post方法,post方法里面的參數(shù)是一個Runnable對象,所以我們知道會調(diào)用這個Runnable對象的run方法:

   public void run() {
                final BroadcastReceiver receiver = mReceiver;
                final boolean ordered = mOrdered;
               .....
                final IActivityManager mgr = ActivityManagerNative.getDefault();
                final Intent intent = mCurIntent;
                if (intent == null) {
                    Log.wtf(TAG, "Null intent being dispatched, mDispatched=" + mDispatched);
                }

                mCurIntent = null;
                mDispatched = true;
                if (receiver == null || intent == null || mForgotten) {
                    if (mRegistered && ordered) {
                        if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                "Finishing null broadcast to " + mReceiver);
                        sendFinished(mgr);
                    }
                    return;
                }

                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveReg");
                try {
                    ClassLoader cl =  mReceiver.getClass().getClassLoader();
                    intent.setExtrasClassLoader(cl);
                    intent.prepareToEnterProcess();
                    setExtrasClassLoader(cl);
                    receiver.setPendingResult(this);
                    receiver.onReceive(mContext, intent);
                } catch (Exception e) {
                .......
                }
                
                if (receiver.getPendingResult() != null) {
                    finish();
                }
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }

這里的mReceiver就是ReceiverDispatcher對象的成員變量,它是一個BroadcastReceiver類型,這里它就是Activity注冊廣播接收器時創(chuàng)建的BroadcastReceiver實例了。有了ReceiverDispatcher就可以回調(diào)他的onReceive方法了。這樣的話,到這里動態(tài)注冊廣播的處理也就完成了。這樣的話我們繼續(xù)來看processNextBroadcast()方法。

3.處理pending廣播

            //目標(biāo)進(jìn)程如果不存在,那么會啟動進(jìn)程,因為啟動進(jìn)程是耗時的工作,則會先把廣播放在mPendingBroadcast中
            if (mPendingBroadcast != null) {
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
                        "processNextBroadcast [" + mQueueName + "]: waiting for "
                        + mPendingBroadcast.curApp);

                boolean isDead;
                synchronized (mService.mPidsSelfLocked) {
                  // 檢查這個靜態(tài)注冊的目標(biāo)廣播接收者所運(yùn)行在的應(yīng)用程序進(jìn)程是否已經(jīng)啟動起來  
                    ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
                    isDead = proc == null || proc.crashing;
                }
               //  如果這個應(yīng)用程序進(jìn)程還活著,就會繼續(xù)等待,否則就不等了
                if (!isDead) {
                    // It's still alive, so keep waiting
                    return;
                } else {
                    mPendingBroadcast.state = BroadcastRecord.IDLE;
                    mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
                    mPendingBroadcast = null;
                }
            }

            boolean looped = false;

目標(biāo)進(jìn)程如果不存在,那么會啟動進(jìn)程,因為啟動進(jìn)程是耗時的工作,則會先把廣播放在mPendingBroadcast中。

4.有序廣播處理

          do {
                //是否存在有序廣播
                if (mOrderedBroadcasts.size() == 0) {
                    // No more broadcasts pending, so all done!
                    mService.scheduleAppGcsLocked();
                    if (looped) {
                        //這是AMS的內(nèi)存回收方法
                        mService.updateOomAdjLocked();
                    }
                    return;
                }

                //這里是獲取一個有序廣播,在循環(huán)的最后會remove,也就是說這里是從mOrderedBroadcasts
                //中循環(huán)取出有序廣播
                r = mOrderedBroadcasts.get(0);
                boolean forceReceive = false;

                int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
                //這里是超時的處理機(jī)制
                if (mService.mProcessesReady && r.dispatchTime > 0) {
                    long now = SystemClock.uptimeMillis();
                    //dispatchTime為廣播發(fā)送的起始時間+mTimeoutPeriod這是由BroadcastQueue
                    //類型決定的,為10秒和60秒*2*有序廣播數(shù)量(也就是說只要整體的廣播發(fā)送事件不超過這個事件
                    //就不會報ANR)
                    if ((numReceivers > 0) &&
                            (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                        Slog.w(TAG, "Hung broadcast ["
                                + mQueueName + "] discarded after timeout failure:"
                                + " now=" + now
                                + " dispatchTime=" + r.dispatchTime
                                + " startTime=" + r.receiverTime
                                + " intent=" + r.intent
                                + " numReceivers=" + numReceivers
                                + " nextReceiver=" + r.nextReceiver
                                + " state=" + r.state);
                        //廣播超時,則廣播
                        broadcastTimeoutLocked(false); // forcibly finish this broadcast
                        forceReceive = true;
                        r.state = BroadcastRecord.IDLE;
                    }
                }

                if (r.state != BroadcastRecord.IDLE) {
                    if (DEBUG_BROADCAST) Slog.d(TAG_BROADCAST,
                            "processNextBroadcast("
                            + mQueueName + ") called when not idle (state="
                            + r.state + ")");
                    return;
                }
                //沒有廣播接收者了,或者廣播被攔截或者被強(qiáng)制結(jié)束則進(jìn)入這個處理
                if (r.receivers == null || r.nextReceiver >= numReceivers
                        || r.resultAbort || forceReceive) {
                    // No more receivers for this broadcast!  Send the final
                    // result if requested...
                    //如果沒有更多的廣播,那么久發(fā)送最后一條
                    if (r.resultTo != null) {
                        try {
                           ////處理廣播消息消息,調(diào)用到onReceive()
                            performReceiveLocked(r.callerApp, r.resultTo,
                                new Intent(r.intent), r.resultCode,
                                r.resultData, r.resultExtras, false, false, r.userId);
                            // Set this to null so that the reference
                            // (local and remote) isn't kept in the mBroadcastHistory.
                            r.resultTo = null;
                        } catch (RemoteException e) {
                        .......  
                        }
                    }

                    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Cancelling BROADCAST_TIMEOUT_MSG");
                    //如果前面發(fā)送的廣播已經(jīng)完成了,那么需要remove掉前面發(fā)送的超時消息
                    cancelBroadcastTimeoutLocked();

                    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
                            "Finished with ordered broadcast " + r);

                    // ... and on to the next...
                    addBroadcastToHistoryLocked(r);
                    if (r.intent.getComponent() == null && r.intent.getPackage() == null
                            && (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
                        // This was an implicit broadcast... let's record it for posterity.
                        mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
                                r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
                    }
                    mOrderedBroadcasts.remove(0);
                    r = null;
                    looped = true;
                    //進(jìn)入下一次循環(huán)
                    continue;
                }
            } while (r == null);

我們看到這個方法主要是判斷是否超時了,如果這些條件都沒有的話,就會返回r取到隊列中的一個有序廣播,然后就會進(jìn)入處理程序內(nèi)容。

5.動態(tài)廣播處理

      // Get the next receiver...
          //獲取下一個廣播
            int recIdx = r.nextReceiver++;

            // Keep track of when this receiver started, and make sure there
            // is a timeout message pending to kill it if need be.
            r.receiverTime = SystemClock.uptimeMillis();
            if (recIdx == 0) {//沒有下一個廣播則設(shè)置開始時間
                r.dispatchTime = r.receiverTime;
                r.dispatchClockTime = System.currentTimeMillis();
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing ordered broadcast ["
                        + mQueueName + "] " + r);
            }
            if (! mPendingBroadcastTimeoutMessage) {//如果已經(jīng)超時則關(guān)閉廣播
                long timeoutTime = r.receiverTime + mTimeoutPeriod;
                if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                        "Submitting BROADCAST_TIMEOUT_MSG ["
                        + mQueueName + "] for " + r + " at " + timeoutTime);
                setBroadcastTimeoutLocked(timeoutTime);
            }

            final BroadcastOptions brOptions = r.options;
            final Object nextReceiver = r.receivers.get(recIdx);

            if (nextReceiver instanceof BroadcastFilter) {//判斷是動態(tài)注冊還是靜態(tài)注冊廣播
                // Simple case: this is a registered receiver who gets
                // a direct call.
                BroadcastFilter filter = (BroadcastFilter)nextReceiver;
                if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                        "Delivering ordered ["
                        + mQueueName + "] to registered "
                        + filter + ": " + r);
                //如果是動態(tài)注冊廣播則調(diào)用deliverToRegisteredReceiverLocked發(fā)送
                deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
                if (r.receiver == null || !r.ordered) {//如果是無序廣播
                    // The receiver has already finished, so schedule to
                    // process the next one.
                    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["
                            + mQueueName + "]: ordered="
                            + r.ordered + " receiver=" + r.receiver);
                    r.state = BroadcastRecord.IDLE;
                  //則直接執(zhí)行下一個廣播,不需要等待前一個廣播完成
                    scheduleBroadcastsLocked();
                } else {
                    if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
                        scheduleTempWhitelistLocked(filter.owningUid,
                                brOptions.getTemporaryAppWhitelistDuration(), r);
                    }
                }
                return;
            }

我們看到這個方法里面會取出下一個廣播,然后判斷是不是動態(tài)注冊廣播,如果是的話就會直接調(diào)用deliverToRegisteredReceiverLocked方法發(fā)送,這個方法前面2.動態(tài)注冊廣播發(fā)送已經(jīng)講過了。

6.靜態(tài)廣播處理

      //因為前面的動態(tài)廣播處理都完成了,剩下的就是靜態(tài)廣播了
      ResolveInfo info =
                (ResolveInfo)nextReceiver;
            ComponentName component = new ComponentName(
                    info.activityInfo.applicationInfo.packageName,
                    info.activityInfo.name);

            boolean skip = false;
            //省略了很多skip標(biāo)志位重設(shè)的判斷
           .......
            String targetProcess = info.activityInfo.processName;
            ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
                    info.activityInfo.applicationInfo.uid, false);
            .......
            if (skip) {
                if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                        "Skipping delivery of ordered [" + mQueueName + "] "
                        + r + " for whatever reason");
                r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
                r.receiver = null;
                r.curFilter = null;
                //如果要跳過的話那么就直接執(zhí)行下一個廣播
                r.state = BroadcastRecord.IDLE;
                scheduleBroadcastsLocked();
                return;
            }
            ......
            // Is this receiver's application already running?
            if (app != null && app.thread != null) {
                try {
                    app.addPackage(info.activityInfo.packageName,
                            info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
                    //如果接收者的應(yīng)用存在且在運(yùn)行的話那么就調(diào)用這個方法處理這個靜態(tài)廣播
                    processCurBroadcastLocked(r, app);
                    return;
                } catch (RemoteException e) {
               ......  
                } catch (RuntimeException e) {
               ......
                }

                // If a dead object exception was thrown -- fall through to
                // restart the application.
            }

            // Not running -- get it started, to be executed when the app comes up.
            //接收者進(jìn)程沒有啟動那么就啟動進(jìn)程,當(dāng)然之后進(jìn)程啟動完畢還是會調(diào)用processCurBroadcastLocked來啟動
            if ((r.curApp=mService.startProcessLocked(targetProcess,
                    info.activityInfo.applicationInfo, true,
                    r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
                    "broadcast", r.curComponent,
                    (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
                            == null) {
                logBroadcastReceiverDiscardLocked(r);
                finishReceiverLocked(r, r.resultCode, r.resultData,
                        r.resultExtras, r.resultAbort, false);
                //執(zhí)行下一個廣播
                scheduleBroadcastsLocked();
                r.state = BroadcastRecord.IDLE;
                return;
            }

            mPendingBroadcast = r;
            mPendingBroadcastRecvIndex = recIdx;
        }

我們看到靜態(tài)廣播處理會調(diào)用processCurBroadcastLocked()方法進(jìn)行發(fā)送,所以我們來看看這個方法。

8.靜態(tài)廣播發(fā)送

  private final void processCurBroadcastLocked(BroadcastRecord r,
            ProcessRecord app) throws RemoteException {
        .....
        r.receiver = app.thread.asBinder();
        r.curApp = app;
        app.curReceiver = r;
        //更新進(jìn)程狀態(tài)為PROCESS_STATE_RECEIVER
        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
        //AMS更新內(nèi)存
        mService.updateLruProcessLocked(app, false, null);
        //該方法的作用是告訴OOMKiller指定進(jìn)程的優(yōu)先級,值越小越重要,該函數(shù)的返回值是boolean類型,
        //如果底層linux包含OOMKiller則返回true,否則返回false
        //也就是提升優(yōu)先級
        mService.updateOomAdjLocked();

        // Tell the application to launch this receiver.
        r.intent.setComponent(r.curComponent);

        boolean started = false;
        try {
            mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
                                      PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
          //這里同上面的2.動態(tài)廣播發(fā)送有點(diǎn)類似
            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);
              started = true;
        } finally {
            if (!started) {
                r.receiver = null;
                r.curApp = null;
                app.curReceiver = null;
            }
        }
    }

這里我們看到最終靜態(tài)廣播發(fā)送也會發(fā)送到ActivityThreadscheduleReceiver()方法中,具體流程與動態(tài)注冊廣播類似,這里就不贅述了。到這里動態(tài)廣播與靜態(tài)廣播發(fā)送處理都完成了。

總結(jié):廣播的發(fā)送和處理還是比較繁瑣的,里面的一些東西也有我理解比較淺顯的,希望大家體諒,如果大家有更好的理解可以教教我,希望大家共同進(jìn)步哈。

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

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