[搞定開(kāi)源05] EventBus3.1.1原理

Android開(kāi)源項(xiàng)目原理系列
[搞定開(kāi)源01] okhttp 3.10原理
[搞定開(kāi)源02] okio 1.14原理
[搞定開(kāi)源03] retrofit 2.4.0與設(shè)計(jì)模式
[搞定開(kāi)源04] 手動(dòng)實(shí)現(xiàn)RxJava2線程切換

EventBus是Android的發(fā)布訂閱開(kāi)源庫(kù)齿风,優(yōu)勢(shì)和使用方法不用說(shuō)了药薯,簡(jiǎn)單過(guò)一下源碼,了解核心原理救斑。

當(dāng)前EventBus最新版本是3.1.1童本,和前代相比,方法名可以自定義脸候,新增注解支持穷娱。

EventBus構(gòu)造函數(shù)

優(yōu)秀開(kāi)源庫(kù)的基本特征是,常用功能用一個(gè)入口類就可以玩轉(zhuǎn)运沦,EventBus也是泵额。

public static EventBus getDefault() {
  if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}

獲取EventBus對(duì)象常規(guī)使用getDefault,典型的Double Check獲取單例携添。如果有特別需求嫁盲,可以通過(guò)EventBusBuilder,典型的建造者模式薪寓。

EventBus構(gòu)造函數(shù)創(chuàng)建了十多個(gè)對(duì)象亡资,關(guān)注其中的:

EventBus(EventBusBuilder builder) {
    logger = builder.getLogger();
    //...
    mainThreadSupport = builder.getMainThreadSupport();
    mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
    backgroundPoster = new BackgroundPoster(this);
    //...
}

Log

EventBus的日志打印使用Logger接口澜共,已經(jīng)實(shí)現(xiàn)了:

  • AndroidLogger
  • JavaLogger
  • SystemOutLogger
static final boolean ANDROID_LOG_AVAILABLE;

static {
    boolean android = false;
    try {
        android = Class.forName("android.util.Log") != null;
    } catch (ClassNotFoundException e) {
        // OK
    }
    ANDROID_LOG_AVAILABLE = android;
}

public static boolean isAndroidLogAvailable() {
    return ANDROID_LOG_AVAILABLE;
}

對(duì)于Android,直接嘗試獲取Log類锥腻,同時(shí)用來(lái)判斷當(dāng)前是否Android嗦董。

獲取Android的UI線程

EventBus如何讓事件執(zhí)行在Android的UI線程呢?

MainThreadSupport getMainThreadSupport() {
    if (mainThreadSupport != null) {
        return mainThreadSupport;
    } else if (Logger.AndroidLogger.isAndroidLogAvailable()) {
        Object looperOrNull = getAndroidMainLooperOrNull();
        return looperOrNull == null ? null :
                new MainThreadSupport.AndroidHandlerMainThreadSupport((Looper) looperOrNull);
    } else {
        return null;
    }
}

Object getAndroidMainLooperOrNull() {
    try {
        return Looper.getMainLooper();
    } catch (RuntimeException e) {
        // Not really a functional Android (e.g. "Stub!" maven dependencies)
        return null;
    }
}

這里用到了上面判斷Android的方法瘦黑,如果是京革,創(chuàng)建AndroidHandlerMainThreadSupport,保存了Looper.getMainLooper()幸斥。很熟悉吧匹摇,這就是Android的UI線程Looper。

Poster

EventBus的event可以指定運(yùn)行在UI線程或者后臺(tái)線程甲葬,靠的就是Poster廊勃,下文會(huì)說(shuō)到,透露一丟丟:

  • UI線程:MainLooper
  • 后臺(tái)線程:線程池

注冊(cè)/取消注冊(cè)

想要接收EventBus發(fā)送的事件经窖,對(duì)象必須先注冊(cè)坡垫,停止接收時(shí)取消注冊(cè)。注冊(cè)過(guò)程画侣,本質(zhì)是通知EventBus查找對(duì)象中的訂閱方法冰悠。

Subscribe注解

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent4Main(MsgEvent event) {}

EventBus3之后,訂閱方法名改為自定義配乱,通過(guò)注解描述溉卓。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;
    boolean sticky() default false;
    int priority() default 0;
}

除了基礎(chǔ)的ThreadMode線程模型,還可以定義粘性事件和優(yōu)先級(jí)搬泥。

注解信息桑寨,在注冊(cè)過(guò)程最后由SubscriberMethod類描述。

注冊(cè)過(guò)程

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

