Chinaunix首页 | 论坛 | 博客
  • 博客访问: 868371
  • 博文数量: 190
  • 博客积分: 7021
  • 博客等级: 少将
  • 技术积分: 1752
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-17 19:26
文章分类

全部博文(190)

文章存档

2014年(9)

2011年(32)

2010年(149)

我的朋友

分类: 嵌入式

2010-06-11 18:51:50

       在我们使用BREW进行开发的时候,我们必须彻底的了解两个概念:应用程序流程和在我们的应用程序中可以使用的接口。因为这些主题对于BREW开发来说是至关重要的,所以在这一节里将介绍这些内容。如果说前几章让我们窥见了BREW的全貌,那么从这一节开始将正式带您进入BREW的应用程序开发。

7.3.1理解BREW应用程序流程

       正如我们前面所见到的一样,我们的应用程序是基于事件驱动机制的,也就是说,我们的应用程序通过BREW平台发送过来的事件开始运行的。这些事件不但包含了诸如控件发送过来的用户接口事件,而且还包含了描述应用程序诸如启动和停止等外部行为的事件,例如接收一条短消息或者启动一个应用程序。因此,我们的应用程序的中心是一个叫做_HandleEvent的事件捕获函数。应用程序通过这个事件捕获函数获得系统中的事件(更为准确的说法应该是BREW平台通过这个函数将事件传递给应用程序),并通过这些事件检测系统的运行状态(如按键、启动应用程序等等)。进一步的,我们的应用程序判断这些传进来的事件,并决定如何处理这些事件(捕获则返回TRUE,不捕获则返回FALSE)。如果我们的应用程序在某个事件中并不希望进行任何处理,通常情况下应首先将它传递给应用程序正在使用的接口(如控件),然后再返回处理结果。这样可以让应用程序中使用的接口获得处理事件的机会。

       在我们的应用程序可以捕获事件之前,我们首先需要在系统中注册我们的事件捕获函数由于我们的应用程序实际上是一个BREW接口的实例,并且事件捕获函数不过是这个接口中的一个方法而已,因此我们必须在应用程序接口中增加一个指向我们的应用程序事件捕获函数的引用。虽然这并不是一件困难的事,而且是每一个BREW应用程序都必须去做的事情,但是BREW还是提供了一个助手函数用来帮助我们实现这个应用程序接口,包括注册一个事件捕获函数。

       所有的这一切都发生在我们的应用程序载入的时候。当BREW试图载入一个应用程序的时候,它将执行每一个应用程序的AEEClsCreateInstance函数。在我们的应用程序的这个函数中,将判断传入的Class ID是否与我们应用程序的Class ID相同,如果相同则创建一个自身的实例。典型的我们可以通过调用BREW的助手函数AEEApplet_New函数来实现这些功能。这个AEEApplet_New函数将在幕后为我们的应用程序分配存储空间、实现应用程序接口并返回一个我们的应用程序实例。下面就是一个简单的AEEClsCreateInstance函数:

 

