本章讨论了BREW编程的一些基本概念,包括Applet、模块、BREW的Class ID、Shell服务、浮点运算等。
Applet和模块总述
BREW中每个应用都是作为一个独立的Window DLL存在的,每个模块可以包括一个或多个小应用 (Applet), 并且必需有一个与之对应的MIF文件,MIF文件是通过MIF编辑器生成的,它包含了关于这个模块的内容信息,比如:支持的类、支持的Applet、Applet权限、以及Applet的一些详细信息(图标、题头)。MIF文件同时也包含了模块中类的唯一的Class ID, 并指出模块中哪点类是可以输出供其它的模块使用。
一个模块可以从BREW资源文件中读取数据,从而使用应用程序可以获取字符串、图像、对话框资源。通过在资源文件存入与语言或设备相关的信息,使用得编写适应不同国家或设备的应用成为可能。资源文件可以通过BREW的资源编辑器来生成二进制资源文件和相应的资源头文件。
开发BREW应用,可以被编辑成可以模拟器和目标设备上运行,用于编辑BREW应用所需的一些头文件和源代码随BREW SDK提供,一些用于生成DLL 特定功能的文件也包含中开发包中,允许开发人员生成应用程序和资源文件,在模块器和目标设备使用的资源文件及其二进制文件格式是相同的。
下面列出了生成一个应用所需的部件:
- BREW SDK提供的AEE头文件。
- BREW SDK提供的用于帮助生成应用的源代码。
- Applet代码和头文件。
- 同MIF编辑器生成的MIF文件。
- 由资源编辑器生成的Applet资源文件及其头文件(可选)。
关于MIF文件
在Applet可以被模拟器调入之前必需创建该模块的MIF文件,MIF文件也必需包含中Applet的发布包中。
在设备或是模拟器启动时,应用程序管理器读取每个MIF文件,以取得包含中该MIF文件中应用程序列表,应用程序管理在屏幕显示该列表(应用程序图标及标题)。
BREW Class ID
BREW中每个Applet或类都有唯一的Class ID与这对应,在BREW中使用一个32位整数来标记公共类或Applet, 可以从BREW开发者Extranet www.qualcomm.com/brew/sdk/classid.取得所需的Class ID。
注意:BREW Class ID只对于授权的开发者开放,如果没有得到授权时,可以使用一些临时Class ID,直到你取得相应的授权为止。
如果手工为类或Applet赋Class ID,必需保证其唯一性,如果一个类或Applet的ID相同,则该类或是Applet不能运行。比如如下的目录结构:
sample/bar.mif
sample/testapp.mif
sample/testapp/testapp.dll
如果bar.mif和testapp.mif具有相同的Class ID,该应用不会被运行,这是因为BREW按照其中目录中出现的顺序来取MIF文件,对于指定Class ID,BREW先调入bar.mif。所以当试图运行该项应用时,BREW在该目录中查找bar子目录但找不到。
BREW中含用两种类的Class ID:接口 Class ID和Applet Class ID。
接口Class ID
所有BREW公用接口具有一个可以唯一标识其类别的Class ID,这些Class ID是BREW保留并不可以被其它的类或Applet使用,当一个Applet通过调用ISHELL_CreateInstance来取得一个指向某个接口的指针时,它使用该接口的Class Id来标明所需的接口,可以参考BREW API查看所有的接口Class ID。
Applet Class ID
所有在BREW平台上运行的Applet也必需有一个唯一的Class ID,在Applet被载入时该Class ID将传给AEE Shell,Shell 随后将使用该Class ID来载入和管理该Applet。
创建实例
BREW提供了一个功能强大的函数ISHELL_CreateInstance用于创建Applet或模块。调用该函数时,BREW从所支持的类列表中查找指定Class ID,该列表包含了BREW提供核心API以及在系统启动从MIF文件中所取得的Applet及类列表。
通过BREW模块支持的IMODULE_CreateInstance函数来创建一个类实例,然而在可以使用该函数前,模块必需被载入和初始化,BREW平台采用一个按需载入的机制,当模块被载入到系统中,BREW通过IModule来创建所需的类实例,直到模块的引用值(reference count)为0时,该模块才从系统中载出,此时BREW将释放该模块的所占内存。
通常情况下,BREW调用ISHELL_CreateInstance只会调用AddRef和Release,而不会调用其它函数,如果一个类被成功创建,将返回一个指向该接口的指针,当调用者不再使用该类时,必需调用该类的Release函数。
特点要注意的所用包含在模块中的对象在创建时要增加该模块的引用值,而在释放该类时,要减小对模块的引用值,从而允许BREW按需要载入载出该模块。
创建Applet
在BREW开发模式下,Applet就是支持IApplet接口的类,允许BREW调用相同的接口函数来管理所有的Applet。IApplet接口只支持HandleEvent函数以及标准的AddRef 和 Release函数。
BREW在两种情况会创建Applet:
- 响应ISHELL_StartApplet函数,从而启动Applet,在这种情况下,将创建Applet并立即发送EVT_APP_START消息,消息EVT_APP_START通知Applet它已处于活动状态可以向屏幕上绘制。
- 响应注册过的通知及告警,在这种情况下,将创建Applet并向其发送通知或告警特定的消息。
处理Applet消息
ISHELL_SendEvent函数用于向Applet发送消息,消息可以是同步也可以异步,发送到Applet的消息可以是键盘事件、对话框事件、控件事件等。
下面是发送到一个Applet消息序列:
EVT_APP_START
…
Other App Events (EVT_KEY, etc.)
…
EVT_APP_SUSPEND (optional)
EVT_APP_RESUME (optional)
…
Other App Events (EVT_KEY, etc.)
…
EVT_APP_STOP
一般情况下,Applet只需处理一些事件,可以包括EVT_APP_START,EVT_APP_STOP, EVT_KEY, 和 EVT_COMMAND.
暂停和恢复运行Applet
按照BREW模式,正常情况下只能有一个Applet处于运行和可见(控制显示器),然而,多个Applet可以以后台方式运行,这并不意味着一个Applet不有被初始化以响应通知、告警或其它消息。
当BREW暂停一个Applet,它将向Applet发送EVT_APP_SUSPEND消息。如果Applet处理该消息时返回TRUE, 表明该Applet已处理过该消息并且没有被调出内存。如果Applet不想处理EVT_APP_SUSPEND消息,可以返回FALSE,在这种情况下,BREW将停止该Applet,将向Applet发送EVT_APP_STOP消息,Applet将被载出内存。
如果在AEEClsCreateInstance()函数中分配内存,而在EVT_APP_STOP中释放该内存是极其危险的。因止在EVT_APP_START分配的内存必需在EVT_APP_STOP中释放,而在AEEClsCreateInstance () 分配的内存,必需在APPFreeData () 释放。
在模拟器情况下,可以先运行一个Applet,然后通过点击模拟器菜单选择一个选项(比如Tool->Settings),此时BREW立即向Applet发送一个EVT_APP_SUSPEND消息,当你在设置窗口点击”Cancel”键时,BREW将向Applet发送EVT_APP_RESUME消息。
BREW开发指南
下面提供的BREW开发指南可以帮助开发人员避免一些常见的错误以及减少用于测试及高度的时间。包括了:
- 从Window平台到ARM环境移植中可能出现的问题。
- 可能在目标设备出现的问题,但却无法在模拟器中检测及出现。
- 一般的通用的好的编码方法。
通过阅读下面的指南可以帮助你减少从Window到ARM环境的移植时间,并最大可能使你的应用在目标设备上成功运行。
开发指南 | 说明 |
进行NULL指针检测 | 有两种情况,如下:
|
避免堆栈溢出 | 不要在堆栈中使用大数组,不要在函数中定义大的数组或变量作为局部变量,这是因为在BREW环境下提供给堆栈使用的内存是有限的,如果需要使用充足的内存(比如超过256字节),使用动态内存分配机制来分配内存(MALLOC或使用IHeap 接口)。 如果是在一个递归过程中分配空间,动态分配内存。 |
不要在循环中占用过多时间 | 如果在循环中占用过多CPU时间可能引用设备复位,所以不要在一个事件处理中占用过多时间,这会阻止其它事件的发送而最终引起设备复位。 当接收到某个事件时,应用只需做必需的事件处理后立限返回,从而可以保证其它事件的发送。 |
开发设备无关的应用 | 为保证所开发的应用可以在不同的设备上运行,而不需关心设备的内存大小、键盘、屏幕大小、颜色深度,使用ISHELL_GetDeviceInfo()来取得关于设备的信息,使用这些信息作为键盘、屏幕大小、颜色深度的参数,而不要在代码中写死。 使用CONVERTBMP例程在不同设备上处理图像。 |
使用资源文件 | 使用资源文件来存放与语言相关的字符串、对话框及图像数据。这允许将应用从一种语言切换到另一种语言。而不要直接写入代码中。 |
释放内存 | 由于设备上所能使用的内存是有限的,必需释放不再使用的内存空间,有两种情形:
高通推荐立限释放不再使用的内存。 |
不要使用全局及静态变量 | BREW框架不支持全局及静态变量,这是因为动态下载下来的应用不能处理这种类型的变量。此外,全局及静态变量在编译时将导致链接错误。在Applet结构中存取持久性数据。 |
不要在定义一个结构时对其初始化 | 从ARM编译器角度来看,定义并初始化一个结构将被视为静态变量。 |
不要在条件语句中对变量赋值 | 避免ARM编译器错误 |
不要使用本地浮点调用 | 原因同“不要使用全局变量”,这将引起类型转换,使用BREW提供的浮点操作函数。 |
使用BREW提供的标准函数库 | 以保证编译后的代码最小,使用BREW提供的标准C函数子集而不要直接调用C函数。 |
避免类型转换错误 | 因为ARM编辑器比其它的编辑处理隐含类型转换时更为严格,请明确指明类型转换,从而可以避免从Window到ARM环境下的编译错误。 |
检测函数返回值 | 当调用BREW API时,如果有返回值,处理成功和失败两种情况。 |
处理错误码 | 因为某些API返回void,如果适合的话,调用BREW 提供的GetLastError()来区别错误代码。 |
使用小写的文件及数据库名称 | 因为高通提供的EFL中文件名是区别大小写的,使用小写的文件及数据名称。 由于已知的原因,不要在文件名中使用两个.,不要在数据名中使用. 。 |
尽可能减小代码 | 为尽可能减小代码,在将应用载入到目标设备前关闭调试信息选项。 |
设置编译告警等级 | 在Window平台上编译时,将告警等级设为最高级,4级,这将帮助你更进一步检测代码。这是因为ARM编译器比Window编译器更为严格。 |
组合多个读写操作 | 为从整体提高应用程序性能,尽可能组合多个读写操作。将数据读入到内存中并根据需要来存取。 |
创建新的应用
当你熟悉了BREW开发环境,并看过了示例程序之后,就可以编写新的BREW应用了。最简单和最快的方式是使用BREW应用开发向导,该向导将帮助创建开发BREW应用所需的各个部件。
语言相关的系统资源
BREW开发环境使用一组用于BREW运行的系统资源,这些系统资源是和设备支持的语言相关的并可以在资源文件AEEControls.bar中取得,该系统资源定义所有BREW接口所需要的资源。
在设备环境中,所有语言相关的资源放置在该语言特定的目录中,目录名称可以从ISO639 中引出来的。
浮点操作
BREW平台不支持浮点操作,如果在BREW SDK中开发并测试带浮点操作的应用时是可以通过的,这是因为底层的平台是Window操作系统,然后设备BREW平台是不支持浮点操作也就不能使用浮点操作,如果必需使用则必需调用BREW提供的浮点操作实用函数。
访问Shell服务
BREW模块和Applet能过根IShell接口来取得所有外部接口访问,每个接口具有一个唯一标识其身份的接口ID, 通过调用ISHELL_CreateInstance () 来取得指定接口的指针。
IShell接口
IShell 接口提供了可用于Applet访问设备操作系统功能一组服务接口。主要包括:
- 取得Shell或设备支持的类。
- 以得设备相关信息。
- 激活或暂停一个Applet。
- 管理定时器。
- 取得系统时间和时间转换。
- 管理对话框以取得用户输入。
- 调入Applet使用的资源。
- 发送事件到Applet或不同的接口。
- 显示信息或提示框。
- 发送告警。
- 注册和查询某个协议和浏览器。
- 为某个接口注册特定的消息。
IBase接口
IBase接口定义所有AEE类的基本结构, 所有AEE类都是从IBase接口派生出来的。
因为所有的AEE对象都是可以动态生成的,所有必需采用某种机制来分配及释放对象,IBase接口提供了两个基本函数:AddRef 和 Release,允许:
- 动态分配接口
- 释放接口
所有的BREW类都必需实现IBase接口函数。
IModule和IApplet接口
IModule和IApplet接口和其它的一些接口不同,它们实现了应用程序应该所供提的服务,而不是由应用程序来调用的接口服务,它们为IShell接口提供了一种机制用于载入,管理,以及将消息发送给应用程序。BREW SDK提供了缺省的IMoudle和IApplet实现。
IModule接口
BREW模型一个基本机制是通过IShell来链接或载入模块,这些模块向外提供了一个入口点用于取得IModule接口,IModule接口提供了两个函数。第一个和最关键的一个是允许BREW从IModule查找指定的接口。第二个则是允许BREW释放模块不再使用的资源。
BREW同时支持静态和动态模块。静态模块由生产商集成到设备中,因此只有设备制造商可以生成静态模块,动态模块可以动态下载到设备中,BREW SDK只支持动态模块。
静态和动态模块都向外输入单一的入口点,这是在模块中被调用或执行的第一个函数,对于静态链接的模块来说,这个入口点用于取得所支持的类和Applet,入口点函数同时也返回模块主调函数的指针。BREW调用这个主调函数来要求模块创建由该模块支持的类对象。入口点函数必需具有唯一的名称,设备才能够管理这个入口函数并将其加入到静态模块列表中。
动态模块只提供了主调函数,这是通过将它放在可模块重分配代码段第一个对象的第一个函数来实现的。BREW可以根据需要来调入这些模块。
IApplet接口
IApplet接口实现由Applet提供的服务,主要是提供了一个机制使得IShell能够将消息发送到Applet。