Cocos2dx-v3.10 事件分發(fā)機(jī)制源碼解析

Cocos的事件分發(fā)機(jī)制祖能,怎么說呢歉秫,總感覺有些亂蛾洛,借此整理一下养铸。先看看與事件分發(fā)相關(guān)的類。

事件相關(guān)的類

Event 相關(guān)類, 分發(fā)事件的中的事件:

Event(基類), EventCustom(自定義事件), EventTouch(觸摸事件), EventMouse(鼠標(biāo)事件), EventKeyboard(鍵盤事件), EventFocus(控件獲取焦點(diǎn)事件), EventAcceleration(加速計(jì)事件)

事件監(jiān)聽器:

EventListener, EventListenerCustom, EventListenerFocus, EventListenerMouse, EventListenerTouch, EventListenerKayboard, EventListenerAcceleration

事件ID

ListenerID(事件的區(qū)分標(biāo)志轧膘,其實(shí)就是std::string

事件分發(fā)器:

EventDispatcher(事件分發(fā)機(jī)制邏輯集合體)

創(chuàng)建事件

創(chuàng)建事件就簡單的new一個(gè)Event的子類即可钞螟。

事件監(jiān)聽器的創(chuàng)建與監(jiān)聽

事件監(jiān)聽器,也就是說EventListener谎碍。添加事件監(jiān)聽器有三個(gè)方法鳞滨,都在EventDispatch中,分別是:

/** Adds a event listener for a specified event with the priority of scene graph.
 *  @param listener The listener of a specified event.
 *  @param node The priority of the listener is based on the draw order of this node.
 *  @note  The priority of scene graph will be fixed value 0. So the order of listener item
 *          in the vector will be ' <0, scene graph (0 priority), >0'.
*/
void addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node);
 
/** Adds a event listener for a specified event with the fixed priority.
 *  @param listener The listener of a specified event.
 *  @param fixedPriority The fixed priority of the listener.
 *  @note A lower priority will be called before the ones that have a higher value.
 *        0 priority is forbidden for fixed priority since it's used for scene graph based priority.
*/
void addEventListenerWithFixedPriority(EventListener* listener, int fixedPriority);
 
/** Adds a Custom event listener.
It will use a fixed priority of 1.
* @param eventName A given name of the event.
* @param callback A given callback method that associated the event name.
* @return the generated event. Needed in order to remove the event from the dispatcher
*/
EventListenerCustom* addCustomEventListener(const std::string &eventName, const std::function<void(EventCustom*)>& callback);

事件分發(fā)的時(shí)候主要有兩種優(yōu)先級(jí)蟆淀。
第一種是:ScenePriority
第二種是:FixedPriority
仔細(xì)跳到addEventListenerWithSceneGraphPriority函數(shù)去看看會(huì)發(fā)現(xiàn):

void EventDispatcher::addEventListenerWithSceneGraphPriority(EventListener* listener, Node* node)
{
    ...
    // 設(shè)置優(yōu)先級(jí)
    listener->setFixedPriority(0);
    ...
 
    addEventListener(listener);
}
 
EventListenerCustom* EventDispatcher::addCustomEventListener(const std::string &eventName, const std::function<void(EventCustom*)>& callback)
{
    EventListenerCustom *listener = EventListenerCustom::create(eventName, callback);\
    // 設(shè)置優(yōu)先級(jí)
    addEventListenerWithFixedPriority(listener, 1);
    return listener;
}

其實(shí)事件監(jiān)聽器保存的優(yōu)先級(jí)其實(shí)只有 FixPriority拯啦。

EventListener這個(gè)類的方法中也可以看出來,里面涉及到保存優(yōu)先級(jí)的只有setFixedPriority這一個(gè)函數(shù)熔任。

說到優(yōu)先級(jí)褒链,那么事件分發(fā)的時(shí)候是怎么處理這些優(yōu)先級(jí)的呢?官網(wǎng)只有寫 SceneGraphPriority 的優(yōu)先級(jí)是怎么處理的疑苔,那么 FixPriority 的優(yōu)先程度和數(shù)值是什么關(guān)系甫匹,這就去偷窺內(nèi)部了。

