概述
異步通知是客戶端開發中常見的需求,比如在一個網絡處理線程中要通知UI線程更新等等。
通常在Windows編程中,為了方便,我們一般會向UI線程的窗口句柄Post/Send一個窗口消息從而達到將非UI線程的事件切換到UI線程處理的目的。
在SOUI引入通知中心以前要在SOUI中處理非UI線程事件我也推薦用上面的方法。
使用窗口消息至少有以下兩個不足:
1、需要在線程中持有一個窗口句柄。
2、發出的消息只能在該窗口句柄的消息處理函數里處理。
SNotifyCenter
最新的SOUI引入了一個新的單例對象:SNotifyCenter,專門用來處理這類問題。
新的SNotifyCenter解決了窗口消息存在的上面的兩個問題:
1、通過使用全局單例,SNotifyCenter可以在代碼任意位置獲取它的指針(前提當然是要在它的生命周期內);
2、使用SNotifyCenter產生的通知采用SOUI的事件系統來派發,通過結合SOUI的事件訂閱系統,用戶可以在任意位置處理發出的事件。
在介紹如何使用SNotifyCenter前,先看一下NotifyCenter.h的代碼:
1 #pragma once 2 3 #include <core/SSingleton.h> 4 5 namespace SOUI 6 { 7 template<class T> 8 class TAutoEventMapReg 9 { 10 typedef TAutoEventMapReg<T> _thisClass; 11 public: 12 TAutoEventMapReg() 13 { 14 SNotifyCenter::getSingleton().RegisterEventMap(Subscriber(&_thisClass::OnEvent,this)); 15 } 16 17 ~TAutoEventMapReg() 18 { 19 SNotifyCenter::getSingleton().UnregisterEventMap(Subscriber(&_thisClass::OnEvent,this)); 20 } 21 22 protected: 23 bool OnEvent(EventArgs *e){ 24 T * pThis = static_cast<T*>(this); 25 return !!pThis->_HandleEvent(e); 26 } 27 }; 28 29 class SOUI_EXP SNotifyCenter : public SSingleton<SNotifyCenter> 30 , public SEventSet 31 { 32 public: 33 SNotifyCenter(void); 34 ~SNotifyCenter(void); 35 36 /** 37 * FireEventSync 38 * @brief 觸發一個同步通知事件 39 * @param EventArgs *e -- 事件對象 40 * @return 41 * 42 * Describe 只能在UI線程中調用 43 */ 44 void FireEventSync(EventArgs *e); 45 46 /** 47 * FireEventAsync 48 * @brief 觸發一個異步通知事件 49 * @param EventArgs *e -- 事件對象 50 * @return 51 * 52 * Describe 可以在非UI線程中調用,EventArgs *e必須是從堆上分配的內存,調用后使用Release釋放引用計數 53 */ 54 void FireEventAsync(EventArgs *e); 55 56 57 /** 58 * RegisterEventMap 59 * @brief 注冊一個處理通知的對象 60 * @param const ISlotFunctor &slot -- 事件處理對象 61 * @return 62 * 63 * Describe 64 */ 65 bool RegisterEventMap(const ISlotFunctor &slot); 66 67 /** 68 * RegisterEventMap 69 * @brief 注銷一個處理通知的對象 70 * @param const ISlotFunctor &slot -- 事件處理對象 71 * @return 72 * 73 * Describe 74 */ 75 bool UnregisterEventMap(const ISlotFunctor & slot); 76 protected: 77 void OnFireEvent(EventArgs *e); 78 79 void ExecutePendingEvents(); 80 81 static VOID CALLBACK OnTimer( HWND hwnd, 82 UINT uMsg, 83 UINT_PTR idEvent, 84 DWORD dwTime 85 ); 86 87 SCriticalSection m_cs; //線程同步對象 88 SList<EventArgs*> *m_evtPending;//掛起的等待執行的事件 89 DWORD m_dwMainTrdID;//主線程ID 90 91 UINT_PTR m_timerID; //定時器ID,用來執行異步事件 92 93 SList<ISlotFunctor*> m_evtHandlerMap; 94 }; 95 }
在這個文件中提供了兩個類,一個就是SNotifyCenter,另一個是TAutoEventMapReg<T>。
可以看到SNotifyCenter中除構造外只有4個public方法:
FireEventSync, FireEventAsync用來觸發事件。
RegisterEventMap,UnregisterEventMap則用來提供事件處理訂閱。
如何使用SNotifyCenter?
1、在main中實例化SNotifyCenter。(不明白可以參考demo)
2、定義需要通過通知中心分發的事件類型,首先定義事件,然后向通知中心注冊,參見下面代碼:
1 void CMainDlg::OnBtnStartNotifyThread() 2 { 3 if(IsRunning()) return; 4 SNotifyCenter::getSingleton().addEvent(EVENTID(EventThreadStart)); 5 SNotifyCenter::getSingleton().addEvent(EVENTID(EventThreadStop)); 6 SNotifyCenter::getSingleton().addEvent(EVENTID(EventThread)); 7 8 EventThreadStart evt(this); 9 SNotifyCenter::getSingleton().FireEventSync(&evt); 10 BeginThread(); 11 } 12 13 void CMainDlg::OnBtnStopNotifyThread() 14 { 15 if(!IsRunning()) return; 16 17 EndThread(); 18 EventThreadStop evt(this); 19 SNotifyCenter::getSingleton().FireEventSync(&evt); 20 21 SNotifyCenter::getSingleton().removeEvent(EventThreadStart::EventID); 22 SNotifyCenter::getSingleton().removeEvent(EventThreadStop::EventID); 23 SNotifyCenter::getSingleton().removeEvent(EventThread::EventID); 24 }
3、使需要處理通知中心分發的事件的對象從TAutoEventMapReg繼承,實現事件的自動訂閱(方便在事件映射表中統一處理事件),這一步是可選的,你也可以直接使用SOUI提供的事件訂閱機制向通知中心訂閱特定事件。
4、在事件映射表里處理事件(沒有第3步時,則同樣沒有這一步)。
具體用法參見SOUI的demo。