第三十四篇:在SOUI中使用異步通知


概述

異步通知是客戶端開發中常見的需求,比如在一個網絡處理線程中要通知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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM