Android源碼解析四大組件系列(五)---廣播的注冊過程

廣播這個(gè)篇幅打算用四篇文章來寫玫恳,分別為廣播注冊、廣播處理优俘、廣播的發(fā)送京办,廣播深入細(xì)節(jié)理解,如果都寫到一篇文章會比較長帆焕,所以拆分成四篇來寫惭婿。

第一篇
Android源碼解析---廣播的注冊過程
第二篇
Android源碼解析---廣播的處理過程
第三篇
Android源碼解析---廣播的發(fā)送過程
第四篇
Android源碼解析---廣播深入細(xì)節(jié)理解

想收到廣播(Broadcast),必須先要注冊接收廣播的組件---廣播接收者(receiver)叶雹,廣播接收者的注冊分為動態(tài)注冊和靜態(tài)注冊审孽,而注冊中心就是AMS,AMS再把廣播分發(fā)到各個(gè)廣播接收者(receiver)浑娜。

image.png

一個(gè)廣播可以有多個(gè)receiver來接收它佑力,注冊的方式分為兩種,一種是靜態(tài)注冊筋遭,一種是動態(tài)注冊打颤,動態(tài)注冊廣播不是常駐型廣播,也就是說廣播跟隨Activity的生命周期漓滔,在Activity結(jié)束前编饺,需要移除廣播接收器。 靜態(tài)注冊是常駐型响驴,也就是說當(dāng)應(yīng)用程序關(guān)閉后透且,如果有信息廣播來,程序也會被系統(tǒng)調(diào)用自動運(yùn)行豁鲤。

1.1 動態(tài)廣播注冊

動態(tài)注冊是由ContextImpl的registerReceiver方法調(diào)用registerReceiverInternal來注冊的

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) {
               //為空表示默認(rèn)為主線程
               if (scheduler == null) {
                 //AMS并不是直接給廣播接收者發(fā)送廣播的秽誊,當(dāng)廣播到達(dá)應(yīng)用程序進(jìn)程的時(shí)候,
      //會被封裝成一個(gè)Message,然后push到主線程消息隊(duì)列中琳骡,然后才會給接
      //收者處理锅论,你也可以指定一個(gè)處理的Handler,將onReceive()調(diào)度在非主線程執(zhí)行楣号。
                   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 {
         //將rd最易,filter等發(fā)送給AMS
           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();
       }
   }

傳進(jìn)來的receiver不是直接發(fā)送給AMS的怒坯,首先會把receiver封裝成一個(gè)IIntentReceiver對象rd,這個(gè)rd是一個(gè)binder本地對象,具備了跨進(jìn)程通信的能力藻懒。mPackageInfo是LoadedApk,LoadedApk這個(gè)類包含了當(dāng)前加載的apk的主要的信息,其中成員變量mReceivers表就記錄了所有動態(tài)注冊的receiver剔猿。

private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
      = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();

rd的獲取有兩種,當(dāng)mPackageInfo存在時(shí)候嬉荆,就通過mPackageInfo.getReceiverDispatcher()來獲取归敬。

   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;
        //registered傳進(jìn)來的是true
           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 {
           //檢查廣播分發(fā)者的context、handler是否一致
               rd.validate(context, handler);
           }
           rd.mForgotten = false;
           return rd.getIIntentReceiver();
       }
   }

這個(gè)方法內(nèi)部維護(hù)了一張表 ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null员寇,每一個(gè)廣播接收者對應(yīng)一個(gè)ReceiverDispatcher(廣播分發(fā)者)弄慰,并且把最后把這個(gè)map以Context為key存在mReceivers中,而這個(gè)Context跟廣播的發(fā)送方有關(guān)系第美,如果是在Activity中發(fā)送的蝶锋,這個(gè)Context就指向與這個(gè)Activity,如果是在Service中發(fā)送的,這個(gè)Context就指向了這個(gè)Service什往。那么這個(gè)廣播分發(fā)者有什么用呢扳缕?用map來存儲,表示ReceiverDispatcher跟BroadcastReceiver是一一對應(yīng)的别威,每個(gè)廣播接收者對應(yīng)一個(gè)廣播分發(fā)者躯舔, 當(dāng)AMS向app發(fā)送廣播時(shí)會調(diào)用到app進(jìn)程的廣播分發(fā)者,然后再將廣播以message形式post到app的主線程省古,來執(zhí)行onReceive()方法粥庄。