注冊(cè)時(shí)傳入訂閱對(duì)象佑钾,兩部操作:

  1. 分析訂閱對(duì)象的類西疤,SubscriberMethodFinder從中查找訂閱方法,包裝為SubscriberMethod休溶。
  2. 調(diào)用subscribe代赁,數(shù)據(jù)轉(zhuǎn)化為EventBus內(nèi)部的兩個(gè)Map。
List<SubscriberMethod>
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    //1
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }

    //2
    if (ignoreGeneratedIndex) {
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
        subscriberMethods = findUsingInfo(subscriberClass);
    }
    if (subscriberMethods.isEmpty()) {
        throw new EventBusException("Subscriber " + subscriberClass
                + " and its super classes have no public methods with the @Subscribe annotation");
    } else {
        //3
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}
  1. 查找目標(biāo)類的訂閱方法前兽掰,先查找緩存METHOD_CACHE芭碍,用的是ConcurrentHashMap,因?yàn)橐幚矶嗑€程孽尽。
Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>()
  1. 默認(rèn)創(chuàng)建的EventBus對(duì)象ignoreGeneratedIndex=false窖壕,先看findUsingInfo
  2. 獲取List成功后,保存在緩存METHOD_CACHE
SubscriberMethodFinder.FindState

上面第二步重點(diǎn)是使用SubscriberMethodFinder的內(nèi)部類FindState執(zhí)行查找過(guò)程:

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    //1
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    //2
    while (findState.clazz != null) {
        findState.subscriberInfo = getSubscriberInfo(findState);
        if (findState.subscriberInfo != null) {
            SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
            for (SubscriberMethod subscriberMethod : array) {
                if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                    findState.subscriberMethods.add(subscriberMethod);
                }
            }
        } else {
            //3
            findUsingReflectionInSingleClass(findState);
        }
        //4
        findState.moveToSuperclass();
    }
    //5
    return getMethodsAndRelease(findState);
}

第一步是調(diào)用prepareFindState獲取FindState:

private static final int POOL_SIZE = 4;
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
    
private FindState prepareFindState() {
    synchronized (FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            FindState state = FIND_STATE_POOL[i];
            if (state != null) {
                FIND_STATE_POOL[i] = null;
                return state;
            }
        }
    }
    return new FindState();
}

FindState的創(chuàng)建不是直接new,而是嘗試從FIND_STATE_POOL中獲取(最多保存4個(gè))瞻讽,目測(cè)是享元模式鸳吸,沒(méi)有才new對(duì)象。

第二步進(jìn)入循環(huán)速勇,從當(dāng)前類開(kāi)始晌砾,遍歷父類查找訂閱方法。

這里涉及EventBus3新特性烦磁,運(yùn)行時(shí)反射查找方法當(dāng)然會(huì)慢點(diǎn)养匈,可以使用Subscriber Index在編譯期操作加速,具體不展開(kāi)都伪,直接看else部分呕乎。

第三步的findUsingReflectionInSingleClass,使用反射查找陨晶,細(xì)節(jié)不用展開(kāi)猬仁。

第四步將當(dāng)前查找目標(biāo)切換到父類,繼續(xù)循環(huán)查找珍逸。

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
    List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
    findState.recycle();
    synchronized (FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            if (FIND_STATE_POOL[i] == null) {
                FIND_STATE_POOL[i] = findState;
                break;
            }
        }
    }
    return subscriberMethods;
}

第五步清理FindState逐虚,放回FIND_STATE_POOL,下次繼續(xù)利用谆膳。

subscribe

register函數(shù)對(duì)每個(gè)SubscriberMethod執(zhí)行subscribe,注意這里synchronized當(dāng)前EventBus對(duì)象撮躁。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        if (subscriptions.contains(newSubscription)) {
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                    + eventType);
        }
    }

    int size = subscriptions.size();
    for (int i = 0; i <= size; i++) {
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

    //... 粘性事件略
}

subscribe不外乎是對(duì)數(shù)據(jù)的挪騰漱病,目的是構(gòu)建subscriptionsByEventType和typesBySubscriber兩個(gè)Map:

subscriptionsByEventType

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;

Subscription包裝了訂閱對(duì)象和訂閱方法,因此Map的key/value對(duì)應(yīng)如下:

event類型 -> List<訂閱對(duì)象,訂閱方法>

表示了event由哪些對(duì)象的哪個(gè)方法訂閱了把曼。

typesBySubscriber

private final Map<Object, List<Class<?>>> typesBySubscriber;

typesBySubscriber簡(jiǎn)單些杨帽,表示訂閱對(duì)象訂閱了哪些event類型:

訂閱對(duì)象 -> List<event類型>