int AEEClsCreateInstance( AEECLSID clsID,
                          IShell * pIShell,
                          IModule * po,
                          void ** ppObj )
{
     boolean result;
     *ppObj = NULL;
 
     // 如果Class ID符合我的应用程序...

     if( clsID == AEECLSID_MYCLASSID )
     {
                  // 使用BREW助手函数创建应用程序实例

                  result = AEEApplet_New( sizeof( AEEApplet ),
                                          clsID,
                                          pIShell,
                                          po,
                                          (IApplet**)ppObj,
                                          (AEEHANDLER) HandleEvent,
                                          NULL );
     }
     return result ? AEE_SUCCESS : EFAILED;
}

       以面向对象的思想去考虑,我们可以将AEEClsCreateInstance函数理解为一个创建对象实例的对象工厂,它负责创建一个指定类的实例。在我们的应用程序中实现的AEEClsCreateInstance函数仅仅是一个系统调用的类方法,在其他的应用程序请求启动我们的应用程序时,系统将会调用它。注意,无论我们是在创建一个应用程序或者一个扩展接口(与其它应用程序共享的一个BREW接口),这些启动的处理都是一样的需要通过AEEClsCreateInstance函数,只不过扩展接口不需要注册事件捕获函数,因此不必调用AEEApplet_New函数了。在下一部分一识庐山真面目里,将会详细的剖析这一启动过程,届时将进一步增加我们对BREW平台的理解。

       一旦我们已经创建了我们应用程序的实例,BREW系统将会调用我们应用程序的事件捕获函数,传递各种BREW事件。通常,在我们的应用程序中必须处理以下的几个事件:

       1EVT_APP_START事件。在应用程序启动时,我们在应用程序中注册的事件捕获函数将会接收到这个事件,这表示我们的应用程序已经开始运行了。在我们的应用程序中,可以在这个事件中进行创建接口,或者分配内存空间等操作。

       2EVT_APP_STOP事件。在我们的应用程序结束时将接收到这个事件,表示应用程序已经停止运行了。我们应该在应用程序收到这个事件的时候,释放全部分配的内存和和创建的接口实例等资源。

       3EVT_APP_SUSPEND事件。在我们的应用程序接收到这个事件的时候,它表示应用程序需要中断执行。这种情况通常发生在我们在当前的应用程序中启动了另一个应用程序,或者在我们的应用程序运行过程中收到了一个电话等需要打断当前应用程序运行的情况下。在这个事件中,我们需要保存应用程序中的相关状态数据,用于在应用程序恢复执行时恢复程序的状态。此事件过后,应用程序进入挂起状态。

       4EVT_APP_RESUME事件。在我们的应用程序从中断执行(挂起)状态返回到运行状态时,将会收到这个事件。在这个事件中我们需要根据在EVT_APP_SUSPEND事件中保存的状态数据恢复应用程序的执行状态。此事件之后,应用程序就处于正常的活动状态了。

       下面将介绍一下其他相关的BREW系统事件,对于每一个BREW事件,如果有 wParamdwParam参数,将被传递给给定的小程序和控件。如果我们处理这个事件,需要在事件处理函数中返回TRUE,否则返回FALSE。这些事件如下所示:

事件名称

所属类型

描述

EVT_APP_START

系统事件

启动应用程序的事件,dwParam = ( AEEAppStart * )

EVT_APP_STOP

系统事件

应用程序停止,无参数

EVT_APP_SUSPEND

系统事件

应用程序挂起,无参数

EVT_APP_RESUME

系统事件

应用程序恢复,dwParam = ( AEEAppStart * )

EVT_APP_CONFIG

系统事件

切换应用程序,显示配置界面

EVT_APP_HIDDEN_CONFIG

系统事件

切换应用程序,显示隐藏配置界面

EVT_APP_BROWSE_URL

系统事件

EVT_APP_START之后调用,dwParam = (const AECHAR * pURL)

EVT_APP_BROWSE_FILE

系统事件

EVT_APP_START 之后调用,dwParam = (const AECHAR * pszFileName)

EVT_APP_MESSAGE

系统事件

文本消息,wParam = AEESMSEncodingdwParam取决于 wParam 值的字符串格式

EVT_APP_TERMINATE

系统事件

EVT_APP_STOP的强制版本。小程序将被释放。

EVT_EXIT

系统事件

BREW终止时发送给所有已加载的小程序

EVT_APP_NO_CLOSE

系统事件

应用程序不应关闭

EVT_APP_NO_SLEEP

系统事件

应用程序正在运行 - 运行应用程序很长时间之后调用

EVT_KEY

按键事件

按键事件,wParam = 按键代码

EVT_KEY_PRESS

按键事件

按键按下事件,wParam = 按键代码,发生在EVT_KEY事件之前

EVT_KEY_RELEASE

按键事件

按键抬起事件,wParam = 按键代码,发生在EVT_KEY事件之后

EVT_CHAR

按键事件

字符事件,wParam表示发生事件的宽字符

EVT_UPDATECHAR

按键事件

字符更新事件,wParam表示发生事件的宽字符

EVT_COMMAND

控件事件

自定义控件的事件,wParam表示发生事件的Item ID值。如菜单按下时发生此事件。

EVT_CTL_TAB

控件事件

控件焦点切换事件,dwParam = 控件;wParam = 0-向左顺序切换,1-向右顺序切换

EVT_CTL_SET_TITLE

控件事件