假設(shè)我的app只有兩個(gè)Activity(HomeActivity和DetailActivity),這個(gè)app被打包成xxx.apk豺妓,那么在內(nèi)存中惜互,這個(gè)xxx.apk由LoadedApk來描述,如果HomeActivity和DetailActivity都注冊了廣播琳拭,那么LoadedApk內(nèi)部維持的mReceivers的長度就為2⊙刀眩現(xiàn)在梳理一下上面的代碼,當(dāng)一個(gè)BroadcastReceiver要注冊的時(shí)候白嘁,會優(yōu)先使用Context查看一下坑鱼,這個(gè)組件有沒有注冊過廣播,如果有就取出來絮缅,類型是一個(gè)ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> 的map鲁沥。如果沒有,就把map創(chuàng)建出來耕魄,并且存到mReceivers中黍析。有了map之后,需要把ReceiverDispatcher(廣播分發(fā)者)存到map里面去屎开。第一次注冊時(shí)候阐枣,肯定走的是if(rd==null)的代碼塊里面马靠,這樣rd對象被創(chuàng)建出來。現(xiàn)在分析一下ReceiverDispatcher這個(gè)類蔼两。

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

       .......

       final IIntentReceiver.Stub mIIntentReceiver;
       final BroadcastReceiver mReceiver;
       final Context mContext;
       final Handler mActivityThread;
       .......
       ReceiverDispatcher(BroadcastReceiver receiver, Context context,
               Handler activityThread, Instrumentation instrumentation,
               boolean registered) {
           if (activityThread == null) {
               throw new NullPointerException("Handler must not be null");
           }
           mIIntentReceiver = new InnerReceiver(this, !registered);
           //廣播接收者
           mReceiver = receiver;
           //表示哪個(gè)發(fā)送的廣播
           mContext = context;
           //主線程
           mActivityThread = activityThread;
            .......
       }
       .......
       IIntentReceiver getIIntentReceiver() {
           return mIIntentReceiver;
       }
      .......
   }

在內(nèi)部會創(chuàng)建InnerReceiver甩鳄,InnerReceiver是ReceiverDispatcher的內(nèi)部類,是一個(gè)實(shí)現(xiàn)Binder的本地對象额划,前面也說過了妙啃,最終是將一個(gè)InnerReceiver對象注冊到了AMS中。

OK,繞了這么一大圈子俊戳,其實(shí)就是為了封裝一個(gè)InnerReceiver用于和AMS通信揖赴,我也不知道谷歌這幫程序員怎么想的,有點(diǎn)麻煩抑胎。忽略跨進(jìn)程的代碼燥滑,現(xiàn)在由用戶進(jìn)程走到SystemServer進(jìn)程了,即走到AMS的registerReceiver方法阿逃。

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
           IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
       enforceNotIsolatedCaller("registerReceiver");
       ArrayList<Intent> stickyIntents = null;
       ProcessRecord callerApp = null;
       int callingUid;
       int callingPid;
       synchronized(this) {
           if (caller != null) {
             //由caller獲取當(dāng)前進(jìn)程對象
               callerApp = getRecordForAppLocked(caller);
               //進(jìn)程還沒創(chuàng)建铭拧,直接拋出異常
               if (callerApp == null) {
                   throw new SecurityException(
                           "Unable to find app for caller " + caller
                           + " (pid=" + Binder.getCallingPid()
                           + ") when registering receiver " + receiver);
               }
               if (callerApp.info.uid != Process.SYSTEM_UID &&
                       !callerApp.pkgList.containsKey(callerPackage) &&
                       !"android".equals(callerPackage)) {
                   throw new SecurityException("Given caller package " + callerPackage
                           + " is not running in process " + callerApp);
               }
               callingUid = callerApp.info.uid;
               callingPid = callerApp.pid;
           } else {
               callerPackage = null;
               callingUid = Binder.getCallingUid();
               callingPid = Binder.getCallingPid();
           }

           userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                   ALLOW_FULL_ONLY, "registerReceiver", callerPackage);

          //獲取IntentFilter中的action
           Iterator<String> actions = filter.actionsIterator();
           if (actions == null) {
               ArrayList<String> noAction = new ArrayList<String>(1);
               noAction.add(null);
               actions = noAction.iterator();
           }

           //從actions中,先把粘性廣播帥選出來恃锉,放進(jìn)stickyIntents中
           int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
           while (actions.hasNext()) {
               String action = actions.next();
               for (int id : userIds) {
               //從mStickyBroadcasts中查看用戶的sticky Intent,mStickyBroadcasts存了系統(tǒng)所有的粘性廣播
                   ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
                   if (stickies != null) {
                       ArrayList<Intent> intents = stickies.get(action);
                       if (intents != null) {
                           if (stickyIntents == null) {
                               stickyIntents = new ArrayList<Intent>();
                           }
                           stickyIntents.addAll(intents);
                       }
                   }
               }
           }
       }

       ArrayList<Intent> allSticky = null;
       if (stickyIntents != null) {
           final ContentResolver resolver = mContext.getContentResolver();
           // Look for any matching sticky broadcasts...
           for (int i = 0, N = stickyIntents.size(); i < N; i++) {
               Intent intent = stickyIntents.get(i);
               // If intent has scheme "content", it will need to acccess
               // provider that needs to lock mProviderMap in ActivityThread
               // and also it may need to wait application response, so we
               // cannot lock ActivityManagerService here.
               if (filter.match(resolver, intent, true, TAG) >= 0) {
                   if (allSticky == null) {
                       allSticky = new ArrayList<Intent>();
                   }
                   allSticky.add(intent);
               }
           }
       }

       // The first sticky in the list is returned directly back to the client.
       Intent sticky = allSticky != null ? allSticky.get(0) : null;
       if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);
       //如果receiver為空搀菩,就直接返回了
       if (receiver == null) {
           return sticky;
       }

       synchronized (this) {
           if (callerApp != null && (callerApp.thread == null
                   || callerApp.thread.asBinder() != caller.asBinder())) {
               // 進(jìn)程不存在(死亡了),也是不能注冊成功的
               return null;
           }
          //mRegisteredReceivers表存了所有動態(tài)注冊的廣播接收者破托,
     //由receiver作為key肪跋,獲取到ReceiverList,為什么是ReceiverList土砂,
    //而不是一個(gè)Receiver呢州既,因?yàn)橐粋€(gè)廣播可能會有多個(gè)接收者,
     //最好整成一個(gè)隊(duì)列或者鏈表的形式瘟芝,而ReceiverList繼承ArrayList易桃,滿足這個(gè)需求。
     //每個(gè)ReceiverList都對應(yīng)著Client端的一個(gè)ReceiverDispatcher锌俱。
           ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
           if (rl == null) {
               rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                       userId, receiver);
               if (rl.app != null) {
                   //把廣播接收者列表加到這個(gè)進(jìn)程對象的receivers中
                   rl.app.receivers.add(rl);
               } else {
                   try {
                       //進(jìn)程不存在晤郑,注冊死亡通知
                       receiver.asBinder().linkToDeath(rl, 0);
                   } catch (RemoteException e) {
                       return sticky;
                   }
                   rl.linkedToDeath = true;
               }
               //新創(chuàng)建的接收者隊(duì)列,添加到已注冊廣播隊(duì)列贸宏。
               mRegisteredReceivers.put(receiver.asBinder(), rl);
           } else if (rl.uid != callingUid) {
               throw new IllegalArgumentException(
                       "Receiver requested to register for uid " + callingUid
                       + " was previously registered for uid " + rl.uid);
           } else if (rl.pid != callingPid) {
               throw new IllegalArgumentException(
                       "Receiver requested to register for pid " + callingPid
                       + " was previously registered for pid " + rl.pid);
           } else if (rl.userId != userId) {
               throw new IllegalArgumentException(
                       "Receiver requested to register for user " + userId
                       + " was previously registered for user " + rl.userId);
           }
         //在AMS內(nèi)部造寝,廣播接收者實(shí)際上是BroadcastFilter來描述的,
    //由filter等參數(shù)創(chuàng)建BroadcastFilter對象吭练,并添加到接收者隊(duì)列,
    //注意只有registerReceiver()過程才會創(chuàng)建BroadcastFilter,也就是該對
    //象用于動態(tài)注冊的廣播Receiver;诫龙,靜態(tài)的接收者對象不是BroadcastFilter。
           BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                   permission, callingUid, userId);
           rl.add(bf);
           if (!bf.debugCheck()) {
               Slog.w(TAG, "==> For Dynamic broadcast");
           }
           mReceiverResolver.addFilter(bf);

           //如果是粘性廣播鲫咽,創(chuàng)建BroadcastRecord签赃,并添加到
   //BroadcastQueue的并行廣播隊(duì)列(mParallelBroadcasts)谷异,
    //注冊后調(diào)用AMS來盡快處理該廣播。
           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, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
                           null, 0, null, null, false, true, true, -1);
                   queue.enqueueParallelBroadcastLocked(r);
                   queue.scheduleBroadcastsLocked();
               }
           }

         //返回值是一個(gè)Intent
           return sticky;
       }
   }

