使用Applet和模块
在BREW SDK中,每一个应用程序模块做为一个独立的Windows DLL文件开发。每一个模块中可以包含一个或多个Applet,并且必须有一个与此应用程序模块对应的MIF文件。通过BREW MIF Editor(MIF文件编辑器)创建的这个MIF文件中,包含了关于这个模块信息,例如支持的类、支持的Applet、Applet的权限和Applet信息等。在MIF文件中还包含了模块中每一个类和指定给其他应用程序使用的类的唯一Class ID。我们这里所说的类,包含了Applet和扩展接口。
一个模块可以从BREW的资源文件中读取数据,使得应用程序中可以使用字符串、图片和对话框等资源。通过在资源文件中存储指定的语言数据,使得针对不同国家开发不同版本的应用程序成为可能。我们可以使用BREW Resource Editor(资源文件编辑器)来为应用程序创建资源文件,同时生成资源文件中关于资源定义的头文件。
一个已经开发的BREW应用程序可以运行在模拟器上(DLL文件),也可以运行在指定的设备上(MOD文件)。如果需要生成MOD文件,必须包含所运行设备CPU类型的专用编译器,如ARM CPU的C/C++编译器,不过对于普通的开发者来说,获得ARM编译器需要从ARM公司购买软件,这就需要一笔小投资了。建立应用程序的基本的头文件和源文件已经在BREW SDK中提供了,通过这些文件可以创建一个应用程序和资源文件。BREW应用程序使用的资源文件和二进制资源文件,无论应用程序运行在模拟器环境下,还是在设备的BREW环境下,都是使用相同的文件格式,无需在设备和模拟器之间进行不同的处理。
下面就列举出了开发一个BREW应用程序所需的组件:
1、BREW AEE随SDK提供的头文件(在SDK中的inc目录下的.h文件)
2、BREW模块创建所需的助手源文件(AEEAppGen.c和AEEModGen.c)
3、Applet源文件和头文件
4、使用MIF文件编辑器创建MIF文件
5、Applet资源文件和相应的资源文件头文件,这些文件使用BREW资源文件编辑器创建
注意,应用程序中使用的源文件,对于Windows环境和指定设备环境下是相同的,使用同样的源文件去建立Windows的DLL二进制文件和设备指定的MOD二进制文件。
在这一章中,我们将主要根据上面列举出来的,构成BREW应用程序的要素进行一一讲解,期望能够让您对BREW应用程序有一个更加详细的了解。
文件
MIF文件是每一个BREW应用程序必不可少的,其中存储了该模块的详细信息,这些信息包括支持的接口类、支持的Applet类以及Applet的标题图标等内容。在系统启动的时候(模拟器或BREW设备),BREW枚举所有的MIF文件。对于每一个MIF文件,BREW会获取其中全部支持的类。可以通过BREW的API(如ISHELL_EnumApplet等)使这些信息在应用程序中使用,应用程序管理器(Application Manager)可以通过这些API列举出当前系统中的全部BREW Applet。
在开发过程中,BREW MIF文件的命名有严格的要求,主要在以下两个方面:
1、MIF文件名必须使用小写字母做为开头。
2、MIF文件名中至少要包含一个字母,不能全部是数字命名的。
下面的表格中包含了有效和无效的MIF文件名的事例:
有效MIF文件名 |
无效MIF文件名 |
无效原因 |
abc.mif |
Abc.mif |
文件名中使用了大写字母 |
a2c.mif |
123.mif |
全数字文件名 |
a23.mif |
1ab.mif |
第一个不是字母 |
不能够使用大写字母的原因是,在BREW平台上,规定了文件名不支持大小写混合。不过在当前BREW3.x的模拟器上,检测到大小写混合的文件名的时候,只是显示一个警告对话框,但是应用程序是可以在模拟其中运行的。但这不能够保证在实际的BREW设备中也可以运行,因此我们禁止这样的命名方法。
对于首字母是数字或全部数字命名的MIF文件名,是BREW在从ADS服务器上下载应用程序是自动替换和命名的,因此是保留的命名方式。而且BREW对待全数字命名的应用程序有特殊的方式,因此,如果在开发过程或发布过程中使用这种方式命名的MIF文件,可能导致我们的应用程序发生致命的错误。因此,我们应当禁止这种做法。
的Class ID
在BREW平台中,每一个接口类或Applet都必须有一个唯一的Class ID。BREW应用程序开发者可以通过高通网站的页面获得这些Class ID。不过,只有注册的授权用户,同时交纳了Class ID的购买费用之后才可以申请。如果您当前正在开发应用程序,并且还没有注册成为授权用户,您可以使用临时的Class ID进行开发。如果我们手动的为我们的应用程序分配Class ID,那么我们必须保证它们是唯一的。如果有两个或两个以上的接口Class ID或Applet Class ID是一样的,那么这些接口和Applet将有可能不能运行。例如,考虑下面的目录结构:
abc.mif
test.mif
test\test.dll
如果abc.mif和test.mif使用了同样的Class ID,那么test Applet将不能正确地运行。因为,BREW是按照文件名的顺序枚举MIF文件的,因此,对于指定的Class ID,abc.mif会首先被载入。所以,当我们试图启动test应用程序的时候,可能会启动abc这个应用程序,或者什么应用程序也启动不了(如果abc.mif中仅仅输入了接口类的话)。
创建一个接口的实例
在一个模块或Applet中可以使用任何一个BREW的接口,不过在使用这些接口方法之前,需要先创建接口的实例,也就是获得接口的指针。BREW创建一个接口实例的方法是通过调用ISHELL_CreateInstance(IShell * pIShell, AEECLSID cls, void * * ppobj)成员函数,这是一个在BREW中十分有用的API接口函数。在调用这个函数的时候,需要指定创建接口的Class ID,以及接收接口指针数据的接口指针,这个接口指针通过**ppobj的二重指针传入。在这个成员函数执行的时候,BREW会根据指定的Class ID搜索支持类的列表,这个列表由两部分组成:一种是内嵌在BREW平台中的接口,如BREW的标准接口;另一种是系统启动时枚举的MIF文件中支持的类。BREW中调用ISHELL_CreateInstance成员函数的具体执行内容如下:
1、查找支持当前类的模块
2、将这个模块载入内存(如果当前没有载入的话)
3、调用IMOUDULE_CreateInstance()成员函数创建接口实例
如果成功创建了这个类的实例,那么,将会通过参数ppobj返回这个接口实例的指针。在这个接口的使用者不再需要这个接口的时候,必须调用该类的Release方法释放接口实例,同时将接口指针置空。在编写BREW应用程序的时候,创建实例的应用程序一定要负责释放这个实例。
所有的BREW类都是从IBase继承而来的。IBase接口中有两个方法,IBASE_AddRef和IBASE_Release,由于全部的类都是从IBase继承而来,那么,全部BREW类都支持AddRef和Release方法。这两个方法是用来控制接口实例引用计数的。引用计数也就是指当前的类实例有多少个指针指向它(也就是引用它)。当增加一个引用的时候,必须调用AddRef方法增加类实例内部的引用计数,当释放一个引用的时候必须调用Release方法减少引用计数。当内部引用计数为0的时候,就会释放这个类的实例。我们必须严格的遵守这个规则,否则将不能够正确地释放接口实例所占用的系统资源,如内存等,会引起系统资源的耗尽,从而导致系统崩溃。
创建和终止一个Applet
在BREW模式下,Applet是一个支持IApplet接口的类。这使得BREW可以通过一个简单的接口控制所有的Applet。除了标准的AddRef和Release方法外,IApplet接口还支持HandleEvent方法。
BREW可以通过两种方式创建一个Applet:
1、直接调用ISHELL_StartApplet(IShell * pIShell, AEECLSID cls)成员函数。这个成员函数允许创建Applet并同时通过IApplet的HandleEvent方法发送EVT_APP_START事件。EVT_APP_START事件告知BREW Applet现在已经处于激活状态,同时可以刷新屏幕了。
2、注册通知或闹钟事件。在这种情况下,将立刻创建Applet并接收指定的通知事件。如果Applet需要刷新屏幕或接收按键输入时,可以通过调用ISHELL_StartApplet来启动自身。
如果需要终止当前激活的Applet,可以调用ISHELL_CloseApplet(IShell * pIShell, boolean bReturnToIdle)成员函数。调用此方法时,将会终止当前处于激活状态的Applet,同时发送EVT_APP_STOP事件告知Applet:应用程序将被关闭,请释放相关资源。
处理Applet的事件
当一个BREW Applet正在运行的时候(出于激活状态),它将通过HandleEvent函数接收事件。这些事件包括键盘、对话框和控件更改事件。下面是一个Applet的示例事件流程:
EVT_APP_START
…
其他BREW事件(EVT_KEY等)
…
EVT_APP_SUSPEND(可选)
EVT_APP_RESUME(可选)
…
其他BREW事件(EVT_KEY等)
…
EVT_APP_STOP
通常情况下,一个Applet值需要处理几种事件就可以了,包括EVT_APP_START、EVT_APP_STOP、EVT_KEY和EVT_COMMAND。如果在我们的Applet中没有处理一个事件的话,我们应该在应用程序的HandleEvent函数中返回FALSE。这将让BREW采用默认的方式处理这个事件。任何一个BREW Applet的核心函数都是HandleEvent,因为BREW基于事件驱动的机制就是通过这个函数体现出来的,它是BREW Applet的一个事件入口。Applet的运行也是通过这个函数接收事件而运转的。
挂起和恢复Applet
在BREW环境下,同一时刻仅能有一个激活的、顶层可见的Applet。但是,在同一时刻可以有多个Applet处于运行状态。这个顶层可见的意思是这个Applet控制着主显示屏以及接收键盘事件。除了这个顶层可见的应用程序外,其他处于运行状态的应用程序统统处于一个叫做挂起(Suspend)的状态。处于挂起状态的Applet除了主屏显示和接收键盘事件外,其他的操作都可以正常进行。如果需要将处于挂起状态的应用程序激活,处于顶层可见,那么需要调用ISHELL_StartApplet函数,或通过调用ISHELL_CloseApplet函数关闭当前活动的Applet直到需要激活的应用程序为止。
当App1处于顶层可见时,如果App2通过调用ISHELL_StartApplet变为顶层可见,那么,App1将被挂起。当App2关闭时,App1被恢复。挂起和恢复是BREW描述一个Applet是否具有主显示屏和键盘事件控制权的一个机制。
当BREW挂起一个Applet的时候,会向这个Applet发送EVT_APP_SUSPEND事件。如果这个Applet将这个事件的处理结果返回TRUE,则表示告知BREW已经处理了挂起事件,但是,此时的Applet不会从内存中卸载。如果当前Applet不希望处理挂起事件,它可以返回FALSE,这样,BREW将会终止当前的应用程序,并发送EVT_APP_STOP事件,同时在内存中卸载这个应用程序。任何在EVT_APP_START事件中分配的内存,都应改在EVT_APP_STOP事件中释放。任何在AEEClsCreateInstance()函数中分配的内存,需要在应用程序的APPFreeData()函数中释放。如果在内存是在AEEClsCreateInstance()中分配的话,那么在EVT_APP_STOP事件中释放将会是十分危险的。举个例子,如果在EVT_APP_START事件处理中返回了FALSE,那么EVT_APP_STOP事件将不再发送,这样就导致了内存泄露。
在模拟器中,可以通过选区菜单的方式来模拟EVT_APP_SUSPEND事件的发送。例如,选取菜单“工具-〉设置”,则模拟器会向当前的应用程序发送EVT_APP_SUSPEND事件,在关闭这个设置对话框之后,会发送EVT_APP_RESUME事件。
应用程序堆栈和IAppHistory接口
BREW允许有多个Applet同时运行,但是只有一个Applet是激活的。这个时候就牵扯到了BREW Applet管理的问题,这就是BREW应用程序堆栈。通过应用程序的堆栈我们可以:
1、在应用程序历史数据中,允许一个应用程序出现多次
2、即便应用程序处于背景运行装台下(Suspend),也可以使应用程序出现在应用程序历史数据中。
BREW通过IAppHistory接口管理应用程序的历史数据列表,同时允许一个应用程序在堆栈中存在多个历史数据条目。例如,在当前的BREW历史数据中,可以存在如下的应用程序条目序列:
App A -> App B -> App C -> App A -> App B
此时应用程序A和B都在历史列表中有两个条目。此时App B处于激活状态,如果关闭App B,那么,上面的序列将变成下面这样:
App A -> App B -> App C -> App A
这样的处理方式可以使得整个的应用程序序列按原样返回。并不是每一个应用程序每次启动时都会增加历史数据,但是可以保证的是当前每一个正在运行的应用程序都会有历史数据,无论它是处于激活或挂起状态。如果当前使用ISHELL_StartApplet启动的应用程序,没有一个相关的历史数据条目相对应的话,则BREW会在历史数据列表中增加条目。
如果历史列表中该应用程序相关的历史数据已经存在的话,是增加新的历史数据条目,还是仅仅改变当前历史数据的顺序,将遵循如下的规则:
1、如果这个应用程序已经在前面挂起的过程中,通过调用IAPPHISTORY_SetResumeData()设置了恢复数据的话,那么,将会在历史数据列表中新增历史数据条目。此时就是同一个应用程序对应多个历史数据条目的情况。
2、如果这个应用程序已经在前面挂起的过程中,没有调用IAPPHISTORY_SetResumeData()设置恢复数据,那么,将不增加历史数据条目,而只是改变已有历史数据的顺序。由于在BREW3.0版本之前,BREW并没有提供IAppHistory接口,因此这些版本全部采用的是这种处理方式。
在使用非IAppHistory接口的方式关闭应用程序的时候,将会删除与全部已关闭应用程序的历史数据条目。使用IAPPHISTORY_Stop()关闭一个应用程序的时候,不会从应用程序堆栈中移除历史数据,除非这个应用程序处于激活状态。
BREW的IAppHistory接口提供给我们控制BREW应用程序的高级方法,使得我们对BREW应用程序的控制能力进一步加强了(BREW3.0之前的版本是没有这个接口的)。例如,当系统的内存不足时,我们可以通过使用IAPPHISTORY_Stop()方法卸载在内存中的应用程序,而不影响当前的应用程序堆栈。
创建自定义通知
通知事件是BREW提供的一种常用的高级功能。通过这个通知机制,我们可以接收到来自网络、闹钟等模块的事件,而不必采用轮询的方式进行监测,这大大提高了程序的效率。最常见的几种通知类型是通话、短信息、闹钟(闹钟使用的是EVT_ALARM事件,但它与通知事件原理相同)事件。一个应用程序如果要获得这样的通知事件,需要使用ISHELL_RegisterNotify()函数进行事件注册。注册后,当事件产生的条件满足时,BREW将发送EVT_NOTIFY事件给注册的应用程序。不管这个应用程序是否处于激活状态,都能够接收到这个事件。
除了BREW系统的通知事件以外,我们还可以创建自己的通知类。基本步骤如下:
1、编写一个从INotifier接口继承而来的BREW接口类(非Applet),同时实现INotifier接口成员函数。
2、定义接口类所需的通知掩码。
3、实现这个新的接口类。
一旦这个接口类被实现了,那么任何一个BREW的应用程序都可以注册这个接口的通知事件。在这里,我们并没有详细的讲解如何的实现这个接口类,因为这里的目的是让您了解BREW有这样的一个能力,在后面的章节中,我们会详细的讨论如何创建自定义的通知类。
小结
在本章中,主要介绍了使用BREW Applet和模块的方法,首先讲述了BREW应用程序的组成部分MIF文件和Class ID的作用,以及他们的特性。接下来介绍了使用BREW的ISHELL_CreateInstance()函数创建接口实例的方法,让您掌握在应用程序中使用接口的方法。再接下来介绍了创建Applet和处理BREW事件的方法,让您懂得IApplet接口中HandleEvent成员函数的作用。重点介绍了BREW应用程序堆栈的管理方式以及IAppHistroy接口的高级特性。最后告诉您,BREW可以创建自己的通知类。总体上来讲,这章的内容从总体上介绍了一下BREW编程的特性,在下一章里,我们将通过实际的例子来讲解创建BREW Applet的过程。
1、Class ID有什么作用?Applet和接口的Class ID有什么区别吗?
2、MIF文件有什么作用?
3、应用程序堆栈管理中,增加历史数据条目的规则是什么?