事件分發(fā)優(yōu)先級(jí)順序:
我們來脫掉 EventDispatcher 的衣服看看:

void EventDispatcher::dispatchEvent(Event* event)
{
    ...
    // 先通過event獲取到事件的標(biāo)志ListenerID
    auto listenerID = __getListenerID(event);
    // 排序此事件的所有的監(jiān)聽器
    sortEventListeners(listenerID);
    // 分發(fā)事件邏輯的函數(shù)指針
    auto pfnDispatchEventToListeners = &EventDispatcher::dispatchEventToListeners;
    if (event->getType() == Event::Type::MOUSE) {
        // 如果是鼠標(biāo)事件重新賦值分發(fā)事件的函數(shù)指針
        pfnDispatchEventToListeners = &EventDispatcher::dispatchTouchEventToListeners;
    }
    // 獲取改事件的所有的監(jiān)聽器
    auto iter = _listenerMap.find(listenerID);
    if (iter != _listenerMap.end())
    {
        // 如果有,取出里面監(jiān)聽器的Vector
        auto listeners = iter->second;
        // 找到對(duì)應(yīng)的監(jiān)聽器的時(shí)候會(huì)觸發(fā)的回調(diào)函數(shù)
        auto onEvent = [&event](EventListener* listener) -> bool{
            event->setCurrentTarget(listener->getAssociatedNode());
            // 觸發(fā)onEvent回調(diào)
            listener->_onEvent(event);
            return event->isStopped();
        };
        // 調(diào)用函數(shù)指針分發(fā)事件
        (this->*pfnDispatchEventToListeners)(listeners, onEvent);
    }
 
    ...
}

偷窺代碼分析后發(fā)現(xiàn)這段代碼并沒有詳細(xì)指出分發(fā)事件的時(shí)候的優(yōu)先級(jí)兵迅。仔細(xì)想想應(yīng)該是 sortEventListenerpfnDispatchEventToListeners 中在搞鬼抢韭。
先分析 sortEventListener,脫掉她的胖次去里面看看恍箭。

void EventDispatcher::sortEventListeners(const EventListener::ListenerID& listenerID)
{
    ...
        ...
 
        if ((int)dirtyFlag & (int)DirtyFlag::FIXED_PRIORITY)
        {
            // 對(duì)FixPriority優(yōu)先級(jí)類型的監(jiān)聽器排序
            sortEventListenersOfFixedPriority(listenerID);
        }
 
        if ((int)dirtyFlag & (int)DirtyFlag::SCENE_GRAPH_PRIORITY)
        {
            auto rootNode = Director::getInstance()->getRunningScene();
            if (rootNode)
            {
                // 對(duì)SceneGraphPriority類型的監(jiān)聽器排序
                sortEventListenersOfSceneGraphPriority(listenerID, rootNode);
            }
            ...
        }
    }
}
 
void EventDispatcher::sortEventListenersOfFixedPriority(const EventListener::ListenerID& listenerID)
{
    auto listeners = getListeners(listenerID);
 
    ...
 
    // After sort: priority < 0, > 0 排序刻恭,根據(jù)FixedPriority的數(shù)值大小,越小的優(yōu)先級(jí)越高
    std::sort(fixedListeners->begin(), fixedListeners->end(), [](const EventListener* l1, const EventListener* l2) {
        return l1->getFixedPriority() < l2->getFixedPriority();
    });
 
    ...
}
 
void EventDispatcher::sortEventListenersOfSceneGraphPriority(const EventListener::ListenerID& listenerID, Node* rootNode)
{
    auto listeners = getListeners(listenerID);
 
    ...
    auto sceneGraphListeners = listeners->getSceneGraphPriorityListeners();
 
    ...
 
    // Reset priority index
    _nodePriorityIndex = 0;
    _nodePriorityMap.clear();
 
    // 從當(dāng)前根節(jié)點(diǎn)rootNode開始扯夭,遍歷整棵節(jié)點(diǎn)樹吠各,并標(biāo)記上相應(yīng)的節(jié)點(diǎn)等級(jí),父節(jié)點(diǎn)的優(yōu)先級(jí)比子節(jié)點(diǎn)的大
    visitTarget(rootNode, true);
 
    // After sort: priority < 0, > 0 因?yàn)镾ceneGraphPriority都是FixPriority為0的事件類型勉抓,所以比較節(jié)點(diǎn)在渲染書中的優(yōu)先級(jí)
    std::sort(sceneGraphListeners->begin(), sceneGraphListeners->end(), [this](const EventListener* l1, const EventListener* l2) {
        return _nodePriorityMap[l1->getAssociatedNode()] > _nodePriorityMap[l2->getAssociatedNode()];
    });
 
    ...
}

