EventBus 源碼分析

這篇文章主要是根據(jù)我們平時(shí)的使用盾鳞,一步一步的分析EventBus源碼流程爬虱,因此分為三步:

1、注冊(cè)訂閱者
2躺涝、事件發(fā)布
3厨钻、反注冊(cè)訂閱者

1、register 注冊(cè)訂閱者

在使用eventBus的時(shí)候坚嗜,第一個(gè)步驟就是注冊(cè)訂閱者

  EventBus.getDefault().register(this);

getDefault方法是一個(gè)單例模式的初始化方法,主要就是獲取一個(gè)實(shí)例诗充,源碼如下:

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

可以很清楚的看到苍蔬,這里只是一個(gè)DoubleCheck的單例模式,接下來(lái)直接看一下構(gòu)造方法:

  /**
     * Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
     * central bus, consider {@link #getDefault()}.
     */
    public EventBus() {
        this(DEFAULT_BUILDER);
    }

    EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadSupport = builder.getMainThreadSupport();
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
      //
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
    }

構(gòu)造方法里面是通過(guò)一個(gè)Builder模式來(lái)對(duì)EventBus各項(xiàng)配置進(jìn)行初始化
在getDefault獲取到實(shí)例之后蝴蜓,就會(huì)調(diào)用register方法進(jìn)行注冊(cè)碟绑,這時(shí)候進(jìn)入register方法看一下注冊(cè)過(guò)程:

   public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        //獲取到訂閱者中所有的訂閱方法,并儲(chǔ)存到list集合中
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

從源碼里面看茎匠,注冊(cè)過(guò)程只有兩步:

a格仲、根據(jù)注冊(cè)時(shí)傳入的訂閱者對(duì)象,找到所有的訂閱方法诵冒;
b凯肋、訂閱所有的訂閱方法

a1)、
首先看一下如何查找到訂閱者中所有的訂閱方法汽馋,即findSubscriberMethods()方法侮东,subscriberMethodFinder是在EventBus構(gòu)造方法中進(jìn)行的初始化,進(jìn)入findSubscriberMethods方法中進(jìn)行查看:

        List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        //METHOD_CACHE 一個(gè)map集合豹芯,以訂閱者為key悄雅,以訂閱方法的list集合為value進(jìn)行緩存
        List<SubscriberMethod> subscriberMethods =  METHOD_CACHE.get(subscriberClass);
        //如果當(dāng)前的對(duì)象已經(jīng)被緩存,則直接獲取返回
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
     if (ignoreGeneratedIndex) {
           // 如果忽略索引铁蹈,就根據(jù)反射來(lái)獲取
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            //否則使用索引
            subscriberMethods = findUsingInfo(subscriberClass);
        }
      //如果該訂閱者中沒(méi)有訂閱方法宽闲,此處會(huì)拋出一個(gè)異常,提醒你該訂閱者和他的父類里面都沒(méi)有訂閱方法(public 修飾并且@Subscribe 進(jìn)行注解)
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
          //如果有訂閱方法,則把該訂閱者和訂閱方法進(jìn)行緩存容诬,并返回訂閱方法的list集合
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

a1.1)围辙、
findSubscriberMethods 里面的邏輯已經(jīng)很清晰了,現(xiàn)在看一下 findUsingInfo()和findUsingReflection()放案,一般使用的時(shí)候姚建,并沒(méi)有自定義進(jìn)行配置,所以一般都是使用索引進(jìn)行查找

a1.1.1)
這里就先分析一下findUsingInfo()方法吱殉,反射的稍后再看:

//忽略索引時(shí)掸冤,查找方法
 private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        // 創(chuàng)建并初始化FindState對(duì)象 FindState是訂閱者的一個(gè)輔助類,用于獲取到訂閱方法
        FindState findState = prepareFindState();
        //關(guān)聯(lián)訂閱者
        findState.initForSubscriber(subscriberClass);
       //此處通過(guò)循環(huán)友雳,獲取到訂閱者父類中的訂閱方法
        while (findState.clazz != null) {
            //獲取到訂閱者的信息
            findState.subscriberInfo = getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) {
                //如果可以找到訂閱者信息稿湿,將訂閱方法緩存到 subscriberMethods 中
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                //如果找不到訂閱者的信息,則通過(guò)反射方法進(jìn)行獲取
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

FindState類是一個(gè)輔助類押赊,用來(lái)輔助訂閱者獲取訂閱方法饺藤,代碼比較簡(jiǎn)單,篇幅原因此處就不在進(jìn)行源碼解析流礁,只要清楚這個(gè)類主要用途即可
prepareFindState() 方法則是用來(lái)創(chuàng)建并初始化FindState的方法涕俗,同時(shí)坐著也通過(guò)一個(gè)緩存池進(jìn)行了優(yōu)化
從上面源碼中可以看到,在可以直接查找到訂閱信息的情況比較簡(jiǎn)單神帅,直接將查找到的信息進(jìn)行緩存即可再姑,下面分析一下找不到訂閱信息的時(shí)候,通過(guò)反射方法進(jìn)行查找:

//通過(guò)反射方式找御,獲取到訂閱者中所有的訂閱方法
    private void findUsingReflectionInSingleClass(FindState findState) {
        // 1 獲取訂閱者內(nèi)所有方法
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        // 2 遍歷所有方法元镀,找到所有的訂閱方法,并將所有的訂閱方法進(jìn)行緩存到 findState 中
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            //如果是非靜態(tài)霎桅、非抽象栖疑、public 修飾的
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                //只有一個(gè)參數(shù)
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    // Subscribe 進(jìn)行注解
                    if (subscribeAnnotation != null) {
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            //符合條件的進(jìn)行緩存
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                    throw new EventBusException("@Subscribe method " + methodName +
                            "must have exactly 1 parameter but has " + parameterTypes.length);
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }
        }
    }

認(rèn)真看一下每一個(gè)步驟,可以發(fā)現(xiàn)其實(shí)就是通過(guò)反射查找到符合條件的訂閱方法滔驶,然后將訂閱方法緩存到findState中遇革,同時(shí)對(duì)注解的訂閱方法進(jìn)行校驗(yàn),給出提示瓜浸,在查找到所有的訂閱方法之后澳淑,代碼執(zhí)行到了,a1.1.1) findUsingReflectionInSingleClass 執(zhí)行完畢插佛,然后 findUsingInfo()在循環(huán)查找并緩存訂閱者中所有的訂閱方法杠巡,至此, 在a1.1) findUsingInfo 方法也執(zhí)行完畢雇寇,成功找到了所有的訂閱方法氢拥,a1)蚌铜、中忽略索引的情況已經(jīng)執(zhí)行完畢
查找完成之后,我們?cè)诨剡^(guò)頭看一下a1)嫩海、findSubscriberMethods 方法中不忽略 索引的方法 findUsingReflection()

 FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findUsingReflectionInSingleClass(findState);
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);

這段代碼就非常簡(jiǎn)單了冬殃,上面已經(jīng)分析過(guò)了,直接使用反射查找到所有的方法叁怪,并進(jìn)行緩存审葬;
在查找到所有的訂閱方法之后,將所有的方法進(jìn)行緩存到 METHOD_CACHE 奕谭,至此涣觉,注冊(cè)第一步就完成了;
無(wú)論使用哪一種方法血柳,到這里都已經(jīng)找到所有的訂閱方法了官册,在再次返回到 register()方法中:

  synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }

這段代碼通過(guò)循環(huán),將所有的訂閱方法和訂閱者關(guān)聯(lián)起來(lái)难捌,看一下subscribe 方法

  private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        //創(chuàng)建訂閱者和訂閱方法的封裝對(duì)象
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //由于所有的訂閱方法都只有一個(gè)參數(shù)膝宁,并且subscriptionsByEventType 是以 訂閱方法的參數(shù)的class為key,Subscription集合為value的
//      //的map集合根吁,所以员淫,根據(jù)eventType 可以找到所有的含有此參數(shù)的訂閱方法所在的訂閱者
        // 根據(jù)訂閱方法的參數(shù)類型,查找到所有包含有該Event 的訂閱者婴栽,Event 即為訂閱方法的參數(shù)
        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);
            }
        }
        //根據(jù)優(yōu)先級(jí)满粗,將當(dāng)前的訂閱者插入訂閱集合
        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;
            }
        }

        //根據(jù)訂閱者,查找訂閱者里面所有的Event 類型愚争,并儲(chǔ)存
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);
        //對(duì)于粘性事件,則立刻執(zhí)行
        if (subscriberMethod.sticky) {
            
            if (eventInheritance) {
                // Existing sticky events of all subclasses of eventType have to be considered.
                // Note: Iterating over all events may be inefficient with lots of sticky events,
                // thus data structure should be changed to allow a more efficient lookup
                // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

至此挤聘,訂閱者和訂閱事件進(jìn)行了關(guān)聯(lián)轰枝,并且進(jìn)行了緩存,register()完成

2组去、事件發(fā)布

事件發(fā)布一般通過(guò)以下兩種方法進(jìn)行發(fā)布鞍陨,postSticky 為發(fā)送粘性事件

 EventBus.getDefault().post(new Event("我是測(cè)試event數(shù)據(jù)"));
 EventBus.getDefault().postSticky(new Event("我是測(cè)試event數(shù)據(jù)"));

首先先分析一下post 事件

  public void post(Object event) {
        //獲取到ThreadLocal中儲(chǔ)存的數(shù)據(jù),并將當(dāng)前post的數(shù)據(jù)添加到隊(duì)列中
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);
        //如果當(dāng)前沒(méi)有在post 事件
        if (!postingState.isPosting) {
            postingState.isMainThread = isMainThread();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            //通過(guò)循環(huán)取出隊(duì)列中所有數(shù)據(jù)从隆,并進(jìn)行訂閱
            try {
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

 private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        //此處僅判斷是否允許繼承诚撵,如果允許就會(huì)執(zhí)行父類中的訂閱方法,不作為重點(diǎn)關(guān)注內(nèi)容
        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);
        }
        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));
            }
        }
    }

    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        //subscriptionsByEventType键闺,key 為訂閱方法的參數(shù)類型寿烟,value為訂閱者和訂閱方法封裝的list集合
        //根據(jù)register()的源碼,已經(jīng)知道在注冊(cè)的時(shí)候辛燥,會(huì)將訂閱者和訂閱方法緩存到subscriptionsByEventType里面
        synchronized (this) {
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        //如果post的Event有訂閱者接收筛武,循環(huán)進(jìn)行訂閱
        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;
    }

  // 訂閱方法的執(zhí)行方法缝其,會(huì)根據(jù)threadMode將訂閱方法在不同的線程進(jìn)行執(zhí)行
    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND:
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