设置标题的消息接口,wParam = IDdwParam = 资源文件(如果 ID != 0)或文本

EVT_CTL_SET_TEXT

控件事件

设置文本的消息接口, wParam = IDdwParam = 资源文件(如果 ID != 0)或文本

EVT_DIALOG_INIT

对话框事件

对话框初始化事件,创建控件,预初始化值、标记和其它项,wParam = IDdwParam = IDialog *

EVT_DIALOG_START

对话框事件

对话框打开事件,wParam = IDdwParam = IDialog *

EVT_DIALOG_END

对话框事件

对话框正常结束事件,wParam = IDdwParam = IDialog *

EVT_COPYRIGHT_END

对话框事件

版权对话框结束事件

EVT_ALARM

Shell事件

闹钟事件,wParam表示闹钟序号

EVT_NOTIFY

Shell事件

通知事件,dwParam = AEENotify *

EVT_BUSY

Shell事件

发送到应用程序以确定是否可以中止或停止该应用程序。

EVT_FLIP

设备事件

设备翻盖事件,wParam = TRUE(打开);FALSE(关闭)

EVT_KEYGUARD

设备事件

键盘锁事件,wParam = TRUE(键盘锁定打开)

EVT_HEADSET

设备事件

耳机事件,wParam == TRUE(已插入耳机,否则为 FALSE

EVT_PEN_DOWN

设备事件

触摸笔按下事件,dwParam = 点笔的位置: 16位表示x坐标,低16位表示y坐标

EVT_PEN_MOVE

设备事件

触摸笔移动事件,dwParam = 点笔的位置: 16位表示x坐标,低16位表示y坐标

EVT_PEN_UP

设备事件

触摸笔抬起事件,dwParam = 点笔的位置: 16位表示x坐标,低16位表示y坐标

EVT_PEN_STALE_MOVE

设备事件

触摸笔事件过多时发送,应用程序通常忽略这个事件。dwParam = 点笔的位置: 16位表示x坐标,低16位表示y坐标

EVT_USER

用户事件

用户定义事件(应用程序私有)

       除了上面的这些BREW系统预定义事件外,在BREW SDKAEE.h文件中还定义了其他的一些事件,有兴趣的读者可以详细的查看这个文件。我们也可以定义属于我们自己应用程序独有的事件,这些事件通常以EVT_USER事件作为起始序号,这样可以避免与系统事件之间的冲突。

       通常,一个事件处理函数的形式如下面的代码所示:

 


static boolean HandleEvent( IApplet *pi,
                       AEEEvent eCode,
                       uint16 wParam,
                       uint32 dwParam )
{
    MyAppTyp * pMe = (AEEApplet*)pi;
 
    // 决定如何处理收到的事件

    switch (eCode){
    // 应用程序启动

    case EVT_APP_START:
        // 进行相关的初始化操作

        return TRUE;
 
    // 挂起应用程序

    case EVT_APP_SUSPEND:
        // 保存数据用于恢复应用程序

        return TRUE;
 
    // 恢复应用程序到运行状态

    case EVT_APP_RESUME:
        // 恢复挂起时的应用程序状态

        return TRUE;
 
    // 应用程序关闭

    case EVT_APP_STOP:
        // 释放资源

        return TRUE;
 
    default:
        break;
    }
    return FALSE;
}


       在事件处理函数中,使用一个switch语句来分发不同的事件,这样的程序既运行高效又划分的清楚。最简单的一个BREW应用程序是由两个函数组成的:AEEClsCreateInstance和一个对应的事件捕获函数。这两个函数共同组成了应用程序的可执行区域,但是,应用程序的数据在什么地方呢?

       BREW平台与其它很多轻量级的应用程序平台一样,都不支持全局变量。这既是一个祝福也是一个诅咒。没有全局变量,我们的应用程序将易于调试和维护;然而,不幸的问题是如果没有全局变量,我们如何存放那些不需要通过函数堆栈传递的数据呢?(不支持全局变量也会引起其他的一些问题,如对于静态变量的管理,C编译器将它同全局变量一样对待,因此也不能够在函数中使用静态的变量。因此我们必需注意在应用程序的任何部分都不能使用静态变量,否则即便可以在模拟器中正常运行,但是也不能在BREW设备上正常运行。)

       做为一种替代全局变量的方法,我们可以定义一个包含应用程序变量的结构体,在这个结构体中我们可以保存应用程序的状态数据以及创建的接口实例指针等全局内容。在运行时为我们的这个结构体分配存储空间,这样在我们的应用程序看来,它就有了存储全局数据的地方了。BREW平台就是这样做的,我们可以在AEEClsCreateInstance函数中调用AEEApplet_New的地方,指定我们自己的结构体来做为创建应用程序实例时的数据结构。为了能够这样做,在我们的应用程序接口体中定义的第一个成员变量必须是AEEApplet结构体,这样就相当于我们的结构体是AEEApplet结构的一个超集,只有这样才不会影响到正常的应用程序实例的创建。一旦AEEApplet_New执行完毕之后,就已经为我们的应用程序结构体分配了存储空间,并为第一个成员变量AEEApplet填充了数据。这样,我们就可以得到如下的一个简单的应用程序代码了:

 

typedef struct _MyAppTyp
{
    AEEApplet a; // 应用程序信息结构体

    uint32 m_launchTime, m_nEvents; // 应用程序指定的数据

} MyAppTyp;
 
int AEEClsCreateInstance( AEECLSID clsID,
                          IShell * pIShell,
                         IModule * po,
                          void ** ppObj )
{
    boolean result;
    *ppObj = NULL;
 
    // 如果Class ID符合我的应用程序...

    if( clsID == AEECLSID_MYCLASSID )
    {
        // 使用BREW助手函数创建应用程序实例

        result = AEEApplet_New( sizeof(MyAppTyp),
                                clsID,
                                pIShell,
                                po,
                                (IApplet**)ppObj,
                                (AEEHANDLER) HandleEvent,
                                NULL );
    }
    return result ? AEE_SUCCESS : EFAILED;
}
 
static boolean HandleEvent( IApplet *pi,
                     AEEEvent eCode,
                     uint16 wParam,
                     uint32 dwParam )
{
MyAppTyp *pMe = (MyAppTyp )pi;
pMe->m_nEvents++;
 
    // 决定如何处理收到的事件

    switch (eCode){
    // 应用程序启动

    case EVT_APP_START:
        pMe->m_launchTime = GETTIMESECONDS();
        pMe->m_nEvents = 1;
        return TRUE;
 
    // 挂起应用程序

case EVT_APP_SUSPEND:
        return TRUE;
 
    // 恢复应用程序到运行状态

case EVT_APP_RESUME:
        return TRUE;
 
    // 应用程序关闭

    case EVT_APP_STOP:
        DBGPRINTF( "Application ran for %ld seconds.",
                    GETTIMESECONDS() - pMe->m_launchTime );
        DBGPRINTF( "Application received %ld events.",
                    pMe->m_nEvents );
        return TRUE;
 
    default:
        break;
    }
    return FALSE;
}

       在这个例子里面,应用程序的结构体中,除了AEEApplet结构外,就包含了两个变量,用来记录起始运行时间和收到的事件数量。这个应用程序记录了接收的事件数量以及运行的时间,在应用程序退出之后通过DBGPRINTF函数在调试窗口中输出这些信息。与前面提供的函数不同的地方在于调用AEEApplet_New函数时分配空间的结构体从AEEApplet变为了MyAppTyp结构体。与此相对应的,HandleEvent函数接收的结构体指针就属于MyAppTyp结构体的指针了。由于AEEApplet结构体定义在MyAppTyp结构体的顶端,因此MyAppTyp结构体指针可以安全的转换为AEEApplet结构体的指针。在应用程序中,由于实际上创建的是一个MyAppTyp的存储空间,因此通过HandleEvent函数第一个参数(IApplet *)传递进来的指针可以转换为MyAppTyp类型,这样我们就可以在应用程序中使用MyAppTyp中的数据成员了。

       经过这种方式处理的应用程序中,既包含了程序的运行代码,又包含了程序的数据存储空间,由此组成了一个完整的可执行应用程序。

 

ps:以上内容来自 《深入BREW开发》 作者:焦玉海

  

阅读(2215) | 评论(0) | 转发(0) |
0

上一篇:brew helloworld程序分析

下一篇:理解BREW接口

给主人留下些什么吧!~~