取消注冊(cè)

public synchronized void unregister(Object subscriber) {
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        for (Class<?> eventType : subscribedTypes) {
            unsubscribeByEventType(subscriber, eventType);
        }
        typesBySubscriber.remove(subscriber);
    } else {
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}

取消注冊(cè)的邏輯很簡(jiǎn)單,將當(dāng)前訂閱者的信息從subscriptionsByEventType和typesBySubscriber中移除嗤军。

發(fā)送

PostingThreadState(每個(gè)線程一份)

EventBus發(fā)送event調(diào)用post方法注盈,首先來(lái)認(rèn)識(shí)EventBus核心的內(nèi)部類PostingThreadState。

private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
    @Override
    protected PostingThreadState initialValue() {
        return new PostingThreadState();
    }
};
    
final static class PostingThreadState {
    final List<Object> eventQueue = new ArrayList<>();
    boolean isPosting;
    boolean isMainThread;
    Subscription subscription;
    Object event;
    boolean canceled;
}

PostingThreadState包含eventQueue和一些標(biāo)志變量叙赚,通過(guò)ThreadLocal在每個(gè)線程中維護(hù)一個(gè)對(duì)象(ThreadLocal的initialValue方法初始創(chuàng)建PostingThreadState對(duì)象)老客。

沒(méi)有深?yuàn)W的玄妙,看數(shù)據(jù)結(jié)構(gòu)大概可以知道震叮,EventBus發(fā)送原理是將event入隊(duì)在線程上的PostingThreadState胧砰,再查找訂閱者發(fā)送。

event進(jìn)出隊(duì)

public void post(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    if (!postingState.isPosting) {
        postingState.isMainThread = isMainThread();
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
            while (!eventQueue.isEmpty()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}

發(fā)送event的邏輯很明確苇瓣,取出當(dāng)前線程的PostingThreadState尉间,將event入隊(duì)。當(dāng)不在發(fā)送狀態(tài)時(shí),循環(huán)調(diào)用postSingleEvent發(fā)送所有event哲嘲。

發(fā)送event

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    //1
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    if (eventInheritance) {
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) {
            Class<?> clazz = eventTypes.get(h);
            subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
        }
    } else {
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
    //2
    if (!subscriptionFound) {
        if (logNoSubscriberMessages) {
            logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}

第一部分先收集event的類型贪薪,eventInheritance在EventBus默認(rèn)構(gòu)造函數(shù)里為true,表示會(huì)向上遍歷event的父類眠副。然后調(diào)用postSingleEventForEventType執(zhí)行發(fā)送過(guò)程画切,得到發(fā)送結(jié)果subscriptionFound。

第二部分侦啸,如果找不到訂閱者槽唾,EventBus將會(huì)發(fā)送NoSubscriberEvent這個(gè)事件,業(yè)務(wù)上可以接收處理光涂。

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            try {
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } finally {
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
}

postSingleEventForEventType根據(jù)event類型從subscriptionsByEventType獲取所有訂閱者庞萍,對(duì)每個(gè)訂閱者執(zhí)行postToSubscription。

訂閱方法各有執(zhí)行線程的要求忘闻,postToSubscription其實(shí)就是對(duì)各個(gè)線程模型的分開(kāi)處理钝计,下面分析常用的POSTING、MAIN和BACKGROUND齐佳。

POSTING
void invokeSubscriber(Subscription subscription, Object event) {
    try {
        subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
    } catch (InvocationTargetException e) {
        handleSubscriberException(subscription, event, e.getCause());
    } catch (IllegalAccessException e) {
        throw new IllegalStateException("Unexpected exception", e);
    }
}

POSTING私恬,在哪個(gè)線程發(fā)送,就在哪個(gè)線程處理事件炼吴,直接反射invoke本鸣。

MAIN
 if (isMainThread) {
    invokeSubscriber(subscription, event);
} else {
    mainThreadPoster.enqueue(subscription, event);
}

如果當(dāng)前已經(jīng)在UI線程,直接調(diào)用invokeSubscriber硅蹦,否則調(diào)用mainThreadPoster將event入隊(duì)荣德。

前面分析了AndroidHandlerMainThreadSupport保存了UI線程的Looper,通過(guò)Looper創(chuàng)建HandlerPoster童芹,mainThreadPoster正是HandlerPoster的對(duì)象涮瞻。有了UI線程的Handler,接下來(lái)的都是順理成章的代碼假褪。

public void enqueue(Subscription subscription, Object event) {
    PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
    synchronized (this) {
        queue.enqueue(pendingPost);
        if (!handlerActive) {
            handlerActive = true;
            if (!sendMessage(obtainMessage())) {
                throw new EventBusException("Could not send handler message");
            }
        }
    }
}

訂閱者信息Subscription和event包裝為PendingPost署咽,通過(guò)next字段,多個(gè)PendingPost構(gòu)建隊(duì)列PendingPostQueue生音。

@Override
public void handleMessage(Message msg) {
    boolean rescheduled = false;
    try {
        long started = SystemClock.uptimeMillis();
        while (true) {
            PendingPost pendingPost = queue.poll();
            if (pendingPost == null) {
                synchronized (this) {
                    // Check again, this time in synchronized
                    pendingPost = queue.poll();
                    if (pendingPost == null) {
                        handlerActive = false;
                        return;
                    }
                }
            }
            eventBus.invokeSubscriber(pendingPost);
            long timeInMethod = SystemClock.uptimeMillis() - started;
            if (timeInMethod >= maxMillisInsideHandleMessage) {
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
                rescheduled = true;
                return;
            }
        }
    } finally {
        handlerActive = rescheduled;
    }
}

通過(guò)發(fā)送空message宁否,切換線程到UI線程,從隊(duì)列中取出event調(diào)用invokeSubscriber久锥。注意到家淤,在UI線程有執(zhí)行時(shí)長(zhǎng)限制,超時(shí)會(huì)拋出異常瑟由。

BACKGROUND
if (isMainThread) {
    backgroundPoster.enqueue(subscription, event);
} else {
    invokeSubscriber(subscription, event);
}
  • 如果事件在UI線程中發(fā)布絮重,那么該事件就會(huì)在新的線程中運(yùn)行
  • 如果事件在子線程中發(fā)布冤寿,那么該事件直接在發(fā)布事件的線程中執(zhí)行

BackgroundPoster處理事件在新線程中的運(yùn)行,同樣的創(chuàng)建PendingPost并入隊(duì)青伤。

public void enqueue(Subscription subscription, Object event) {
    PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
    synchronized (this) {
        queue.enqueue(pendingPost);
        if (!executorRunning) {
            executorRunning = true;
            eventBus.getExecutorService().execute(this);
        }
    }
}

線程池從EventBus對(duì)象獲取督怜,看定義,是一個(gè)線程數(shù)幾乎無(wú)限制的CachedThreadPool狠角。

private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();

最終在線程池的執(zhí)行過(guò)程也是PendingPost出隊(duì)号杠,調(diào)用invokeSubscriber。(代碼嵌套很深7岣琛)

@Override
public void run() {
    try {
        try {
            while (true) {
                PendingPost pendingPost = queue.poll(1000);
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            executorRunning = false;
                            return;
                        }
                    }
                }
                eventBus.invokeSubscriber(pendingPost);
            }
        } catch (InterruptedException e) {
            eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
        }
    } finally {
        executorRunning = false;
    }
}

