为了在BREW应用程序开发的过程中能够拥有更加清晰的程序流程,我们特意设计了这样的一个状态机的应用程序框架。
我们的基本思路是这样的,划分应用程序状态的方法是每一个显示界面都作为一个状态,在进入状态的时候创建界面,同时状态机停止运转,此时等待用户在当前的界面进行操作,用户操作完成后关闭当前的界面,并启动状态机继续运行。由于BREW的每一个应用程序都是通过HandleEvent方法来获得事件的,在每一个界面等待用户输入的时候,每一个界面都对应一个界面的HandleEvent函数,用来捕获用户在这个界面的输入。这样,我们的应用程序将被分成状态和界面两种单元。
状态机在代码上讲就是一个for(;;)循环,只有在创建一个用户界面之后才会暂时终止运行,可以实现这样功能的代码如下(可以在本书所附的示例代码Test5/FileExplorer应用目录下的AppStateMachine.c中找到这个函数):
/*==================================================================== 函数 : CStateMachine_RunFSM 说明 : 有限状态机引擎。 参数 : pMe [in]:指向CStateMachine对象结构的指针 返回值 : 无。 备注 : 无 ====================================================================*/ static void CStateMachine_RunFSM(CStateMachine *pMe) { NextFSMAction nextFSMAction = NFSMACTION_WAIT; for(;;) { nextFSMAction = pMe->m_pfnState((IStateMachine *)pMe, pMe->m_pUserState); pMe->m_eWndRet = WNDRET_CREATE; pMe->m_wWndParam = 0; pMe->m_dwWndParam = 0; if (nextFSMAction == NFSMACTION_WAIT) { break; } } } // End CStateMachine_RunFSM
|
pMe->m_pfnState就是我们在应用程序中的状态函数,状态机根据这个函数的返回值nextFSMAction来判断是否暂停状态机的运行。从代码中可以看到当nextFSMAction为NFSMACTION_WAIT的时候这个for循环将被break语句打破。
我们定义的状态机结构体如下所示(下面的代码综合了AppStateMachine.h和.c的内容):
// 状态处理函数返回给状态处理主函数的值类型
typedef enum _NextFSMAction{ NFSMACTION_WAIT, NFSMACTION_CONTINUE } NextFSMAction; typedef NextFSMAction (*PFNSTATE)(IStateMachine *po, void *pUser); typedef struct _CStateMachine{ AEEVTBL(IStateMachine) *pvt; uint32 m_nRefs; // IShell interface
IShell *m_pShell; PFNSTATE m_pfnState; // 状态处理函数
void * m_pUserState; // 用户数据
PFNAEEEVENT m_pfnEvent; // 事件处理函数
void * m_pUserEvent; // 用户数据
int m_eWndRet; // Windows返回值
uint16 m_wWndParam; // Windows返回值word参数
uint32 m_dwWndParam; // Windows返回值dword参数
AEECLSID m_dwAppClsID; // 当前使用此接口的应用程序Class ID
boolean m_bSuspending; // 当前状态机是否被挂起
}CStateMachine;
|
应用程序中将通过一个指针变量来保存一个这样的结构体。我们将创建的用户界面叫做一个Window。状态机使用m_pfnState来获得状态,使用m_pfnEvent来处理用户Window的事件,在m_pfnState状态函数执行的时候创建Window。
8.2.3实现状态机管理的接口
为了能够管理状态机,我们必须提供一些管理状态机的接口:
1、启动状态机,通过这个接口应用程序可以选择在何时启动这个状态机。
2、终止状态机,通过这个接口应用程序可以选择在何时终止状态机的运行。
3、挂起状态机,通过这个接口应用程序可以暂停状态机的运行,同时保存相关的状态数据,以便于恢复状态机的运行。
4、恢复状态机,通过这个接口应用程序可以恢复一个被挂起的状态机继续运行。
5、转移状态,通过这个接口应用程序可以转移到下一个状态去处理。
6、创建一个窗口(Window),通过这个接口应用程序可以创建一个显示界面。
7、关闭一个窗口(Window),通过这个接口应用程序可以关闭一个显示界面,同时设置相应的Window返回值给状态处理函数。
8、Window事件捕获函数,应用程序将通过这个接口将事件传递给Window的事件捕获函数。
9、辅助函数,如获得当前的状态,以及获得当前窗口的返回值等操作。
与上面描述功能基本相近的各个函数已经定义在了FileExplorer应用程序中的AppStateMachine.c的文件中。他们的形式如下:
/*==================================================================== 函数 :IStateMachine_HandleEvent 描述 :状态机的事件捕获函数,在此函数中将事件传递给在当前状态中打开的窗口 参数 :po[in] 指向当前状态机结构体的指针 eCode[in] 当前的事件代码 wParam[in] 事件的16位参数 dwParam[in] 事件的32位参数 返回值 :如果捕获事件则返回TRUE,否则返回FALSE ====================================================================*/ static boolean IStateMachine_HandleEvent(IStateMachine *po, AEEEvent eCode, uint16 wParam, uint32 dwParam) { CStateMachine *pMe = (CStateMachine*)po; return pMe->m_pfnEvent?pMe->m_pfnEvent(pMe->m_pUserEvent,eCode,wParam,dwParam):FALSE; }// End IStateMachine_HandleEvent
/*==================================================================== 函数 :IStateMachine_Start 描述 :开始状态机的运行,状态机开始之后可以调用ISTATEMACHINE_Stop终止状态机 的运行,或者ISTATEMACHINE_Stop挂起状态机 参数 :po[in] 指向当前状态机结构体的指针 pfnState[in] 启动时的状态函数,同时也是状态ID pUser[in] 传递给状态函数的用户数据 返回值 :无 ====================================================================*/ static void IStateMachine_Start(IStateMachine *po, PFNSTATE pfnState, void *pUser) { CStateMachine *pMe = (CStateMachine*)po; pMe->m_pfnState = pfnState; pMe->m_pUserState = pUser; if(pMe->m_pfnState){ CStateMachine_RunFSM(pMe); } }// End IStateMachine_Start
/*==================================================================== 函数 :IStateMachine_Stop 描述 :终止状态机的运行,终止之后只能使用ISTATEMACHINE_Start重新启动状态机 参数 :po[in] 指向当前状态机结构体的指针 返回值 :无 ====================================================================*/ static void IStateMachine_Stop(IStateMachine *po) { CStateMachine *pMe = (CStateMachine*)po; IStateMachine_Suspend(po); pMe->m_pfnState = NULL; pMe->m_pfnEvent = NULL; }// End IStateMachine_Stop
/*==================================================================== 函数 :IStateMachine_Suspend 描述 :挂起当前正在运行的状态机,挂起之后使用ISTATEMACHINE_Start重新启动 状态机,或使用ISTATEMACHINE_Resume恢复当前的状态机运行 参数 :po[in] 指向当前状态机结构体的指针 返回值 :无 ====================================================================*/ static void IStateMachine_Suspend(IStateMachine *po) { CStateMachine *pMe = (CStateMachine*)po; if(pMe->m_pfnState){ pMe->m_bSuspending = TRUE; IStateMachine_CloseWnd(po,0,0,0); } }// End IStateMachine_Suspend
/*==================================================================== 函数 :IStateMachine_Resume 描述 :恢复被挂起的状态机 参数 :po[in] 指向当前状态机结构体的指针 返回值 :无 ====================================================================*/ static void IStateMachine_Resume(IStateMachine *po) { CStateMachine *pMe = (CStateMachine*)po; if(pMe->m_pfnState){ pMe->m_bSuspending = FALSE; CStateMachine_RunFSM(pMe); } }// IStateMachine_Resume
/*==================================================================== 函数 :IStateMachine_GetState 描述 :获取状态机当前的状态 参数 :po[in] 指向当前状态机结构体的指针 返回值 :无 ====================================================================*/ static void* IStateMachine_GetState(IStateMachine *po) { CStateMachine *pMe = (CStateMachine*)po; return pMe->m_pfnState; }// IStateMachine_GetState
/*==================================================================== 函数 :IStateMachine_MoveTo 描述 :转移当前的状态到下一个状态 参数 :po[in] 指向当前状态机结构体的指针 pfnState[in] 状态处理函数,也是状态的ID pUser[in] 状态处理函数使用的用户数据 返回值 :无 ====================================================================*/ static void IStateMachine_MoveTo(IStateMachine *po, PFNSTATE pfnState, void *pUser) { CStateMachine *pMe = (CStateMachine*)po; pMe->m_pfnState = pfnState; pMe->m_pUserState = pUser; }// End IStateMachine_MoveTo
/*==================================================================== 函数 :IStateMachine_OpenWnd 描述 :打开一个窗口,用来显示用户界面和处理用户事件 参数 :po[in] 指向当前状态机结构体的指针 HandleEvent[in] 窗口事件处理函数,也是窗口的ID pUser[in] 窗口事件处理函数使用的用户数据 返回值 :无 ====================================================================*/ static void IStateMachine_OpenWnd(IStateMachine *po, PFNAEEEVENT HandleEvent, void *pUser) { CStateMachine *pMe = (CStateMachine*)po; pMe->m_pfnEvent = HandleEvent; pMe->m_pUserEvent = pUser; (void)ISHELL_PostEvent( pMe->m_pShell, pMe->m_dwAppClsID, EVT_WND_OPEN, 0, 0); }// End IStateMachine_OpenWnd
/*==================================================================== 函数 :IStateMachine_CloseWnd 描述 :关闭当前的窗口 参数 :po[in] 指向当前状态机结构体的指针 eWndRet[in] 窗口关闭时的返回值,状态处理函数通过这个值来决定下一步 怎么做 wParam[in] 窗口返回值的16位参数 dwParam[in] 窗口返回值的32位参数 返回值 :无 ====================================================================*/ static void IStateMachine_CloseWnd(IStateMachine *po, int eWndRet, uint16 wParam, uint32 dwParam) { CStateMachine *pMe = (CStateMachine*)po; if(pMe->m_pfnEvent){ (void)ISHELL_HandleEvent(pMe->m_pShell, EVT_WND_CLOSE, 0, 0); pMe->m_pfnEvent = NULL; pMe->m_pUserEvent = NULL; pMe->m_eWndRet = eWndRet; pMe->m_wWndParam = wParam; pMe->m_dwWndParam = dwParam; // 状态机被挂起,等待状态机恢复或重新Start
if(FALSE == pMe->m_bSuspending){ CStateMachine_RunFSM(pMe); } } }// End IStateMachine_CloseWnd
/*==================================================================== 函数 :IStateMachine_GetWndRet 描述 :获得最近关闭窗口的返回值 参数 :po[in] 指向当前状态机结构体的指针 *pwParam[in/out] 窗口返回值的16位参数 *pdwParam[in/out] 窗口返回值的32位参数 返回值 :int 窗口的返回值 ====================================================================*/ static int IStateMachine_GetWndRet(IStateMachine *po, uint16 *pwParam, uint32 *pdwParam) { CStateMachine *pMe = (CStateMachine*)po; if(pwParam){ *pwParam = pMe->m_wWndParam; } if(pdwParam){ *pdwParam = pMe->m_dwWndParam; } return pMe->m_eWndRet; }// End IStateMachine_GetWndRet
|
如果我们完全使用状态机来构建一个应用程序的话,那么,我们可以在在应用程序的EVT_APP_START事件中直接调用IStateMachine_Start函数来启动状态机,在调用这个函数时需要指定初始状态;在EVT_APP_STOP事件中调用IStateMachine_Stop来终止状态机的运行;在EVT_APP_SUSPEND事件中调用IStateMachine_Suspend来挂起状态机;在EVT_APP_RESUME事件中调用IStateMachiine_Resume来恢复状态机的运行。在状态机运行的过程中,使用IStateMachine_MoveTo来转移到下一个状态,使用IStateMachine_GetState来获得当前的状态。
在应用程序中,表示状态的是一个如下类型的函数:
typedef NextFSMAction (*PFNSTATE)(IStateMachine *po, void *pUser); |
其中,po就是传递了CStateMachine结构体的一个指针,pUser是用户在使用IStateMachine_MoveTo中指定的状态处理函数所需要传入的用户数据。在这个状态处理函数中通常判断当前的窗口(Window)返回值是什么来决定如何处理状态,这些返回值预定在下面的枚举变量中(AppStateMachine.h):
// 窗口返回值
enum{ WNDRET_CREATE = 0, WNDRET_OK, WNDRET_CANCELED, WNDRET_YES, WNDRET_NO, WNDRET_USER1, WNDRET_USER2, WNDRET_USER3, WNDRET_USER4, WNDRET_USER5, WNDRET_MAX };
|
WNDRET_CREATE表示用户要在这个状态中创建一个窗口(Window),此时应该调用IStateMachine_OpenWnd接口打开一个窗口(Window)。调用此接口的时候,窗口(Window)的事件处理函数将接收到一个EVT_WND_OPEN事件。关闭窗口(Window)的动作是由窗口(Window)本身通过调用IStateMachine_Close来完成的,在这个接口执行的过程中将向窗口(Window)事件处理函数发送EVT_WND_CLOSE事件,同时重新启动状态机。启动状态机的时候将再次执行当前的状态处理函数,所不同的是此时执行状态处理函数的时候,窗口(Window)的返回值不同了(在关闭窗口的时候重新替换成了上面的一些定义)。此时根据窗口的返回值决定如何操作。
到此为止您可能有这样的一个疑问,除了CStateMachine外,里面还有一个IStateMachine类型是做什么的呢?在这里呢,我先不说,具体的情况将会在第三篇扩展BREW接口中谈到。当然,有兴趣的读者可以自己试着将AppStateMachine.c和AppStateMachine.h两个文件通读一遍,提前研究一下有好处。在这里唯一要说明的是,要想使用StateMachine的接口函数,需要调用AppStateMachine_New函数来获得IStateMachine的接口指针。
ps:以上内容来自 《深入BREW开发》 作者:焦玉海
阅读(1556) | 评论(0) | 转发(0) |