post方法本身并不復(fù)雜,重點(diǎn)關(guān)注一下postToSubscription()方法徘六,在此方法中内边,根據(jù)不同的線程進(jìn)行處理,執(zhí)行訂閱方法待锈,到這里漠其,事件發(fā)布就完成了,訂閱方法也已經(jīng)被執(zhí)行了竿音,完成了事件的分發(fā)

3和屎、反注冊(cè)訂閱者

反注冊(cè)就非常簡(jiǎn)單了,僅僅是將緩存的訂閱者及其訂閱方法移除即可

    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());
        }
    }
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {
            int size = subscriptions.size();
            for (int i = 0; i < size; i++) {
                Subscription subscription = subscriptions.get(i);
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }

從代碼中可以看出谍失,反注冊(cè)的時(shí)候?qū)⒆?cè)時(shí)候緩存在 typesBySubscriber 和subscriptionsByEventType 中的數(shù)據(jù)移除眶俩,完成了反注冊(cè)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市快鱼,隨后出現(xiàn)的幾起案子颠印,更是在濱河造成了極大的恐慌,老刑警劉巖抹竹,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件线罕,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡窃判,警方通過(guò)查閱死者的電腦和手機(jī)钞楼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)袄琳,“玉大人询件,你說(shuō)我怎么就攤上這事∷舴” “怎么了宛琅?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)逗旁。 經(jīng)常有香客問(wèn)我嘿辟,道長(zhǎng),這世上最難降的妖魔是什么片效? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任红伦,我火速辦了婚禮,結(jié)果婚禮上淀衣,老公的妹妹穿的比我還像新娘昙读。我一直安慰自己,他們只是感情好舌缤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布箕戳。 她就那樣靜靜地躺著某残,像睡著了一般。 火紅的嫁衣襯著肌膚如雪陵吸。 梳的紋絲不亂的頭發(fā)上玻墅,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音壮虫,去河邊找鬼澳厢。 笑死,一個(gè)胖子當(dāng)著我的面吹牛囚似,可吹牛的內(nèi)容都是我干的剩拢。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼饶唤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼徐伐!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起募狂,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤办素,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后祸穷,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體性穿,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年雷滚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了需曾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡祈远,死狀恐怖呆万,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情车份,我是刑警寧澤桑嘶,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站躬充,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏讨便。R本人自食惡果不足惜充甚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望霸褒。 院中可真熱鬧伴找,春花似錦、人聲如沸废菱。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至衰倦,卻和暖如春袒炉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背樊零。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工我磁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人驻襟。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓夺艰,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親沉衣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子郁副,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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

  • 前面對(duì)EventBus 的簡(jiǎn)單實(shí)用寫(xiě)了一篇,相信大家都會(huì)使用豌习,如果使用的還不熟存谎,或者不夠6,可以花2分鐘瞄一眼:h...
    gogoingmonkey閱讀 317評(píng)論 0 0
  • EventBus是在Android中使用到的發(fā)布-訂閱事件總線框架斑鸦,基于觀察者模式愕贡,將事件的發(fā)送者和接收者解耦,簡(jiǎn)...
    BrotherTree閱讀 407評(píng)論 0 1
  • EventBus 源碼分析 分析源碼之前 EventBus 大神的 github巷屿,最好的老師固以。 一、使用 我們?cè)谄?..
    豬_隊(duì)友閱讀 367評(píng)論 0 4
  • EventBus源碼分析 Android開(kāi)發(fā)中我們最常用到的可以說(shuō)就是EventBus了嘱巾,今天我們來(lái)深入研究一下E...
    BlackFlag閱讀 509評(píng)論 3 4
  • 簡(jiǎn)介 前面我學(xué)習(xí)了如何使用EventBus憨琳,還有了解了EventBus的特性,那么接下來(lái)我們一起來(lái)學(xué)習(xí)EventB...
    eirunye閱讀 465評(píng)論 0 0