后記

EventBus源碼沒(méi)有太難的地方姨蟋,快速通過(guò)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末立帖,一起剝皮案震驚了整個(gè)濱河市眼溶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌晓勇,老刑警劉巖堂飞,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異绑咱,居然都是意外死亡绰筛,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門描融,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)铝噩,“玉大人,你說(shuō)我怎么就攤上這事窿克”¢唬” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵让歼,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我丽啡,道長(zhǎng)谋右,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任补箍,我火速辦了婚禮改执,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘坑雅。我一直安慰自己辈挂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布裹粤。 她就那樣靜靜地躺著终蒂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拇泣,一...
    開(kāi)封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天噪叙,我揣著相機(jī)與錄音,去河邊找鬼霉翔。 笑死睁蕾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的债朵。 我是一名探鬼主播子眶,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼序芦!你這毒婦竟也來(lái)了臭杰?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤芝加,失蹤者是張志新(化名)和其女友劉穎硅卢,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體藏杖,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡将塑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蝌麸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片点寥。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翔横,死狀恐怖瓢阴,靈堂內(nèi)的尸體忽然破棺而出竿痰,到底是詐尸還是另有隱情躬拢,我是刑警寧澤谱秽,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布钧排,位于F島的核電站田藐,受9級(jí)特大地震影響宇植,放射性物質(zhì)發(fā)生泄漏怠苔。R本人自食惡果不足惜同廉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柑司。 院中可真熱鬧迫肖,春花似錦、人聲如沸攒驰。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)玻粪。三九已至隅津,卻和暖如春诬垂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饥瓷。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工剥纷, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人呢铆。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓晦鞋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親棺克。 傳聞我的和親對(duì)象是個(gè)殘疾皇子悠垛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344