總結(jié)一下:動態(tài)注冊是調(diào)用registerReceiver來注冊的锦聊,大致流程如下:

在Android系統(tǒng)中歹嘹,系統(tǒng)每加載一個(gè)apk,就會有一個(gè)LoadedApk對象孔庭。而每個(gè)LoadedApk對象里會有一張名字為mReceivers的HashMap尺上,用來記錄每個(gè)apk里面動態(tài)注冊了那些廣播接收者。mReceivers的類型是ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>>圆到,泛型中的Context代表是誰注冊的怎抛,后面的值也是個(gè)map ,ArrayMap<BroadcastReceiver, ReceiverDispatcher>表示BroadcastReceiver與ReceiverDispatcher一一對應(yīng)芽淡,一個(gè)BroadcastReceiver對應(yīng)一個(gè)ReceiverDispatcher马绝,通過這個(gè)定義可以知道,不管一個(gè)Activity注冊了多少BroadcastReceiver吐绵,ReceiverDispatcher只有一個(gè)迹淌。ReceiverDispatcher內(nèi)部有一個(gè)InnerReceiver的Binder對象河绽,最終是把這個(gè)InnerReceiver發(fā)送給了AMS,AMS內(nèi)部也維護(hù)了一張表mRegisteredReceivers己单,用來記錄所有動態(tài)注冊的接收者,首先會根據(jù)傳進(jìn)來的InnerReceiver對象取出來一個(gè)ReceiverList耙饰,ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder())纹笼,所以實(shí)質(zhì)上每個(gè)ReceiverList都對應(yīng)著Client端的一個(gè)ReceiverDispatcher。最后把創(chuàng)建的對象BroadcastFilter(AMS內(nèi)部廣播接收者用BroadcastFilter來表示)加到ReceiverList接收者隊(duì)列中苟跪,注冊完畢廷痘!

1.2 靜態(tài)廣播注冊

靜態(tài)注冊就是在manifest中注冊。

<receiver android:name=".MyReceiver">
   <intent-filter>
       <action android:name="android.intent.action.MY_BROADCAST"/>
       <category android:name="android.intent.category.DEFAULT" />
   </intent-filter>
</receiver>

它們的信息會在系統(tǒng)啟動時(shí)件已,由PackageManagerService(PMS)解析(在該類的構(gòu)造方法中會對各個(gè)應(yīng)用安裝目錄的apk文件進(jìn)行掃描解析)并記錄下來笋额。