上面寫了兩種優(yōu)先級(jí)的排序贾漏,一種是 FixPriority,優(yōu)先級(jí)根據(jù) fixedPriority 的數(shù)值從小往大排序藕筋、另一種是 SceneGraphPriority纵散,根據(jù)節(jié)點(diǎn)在渲染樹種的優(yōu)先級(jí)排序,具體怎么樣官網(wǎng)有解釋隐圾,這里不做展開伍掀。

值的注意的是 sortEventListener 的時(shí)候,判斷當(dāng)前 ListenerID 的類型是用位標(biāo)記來判斷的暇藏,一個(gè) int 類型的 flag蜜笤。也就是說明一個(gè) listenerID 既可以是 SceneGraphPriority 也可以是 FixedPriority,那么實(shí)際分發(fā)的時(shí)候這兩個(gè)的優(yōu)先級(jí)怎么排盐碱?答案就在 pfnDispatchEventToListeners

pfnDispatchEventToListeners 可以指向兩個(gè)方法:dispatchEventToListenersdispatchTouchEventToListeners把兔。其中兩個(gè)方法除了分發(fā)SceneGraphPriority的時(shí)候不一樣外,其他的一樣瓮顽,為了方便起見县好,這里只分析 dispatchEventToListeners

void EventDispatcher::dispatchEventToListeners(EventListenerVector* listeners, const std::function<bool(EventListener*)>& onEvent)
{
    bool shouldStopPropagation = false;
    auto fixedPriorityListeners = listeners->getFixedPriorityListeners();
    auto sceneGraphPriorityListeners = listeners->getSceneGraphPriorityListeners();
 
    ssize_t i = 0;
    // priority < 0 優(yōu)先處理priority小于0的時(shí)候的事件監(jiān)聽器
    if (fixedPriorityListeners)
    {
        CCASSERT(listeners->getGt0Index() <= static_cast<ssize_t>(fixedPriorityListeners->size()), "Out of range exception!");
 
        if (!fixedPriorityListeners->empty())
        {
            for (; i < listeners->getGt0Index(); ++i)
            {
                auto l = fixedPriorityListeners->at(i);
                // 判斷是否可以執(zhí)行事件暖混,如果可以最后調(diào)用onEvent執(zhí)行缕贡,如果onEvent返回true,說明吞噬事件拣播,結(jié)束分發(fā)晾咪。
                if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
                {
                    shouldStopPropagation = true;
                    break;
                }
            }
        }
    }
    // 接下來分發(fā)SceneGraphPriority的事件
    if (sceneGraphPriorityListeners)
    {
        // 判斷事件是否已經(jīng)終止發(fā)送
        if (!shouldStopPropagation)
        {
            // priority == 0, scene graph priority
            for (auto& l : *sceneGraphPriorityListeners)
            {
                if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
                {
                    shouldStopPropagation = true;
                    break;
                }
            }
        }
    }
    // 最后分發(fā)到fixedPriority > 0 的監(jiān)聽器
    if (fixedPriorityListeners)
    {
        if (!shouldStopPropagation)
        {
            // priority > 0
            ssize_t size = fixedPriorityListeners->size();
            for (; i < size; ++i)
            {
                auto l = fixedPriorityListeners->at(i);
 
                if (l->isEnabled() && !l->isPaused() && l->isRegistered() && onEvent(l))
                {
                    shouldStopPropagation = true;
                    break;
                }
            }
        }
    }
}