if (tagName.equals("activity")) {
               Activity a = parseActivity(owner, res, parser, flags, outError, false,
                       owner.baseHardwareAccelerated);
               if (a == null) {
                   mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                   return false;
               }
               owner.activities.add(a);
           } else if (tagName.equals("receiver")) {
               Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
               if (a == null) {
                   mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                   return false;
               }
               owner.receivers.add(a);
           } else if (tagName.equals("service")) {
               Service s = parseService(owner, res, parser, flags, outError);
               if (s == null) {
                   mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                   return false;
               }
               owner.services.add(s);
           } else if (tagName.equals("provider")) {
               Provider p = parseProvider(owner, res, parser, flags, outError);
               if (p == null) {
                   mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                   return false;
               }
               owner.providers.add(p);
           }

經(jīng)過上面的解析receiver就被保存到了owner.receivers中去了。然后AM會調(diào)用PMS的接口來查詢“和intent匹配的組件”時(shí)篷扩,PMS內(nèi)部就會去查詢當(dāng)初記錄下來的數(shù)據(jù)兄猩,并把結(jié)果返回AMS。

 List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
                       .queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
@Override
   public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
           String resolvedType, int flags, int userId) {
       return new ParceledListSlice<>(
               queryIntentReceiversInternal(intent, resolvedType, flags, userId));
   }

   private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
           String resolvedType, int flags, int userId) {
       if (!sUserManager.exists(userId)) return Collections.emptyList();
       flags = updateFlagsForResolve(flags, userId, intent);
       ComponentName comp = intent.getComponent();
       if (comp == null) {
           if (intent.getSelector() != null) {
               intent = intent.getSelector();
               comp = intent.getComponent();
           }
       }
       if (comp != null) {
           List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
           ActivityInfo ai = getReceiverInfo(comp, flags, userId);
           if (ai != null) {
               ResolveInfo ri = new ResolveInfo();
               ri.activityInfo = ai;
               list.add(ri);
           }
           return list;
       }

       // reader
       synchronized (mPackages) {
           String pkgName = intent.getPackage();
           if (pkgName == null) {
               return mReceivers.queryIntent(intent, resolvedType, flags, userId);
           }
           final PackageParser.Package pkg = mPackages.get(pkgName);
           if (pkg != null) {
               return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers,
                       userId);
           }
           return Collections.emptyList();
       }
   }

因?yàn)樯婕癙MS鉴未,這段邏輯想寫清楚篇幅會比較大枢冤,所以,不深入討論铜秆,以上關(guān)于廣播的動態(tài)注冊和靜態(tài)注冊就介紹完了淹真。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市连茧,隨后出現(xiàn)的幾起案子核蘸,更是在濱河造成了極大的恐慌巍糯,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件客扎,死亡現(xiàn)場離奇詭異鳞贷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)虐唠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門搀愧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人疆偿,你說我怎么就攤上這事咱筛。” “怎么了杆故?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵迅箩,是天一觀的道長。 經(jīng)常有香客問我处铛,道長饲趋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任撤蟆,我火速辦了婚禮奕塑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘家肯。我一直安慰自己龄砰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布讨衣。 她就那樣靜靜地躺著换棚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪反镇。 梳的紋絲不亂的頭發(fā)上固蚤,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機(jī)與錄音歹茶,去河邊找鬼夕玩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛辆亏,可吹牛的內(nèi)容都是我干的风秤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼扮叨,長吁一口氣:“原來是場噩夢啊……” “哼缤弦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起彻磁,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤碍沐,失蹤者是張志新(化名)和其女友劉穎狸捅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體累提,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡尘喝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了斋陪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朽褪。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖无虚,靈堂內(nèi)的尸體忽然破棺而出缔赠,到底是詐尸還是另有隱情,我是刑警寧澤友题,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布嗤堰,位于F島的核電站,受9級特大地震影響度宦,放射性物質(zhì)發(fā)生泄漏踢匣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一戈抄、第九天 我趴在偏房一處隱蔽的房頂上張望离唬。 院中可真熱鬧,春花似錦呛凶、人聲如沸男娄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至建瘫,卻和暖如春崭捍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背啰脚。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工殷蛇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人橄浓。 一個(gè)月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓粒梦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親荸实。 傳聞我的和親對象是個(gè)殘疾皇子匀们,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355

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