這里面清楚的寫出了事件分發(fā)時(shí)候的邏輯處理,先分發(fā)事件到 fixedPriority < 0 的監(jiān)聽器中贮配,然后再分發(fā)到 = 0 的監(jiān)聽器(SceneGraphPriority)中谍倦,最后在分發(fā)到 > 0 的監(jiān)聽器中,如果中途出現(xiàn) onEvent 返回為 true 的結(jié)果牧嫉,則終止分發(fā)剂跟。

Ps:這里的 onEvent 調(diào)用的其實(shí)就是上面 dispatchEvent 代碼中的 lambda 表達(dá)式减途。如果想要?jiǎng)?chuàng)建一個(gè)觸摸事件的優(yōu)先級(jí)比當(dāng)前所有的觸摸事件優(yōu)先級(jí)都高話,只需要把 fixedPriority 的數(shù)值設(shè)為 < 0 即可曹洽。

當(dāng)正在分發(fā)事件的時(shí)候(_inDispath > 0)鳍置,添加和刪除監(jiān)聽器事件

EventDispatcher 中有表示當(dāng)前分發(fā)的事件數(shù)的私有成員變量 _inDispatch ,它是一個(gè) int 類型的數(shù)據(jù)送淆,用于表示當(dāng)前正有多少事件正在分發(fā)税产。既然是表示事件正在分發(fā)的數(shù)量,可定有 ++, -- 的操作偷崩,在DispatchEvent中會(huì)有+1和-1的操作辟拷,但是藏得比較深,怎么個(gè)深法阐斜,看下述源碼衫冻。

void EventDispatcher::dispatchEvent(Event* event)
{
    ...
 
    DispatchGuard guard(_inDispatch);
 
    ...
}

我去,這啥都都沒有摆顺觥隅俘??笤喳?發(fā)現(xiàn)這里面只有使用 _inDispatch 創(chuàng)建了一個(gè)變量为居,并沒有++,- -操作杀狡。其實(shí) DispatchGuard 這個(gè)類的構(gòu)造函數(shù)的參數(shù)是一個(gè) int 類型的引用蒙畴,構(gòu)造時(shí)候?qū)ζ?1,析構(gòu)函數(shù)的時(shí)候會(huì)-1操作呜象。用法很妙膳凝,借助了局部變量是在棧里面這個(gè)特性,當(dāng)方法DispatchEvent結(jié)束的時(shí)候董朝,局部變量guard會(huì)析構(gòu)鸠项,此時(shí)會(huì)-1操作。下面是 DispatchGuard 的源碼子姜。

class DispatchGuard
{
public:
    DispatchGuard(int& count):
            _count(count)
    {
        ++_count;
    }
 
    ~DispatchGuard()
    {
        --_count;
    }
 
private:
    int& _count;
};

了解了_inDispatch的意義,我們來看看添加事件監(jiān)聽器和刪除事件監(jiān)聽器的時(shí)候楼入,如果正在分發(fā)事件(_inDispatch > 0)會(huì)怎么處理哥捕。

void EventDispatcher::addEventListener(EventListener* listener)
{
    if (_inDispatch == 0)
    {
        forceAddEventListener(listener);
    }
    else
    {
        _toAddedListeners.push_back(listener);
    }
 
    listener->retain();
}

上述代碼是監(jiān)聽器添加的時(shí)候的代碼,可以看到如果當(dāng)時(shí)正在分發(fā)事件嘉熊,會(huì)把當(dāng)前需要添加的監(jiān)聽器添加到待添加向量(_toAddedListeners)中遥赚,那么也就是說在事件分發(fā)完畢之后監(jiān)聽器需要從toAddedListeners中轉(zhuǎn)移到正式向量中,這部分代碼可以在updateListeners中看到阐肤,此方法會(huì)在事件分發(fā)結(jié)束之后調(diào)用凫佛。

void EventDispatcher::updateListeners(Event* event)
{
    CCASSERT(_inDispatch > 0, "If program goes here, there should be event in dispatch.");
 
    if (_inDispatch > 1)
        return;
 
    ...
 
    if (!_toAddedListeners.empty())
    {
        for (auto& listener : _toAddedListeners)
        {
            forceAddEventListener(listener);
        }
        _toAddedListeners.clear();
    }
 
    if (!_toRemovedListeners.empty())
    {
        cleanToRemovedListeners();
    }
}

代碼中讲坎,除了添加事件監(jiān)聽器有個(gè)待加入向量外,刪除事件監(jiān)聽器也有一個(gè)待刪除向量(不過這個(gè)好像是廢話)愧薛,開頭有判斷當(dāng)前是否還是處于事件分發(fā)中晨炕。

Cocos Bug 之 DirtyFlag

上面的代碼中,我刪了一些代碼毫炉,因?yàn)榇a太多影響閱讀瓮栗。其中就有很多涉及到了Dirty Flag,望文取義的話是臟標(biāo)記瞄勾,問題是這個(gè)臟是什么臟费奸?

下面來看一下添加監(jiān)聽器和移除監(jiān)聽器的代碼部分。

// 添加監(jiān)聽器
void EventDispatcher::forceAddEventListener(EventListener* listener)
{
    EventListenerVector* listeners = nullptr;
    ...
 
    if (listener->getFixedPriority() == 0)
    {
        setDirty(listenerID, DirtyFlag::SCENE_GRAPH_PRIORITY);
 
        ...
    }
    else
    {
        setDirty(listenerID, DirtyFlag::FIXED_PRIORITY);
    }
}
 // 移除監(jiān)聽器
void EventDispatcher::removeEventListener(EventListener* listener)
{
    ...
 
    for (auto iter = _listenerMap.begin(); iter != _listenerMap.end();)
    {
        ...
 
        removeListenerInVector(sceneGraphPriorityListeners);
        if (isFound)
        {
            // fixed #4160: Dirty flag need to be updated after listeners were removed.
            setDirty(listener->getListenerID(), DirtyFlag::SCENE_GRAPH_PRIORITY);
        }
        else
        {
            removeListenerInVector(fixedPriorityListeners);
            if (isFound)
            {
                setDirty(listener->getListenerID(), DirtyFlag::FIXED_PRIORITY);
            }
        }
 
    ...
}

代碼中可以看到进陡,這個(gè) DirtyFlag 是設(shè)置給 ListenerID 的愿阐,每當(dāng)新添加一個(gè) Listener,或則刪除一個(gè) Listener 的時(shí)候趾疚,就會(huì)給當(dāng)前 ListenerListenerID 添加一個(gè) DirtyFlag换况。說明這個(gè)臟是指 ListenerID 對(duì)應(yīng)的監(jiān)聽器向量列表需要重新排序了,如果不臟就不需要排序盗蟆。

那么問題來了:
照理只要出現(xiàn)了刪除戈二,修改,添加監(jiān)聽器的時(shí)候喳资,監(jiān)聽器列表需要重新排序觉吭,都需要設(shè)置相應(yīng)的 DirtyFlag 操作。但是 Cocos-2dx v3.10 里面的 updateListeners 函數(shù)有刪除監(jiān)聽器的操作仆邓,然而并沒有設(shè)置相應(yīng)的 DirtyFlag 操作鲜滩。此問題我在 Cocos2dx github 的 issues中有回答問題鏈接

這個(gè)就是一個(gè) Bug 了,放著不管會(huì)拋出以下異常

CCASSERT(listeners->getGt0Index() <= static_cast<ssize_t>(fixedPriorityListeners->size()), "Out of range exception!");

代碼中的 Gt0Index() 方法其實(shí)就是獲取到當(dāng)前監(jiān)聽器里誒包中 fixedPriority == 0 的監(jiān)聽器在監(jiān)聽器向量中的位置节值,它只有在給 Listener 排序的時(shí)候會(huì)設(shè)置徙硅,但是如果更新了對(duì)應(yīng) ListenerID 的向量(EventListenerVector),但是沒有重新排序搞疗,就會(huì)出現(xiàn) _gt0Index 未及時(shí)更新的情況嗓蘑,導(dǎo)致拋出這個(gè)異常。
排序的時(shí)候匿乃,會(huì)判斷排序的這個(gè) ListenerID 是否處于Dirty的狀態(tài)桩皿,只有臟狀態(tài)才會(huì)排序,這算優(yōu)化吧幢炸,所以必須在 updateListener 的時(shí)候加上 DirtyFlag泄隔。

Bug 修復(fù)

void EventDispatcher::updateListeners(Event* event)
{
   ...
 
    auto onUpdateListeners = [this](const EventListener::ListenerID& listenerID)
    {
        ...
 
        if (sceneGraphPriorityListeners)
        {
            for (auto iter = sceneGraphPriorityListeners->begin(); iter != sceneGraphPriorityListeners->end();)
            {
                auto l = *iter;
                if (!l->isRegistered())
                {
                    ...
                    // if item in toRemove list, remove it from the list
                    setDirty(l->getListenerID(), DirtyFlag::SCENE_GRAPH_PRIORITY);
                    ..
                }
                ...
            }
        }
 
        if (fixedPriorityListeners)
        {
            for (auto iter = fixedPriorityListeners->begin(); iter != fixedPriorityListeners->end();)
            {
                auto l = *iter;
                if (!l->isRegistered())
                {
                    ...
                    // if item in toRemove list, remove it from the list
                    setDirty(l->getListenerID(), DirtyFlag::FIXED_PRIORITY);
                    ...
                }
                ...
            }
        }
 
        ...
    };
 
    ...
}

PS: 場(chǎng)景切換的時(shí)候 EventDispatcher 會(huì)設(shè)置成 _isEnabled = false; 這時(shí)候分發(fā)自定義事件是無效的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宛徊,一起剝皮案震驚了整個(gè)濱河市佛嬉,隨后出現(xiàn)的幾起案子逻澳,更是在濱河造成了極大的恐慌,老刑警劉巖暖呕,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件斜做,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡缰揪,警方通過查閱死者的電腦和手機(jī)陨享,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钝腺,“玉大人抛姑,你說我怎么就攤上這事⊙藓” “怎么了定硝?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長毫目。 經(jīng)常有香客問我蔬啡,道長,這世上最難降的妖魔是什么镀虐? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任箱蟆,我火速辦了婚禮,結(jié)果婚禮上刮便,老公的妹妹穿的比我還像新娘空猜。我一直安慰自己,他們只是感情好恨旱,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布辈毯。 她就那樣靜靜地躺著,像睡著了一般搜贤。 火紅的嫁衣襯著肌膚如雪谆沃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天仪芒,我揣著相機(jī)與錄音唁影,去河邊找鬼。 笑死桌硫,一個(gè)胖子當(dāng)著我的面吹牛夭咬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铆隘,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼南用!你這毒婦竟也來了膀钠?” 一聲冷哼從身側(cè)響起掏湾,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肿嘲,沒想到半個(gè)月后融击,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡雳窟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年尊浪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片封救。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拇涤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出誉结,到底是詐尸還是另有隱情鹅士,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布惩坑,位于F島的核電站掉盅,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏以舒。R本人自食惡果不足惜趾痘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蔓钟。 院中可真熱鬧永票,春花似錦、人聲如沸奋刽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽佣谐。三九已至肚吏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狭魂,已是汗流浹背罚攀。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留雌澄,地道東北人斋泄。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像镐牺,于是被迫代替她去往敵國和親炫掐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,305評(píng)論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理睬涧,服務(wù)發(fā)現(xiàn)募胃,斷路器旗唁,智...
    卡卡羅2017閱讀 134,708評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,859評(píng)論 6 342
  • 放逐山下,青煙繚繞痹束。 “哎检疫,小鬼,你怎么這么小就成鬼了祷嘶?”餓鬼雙眼凹陷屎媳,似兩個(gè)大黑窟窿,盯著他說道论巍。 “我說餓鬼啊...
    驕傲的橘子閱讀 803評(píng)論 0 0
  • 爸: 清明節(jié)前夕我和舅舅烛谊、妹妹、表姐去看您环壤,一切尚好晒来。只是去時(shí)我把要燒的紙忘在出租車上了,懊惱不己郑现。好像每次去看您...
    熹喜閱讀 286評(píng)論 0 0