Symbian OS 开发初级手册

来源:百度文库 编辑:神马文学网 时间:2024/03/29 09:49:56
Symbian OS 开发初级手册 (1) Introduction
Symbian OS是当前应用最为广泛的smart phone操作系统。 Nokia,Panasonic,Siemens,Sony Ericsson的很多手机都是基于这个系统的。 可惜国内关于Symbian OS 开
发的资料实在很少。 所以我打算根据自己的(浅薄)的经验写一套初级教程,让更多的人了解Symbian OS.
说起Symbian OS, 大概很多人都会想到Nokia Series 60, 80等等。 每一个symbian 联盟手机厂商都会在纯粹的Symbian OS上面添加自己的东西。 Series 60 是Nokia
的一个非常流行的软件开发包,7650, 3650, N-gage等等都基于它。我在这里使用的例子都可运行于Series 60. 但是我在这里讲的概念是纯粹的Symbian OS 而不是
series 60, 这样的好处在于:有了这个基础, 我们可以为任何一个使用Symbian OS 的手机开发, 而不会局限于某些Series 60等特殊的API。这样可以使你的开发
成果能够运行于更多的设备上。
那么首先我们需要Series 60 SDK, 在http://www.forum.nokia.com/main/0,6566,034-4,00.html 下载,安装sdk,详细步骤我就不在这里描述了。 如果有什么问题可
以在这里问我。
安装之后,要测试基本路径是不是设置好了。 打开一个命令行窗口,输入 epoc , 如果你看到模拟器运行, 就说明已经安装好了。如果没有的话,请在提问的时候
提供错误信息。
有了SDK,我们就已经可以开始开发了! 但是开发环境是很重要的, 任何Symbian OS程序都要涉及多个文件,Symbain 建议使用 Metrowork Codewarrior, 不过考虑
VC6的普及程度应该更高,我在这里使用Visual C++ 6.0. 你也可以使用VC. Net, 不过目前中文sdk不支持vc7, 你需要下载英文版的。 以后命令行出现vc6的时候你
就要使用vc7。
在讲解我们的第一个hello world程序之前, 我要简单说一下Symbian OS 工程的文件结构, 我们一共需要创建4种文件:
bld.inf 配置文件
*.mmp 工程文件
*.cpp 源代码文件
*.h 头文件
我们的hello world项目中, bld.inf 如下:
PRJ_MMPFILES
HelloWorld.mmp
只有简单的两行, 意思是: 这个配置文件将编译的工程有:Helloworld.mmp. Helloworld.mmp 稍微复杂一些,暂时不在这里讲解,你只需要知道它定义工程中都包括
哪些源文件,哪些头文件,哪些lib文件就可以了。
基本的helloworld例子C:/Symbian7.0s/Series60_v21_C/examples/Basics/helloworld. 如果你的安装路径不同,需要相应改变。 由于实在很简单, 这大概是所有例
子中唯一没有头文件的项目。Helloworld.cpp 如下:
#include "CommonFramework.h"
LOCAL_C void doExampleL()
{
_LIT(KHelloWorldText,"Hello world! ");
console->Printf(KHelloWorldText);
}
在我们讲解Descriptor之前你只需要知道_LIT把 "Hello world! "这个普通字符串转换成了Symbian OS的字符串格式,并储存在变量KHelloWorldText当中。 console
->Printf(..)就是向命令行打印这个字符串。
那么现在我们需要编译这个工程,在命令行中进入examples/Basics/helloworld,输入bldmake bldfiles, 然后abld makefile vc6, 这是创建VC工程文件的基本步骤。
然后打开Visual studio,打开工作区, 在
C:/Symbian/7.0s/Series60_v21_C/Epoc32/BUILD/SYMBIAN/7.0S/SERIES60_V21_C/EXAMPLES/BASICS/HELLOWORLD/HELLOWORLD/WINS 中。
分析一下这个巨长的路径你会发现,其实分为4段:
1。 C:/Symbian7.0s/Series60_v21_C/Epoc32 是全局环境变量%EPOCROOT%的所在,后我们在说这个路径的时候就用%EPOCROOT%代替。
2。build 这个目录存放abld 生成的项目文件。
3。SYMBIAN/7.0S/SERIES60_V21_C/EXAMPLES/BASICS/HELLOWORLD 是你的工程所在目录。
4。HELLOWORLD/WINS 是Helloworld项目, windows 模拟器,如果你为手机编译的话,就会是Helloworld/thumb.
然后运行的时候会弹出一个对话框要求可执行文件。 输入 %EPOCROOT%/release/wins/udeb/epoc.exe 然后你就会看到熟悉的"hello world"出现在s60模拟器上了!
先写到这里吧,但愿这些文字对大家有一点用处。 下一次我将介绍Symbian OS 的基本类型 和 代码规范
[这个blogger不能处理反斜线, 所以我只好用 ‘/‘代替!]
常见问题:
1。各种编译问题
请确定你已经安装:
visual studio 6, Active Perl, Series 60 SDK
建议VC, Series 60, 还有你以后写的代码都放到同一个分区下, 可以省去设置上的一些麻烦。
visual studio 6的路径需要设置, 如果没有的话在 系统->高级->环境变量->Path 中添加,详细请看下边评论。
Symbian OS 开发初级手册 (2)基本数据类型
Symbian OS 使用的是面向对象的C++, 但是又和标准的C++有一些区别。 比如Symbian OS没有标准的异常处理(Exception), 因为设计Symbian OS的时候还C++还没有
把异常处理标准化。 所以Symbian设计了自己得异常处理机制: TRAP, leave. 另外就是今天要讲的基本类型。 Symbian 基本上不使用任何标准的C++基本类型, 众所
周知,不同的C++编辑器对int, unsigned int的长度理解不同, 所以Symbian OS中使用 TInt8,TInt16,TInt32。
但是如果你么没有很好的理由是用某一特定的长度时,要使用TInt. 其他很多类型也遵守这个原则。 比如以后要讲的TBuf8, TBuf16可最好用为TBuf.
类型 描述
TInt8, TUint8 8位 整数
TInt16, TUint16 16位 整数
TInt32, TUint32 32位 整数
TInt, TUint (32位)整数
TReal32, TReal64 实数
TText8, TText16 字符, 相当于 unsigned char, unsigned short int
TBool 布尔
TAny 相当于void
代码规范
Symbian OS 使用很多代码规范, 使用他们可以增强Symbain 代码的可读性, 有些规范甚至是需要严格遵守的, 比如类的命名:
Symbian OS的类一共有6种:
种类 例子 描述
T classes TDesC, TPoint 这个类可以向基本类型一样使用,因为他们通常很小,而且不使用heap所以也没有析构函数
C classes CConsoleBase, CActive 这个类是Symbian使用最多的类,C代表他们从CBase类继承而来, 他们必须有析构函数因为他们的对象创建在heap中
R classes RFile, RTimer R代表资源(Resource),它们只是一个系统资源的句柄,他们本身被创建在Stack上, 但是他们所使用的资源被创建在heap上,使用完毕需
要Close()
M classes MEikMenuObserver 这个类是一个空的接口,使用的时候需要从它继承
static classes User, Math 这个类只有静态函数, 一般都是库函数
Structs SEikControlInfo c - struct
变量命名:
种类 例子 描述
枚举 EMonday,ETuesday E代表枚举
定量 KMaxFileName K代表定量
成员变量 iDevice, iX i代表成员变量
参数 aDevice, aX a代表参数
局部变量 device, x 局部变量没有固定的规范
另外还用函数的命名, 但是要讲过leave的概念后才可以理解,所以这里先不介绍。 这一课很枯燥,但是理清了一些概念,下一课我们来仔细的看一下图形界面的
Helloworld.
Symbian OS 开发初级手册 (3)GUI程序中的4个基本类
图形界面的HelloWorld比文字版复杂了许多! 做过windows图形和命令行程序的人们应该可以很容易的理解这一点。这个Helloworld一共有4个类, 他们是Symbian OS
中任何图形程序都不可少的4个基本类。他们建立了图形界面程序的基本框架, 使得程序员们很容易就知道某些代码应该放在什么地方, 比如绘图的代码就应该放在
View里边, 同样当我们去读一个程序的时候, 如果我们在意的是程序的数据结构,就该去看Document类的实现。
CExampleApplication
Application 类一共有两个作用:第一个是设置这个应用程序的属性, 比如UID, 每一个应用程序都有其独特的UID. const TUid KUidHelloWorld = { 0X10008ACE
};KUidHelloWorld就是这个Helloworld的UID.,AppDllUid() 向高层的框架提供这个应用程序的UID, 框架就用此来识别我们的程序。  另外就是制造一个Document类
的实例, 因此我们只需要两个函数:AppDllUid 和 CreateDocument。CExampleApplication 从 CEikApplication继承而来,很多不需要我们定义函数都被包含在
CEikApplication中
CExampleDocument
Document 类是应用程序的数据模型,如果这个程序是以文件为基础的,它负责程序的文件操作。 我们的Helloworl不涉及到任何文件,但是我们仍然需要这个个类,
因为它还负责制造UI类的实例。
CExampleAppUI
UI类尽管名字是“用户界面“, 他并不是可视的组件,它制造View类的实例, 而它更重要的任务是分派命令和事件, 比如菜单命令就由UI来决定怎样处理,分配到
什么类,什么函数。 又如键盘事件由UI来决定分派到那个控件去处理。HandleCommandL() 这个函数就是用来处理命令的。
CExampleAppView
View 类是可视的控件, 它负责屏幕显示,同时也可以提供相应的函数来响应相关的事件。描绘用的代码就放在Draw函数中
这4个类的声明都放在相应的头文件中, 实现在相应的cpp中,除此之外,你会发现还有一个源文件:Helloworldbasic.cpp。 这个文件是Helloworld的入口, 它的作
用很简单,就是建立一个Application类的实例。 因此大部分图形界面的工程中这个文件都是差不多的,只是Application类的名字要相应改变。
这一课我们讲了Symbian OS图形程序中4个基本的类以及程序的入口。 下一课我们讲Helloworldbasic中的mmp 文件和pkg文件。 然后我们将概述如何生成sis文件,
也就是手机的安装文件。最后我们的helloworld 就可以在支持symbian 的手机上运行了!
习题:
1. 通过设置断点,观察整个程序的流程, 每一个类被创建的顺序, 在纸上划出流程图。
Symbian OS 开发初级手册 (4) mmp, pkg 文件 和 makesis 工具
这一课我们讲解mmp文件,pkg文件, 以及如何用makesis制作sis文件用来安装程序到手机上。
开始之前,有必要提一下Symbian OS程序的目录规范, 你可以把所有头文件,原文件,mmp文件,资源文件等都放到一个目录下, 但是为了更有效的使用,一般把源
文件放在/src下, 头文件放在/inc, bld.inf,mmp文件放在/group, sis, pkg文件放在/sis下。
首先在group目录中找到Helloworldbasic.mmp文件, 它由一系列的属性组成: Target   这个是目标文件名
TargetType  目标类型,也就是目标文件的扩展名, app代表这是一个图形界面的应用程序(application), 文字界面的那个helloworld就是exe 以后还会看到其他的扩
展名如: dll, agt, prt等等
UID   这里有两个UID, 他们用来独特的识别我们的程序,所有的图形界面程序的第一个UID都是一样的:0x100039CE, 第二个是我们的程序独特的UID,需要从
Symbian申请。 但是在开发阶段可以任意使用0x01000000-0x0ffffff这个范围.
TargetPath 目标文件在安装后的路径
SourcePath 源代码路径, 源代码文件可以存放在多个不同的目录里, 在编译的时候编译器会自动到这些目录寻找
Source 源代码文件
Resource 资源文件, 这个大概超出了本教程的范围,因为这部教程不会详细地将深入讲解如何写图形程序
UserInclude 用户头文件路径, 用于存放用户定义的头文件
SystemInclude 系统头文件路径
Library 这些是你的程序编译所需要的库文件
然后我们概述一下pkg文件, 打开Helloworldbasic.pkg,不难看出凡是以 ‘;‘ 开头的都是注释, &EN代表这里使用的语言是英语,
下面一行是应用程序信息#{"HelloWorldBasic"}是应用程序名称, 0x10005B91 是此程序的UID, 1,0,0是版本号.
下面这一行是你开发用的平台信息 0x101F7960代表Series60 v2.0, "0,0,0"是尚未使用的版本号,它必须是"0,0,0", {"Series60ProductID"} 是平台信息描述, 这个
字符串将在用户试图安装程序到一个不兼容的平台上时出现.
再下面是安装文件, 左边的是目标程序在你的机器上的路径,右边是设备上的路径,HelloWorldBasic.APP 是目标文件, HelloWorldBasic.rsc 是编译过的资源文件.
有了以上的基础,我们就可以编译sis文件了. 一共分为2步:
1. 进入到/group目录下, 输入bldmake bldfiles, 和以前讲过的作用一样, 然后: abld build thumb urel, "abld build"是编译命令, "thumb urel"是编译目标, 一
般手机的格式都是thumb, 用命令行为windows编译的话,就可以用 abld build wins udeb (urel). "udeb" 代表debug build, "urel"代表release build, 前者用于
开发,后者用于发行。
2. 进入/sis, 输入makesis Helloworldbasic.pkg, 然后Helloworldbasic.sis就被创建了。
下面就可以用蓝牙,或者usb等通讯手段将这个sis文件传送到你的symbian 手机上了. 安装,执行, 好运!:)
Symbian OS 开发初级手册 5 - Leave
leave (不知该怎么翻译,就先叫离开吧) 这个词我们在第二课提到过, 知道它是Symbain版本的异常处理。 这一课我们详细讲解它的意义和用法。 首先我们要明白
为什么要离开? 当程序运行到一个地方无法正常继续的时候, 比如内存耗尽, 或者当前状态无法进行某个操作的时候我们不能简单的退出程序,或者忽略这些错误。
前者使得界面不够友好, 而后者可能导致不可估计的问题。 所以我们需要在这一点上离开。 让上一层的使用我们这个函数的代码去处理我们遇到的错误。 上一层代
码可以根据但是的上下环境决定该如何处理这个错误。
离开的基本用法:
假设我们有一个函数提取一个数组的值:
TInt GetL(TInt aIndex)
{
if(aIndex < 0 || aIndex >= KArraySize)
{
User::Leave(KErrArgument);
}
else
{
return iArray[aIndex];
}
}
要使用这个函数, 我们可以:
TInt x, err;
TRAP(err, x = GetL(5);)
if(err != KErrNone)
{
//输出错误信息等等
}
...
首先注意函数名,这是一条规范: 凡是有可能离开的函数,名称都要以L结尾。这包括
在函数实现中使用任何形式的Leave (Leave, LeaveIfNull, LeaveIfError等等)和可leave的函数, 而又没有TRAP掉他们的的函数。
以上的GetL函数当aIndex不在合法范围内的时候就会leave, KErrArgument是其原因(错误的参数)。 使用GetL函数的时候我们可以使用TRAP宏来捕捉离开, 所以当
GetL 离开的时候, 就会输出错误信息等等。 使用GetL的代码并不一定需要TRAP只要把他自己声明为可离开的函数就可以, 但是要保证终归这些函数要被TRAP掉。
图形界面程序的框架在程序入口处提供了TRAP所以我们可以使用离开而不用TRAP一些普遍性的问题(比如内存不足)。但是文字程序一般需要我们自己提供TRAP.
另外还有一点就是, TRAP这个宏必须在一个CTrapCleanup的实例被创建以后才可以使用。 文字Helloworld中使用了头文件commonframework, 在
Examples/basics/commonframework目录下, 我们可以看到:
CTrapCleanup* cleanup=CTrapCleanup::New(); // get clean-up stack
TRAPD(error,callExampleL()); // more initialization, then do example
//TRAPD是TInt error; TRAP(error,callExampleL()); 的缩写版。
delete cleanup; // destroy clean-up stack
也就是说当你的进程中还没有CTrapCleanup实例的时候, 你需要创建它才可以使用TRAP,当你不再使用TRAP的时候就删除这个实例。
有了离开这个基础,我们下一课就可以讲CleanupStack 和 二层构造的概念了。 之后大家就不会觉得Symbian 程序中实例的构造看起来很复杂了。
习题:
1。 在Helloworld中创建一个可leave的函数, 并在doExampleL()中调用,看看不TRAP它的结果是什么。(也就是依靠commonframework中的TRAP) 然后再doExample
中TRAP它, 看看结果有什么不同?
Symbian OS 开发初级手册 6 CleanupStack and Two-phase
在创建实例的时候Symbian C++使用 new (ELeave) 来代替普通的 new , 比如:
CBar *pBar = new (ELeave) CBar;
这个用法是考虑到手机特殊的属性:内存一般较小, 经常会有内存不足的情况出现。 在pc上如果内存不足我们就会退出程序, 但是在手机不能那样频繁的退出, 所
以内存不足被划分为异常, 需要离开.  下面这个函数就会在内存不足的时候在A行离开:
void FooBarL()
{
CBar *pBar = new (ELeave) CBar;  //A
User::LeaveIfError(pBar->Foo());  //B
delete pBar;     //C
}
离开 作为异常处理的机制, 存在着一个问题。如果上面这个程序在B行Foo()返回了一个错误值,就会在那一行离开,但是系统为pBar指针分配的内存也就泄漏了。为
了解决这个问题, Symbian 程序中频繁使用CleanupStack(清洁栈). 它的典型使用方式如下:
void FooBarL()
{
CBar *pBar = new (ELeave) CBar;  //A
CleanupStack::PushL(pBar);
User::LeaveIfError(pBar->Foo());  //B
CleanupStack::Pop();
delete pBar;     //C
}
他的作用就是, 在B行之前, 把pBar指针放到清洁栈上, 一旦B行离开了,清洁栈就会自动删除pBar回收内存, 如果B行顺利通过, 就可以通过pop把pBar拿下来了

CleanupStack 有效地解决了这个潜在的内存泄漏问题, 但是这个方法在一个特殊的情况下不适用。 那就是如果一个类的构建函数离开, 那么new 为它分配的内存就
会泄漏。 (构建的顺序是:系统分配内存,然后运行构建函数)
这个问题就导入了下一个概念:two-phase construction (二层构建)
我们的目标是:构建函数在任何情况下不可以离开! 要实现它就必须:
1。 不在构建函数里使用任何L函数
2。 不在构建函数里分配内存
凡是需要以上两个操作的都放到第二层构建函数中:ConstructL. 要把两层构建结合起来,我们需要另一个静态函数,一般为NewL或者NewLC. L大家都知道代表离开,
C代表清洁栈, 下面会详细讲解NewLC的方便之处。(在这里向大家推荐一个很不错的英文Symbian 资料网站:www.newlc.com :)
二层构建的基本模式就是:
CHelloWorldBasicAppView* CHelloWorldBasicAppView::NewL(const TRect& aRect)
{
CHelloWorldBasicAppView* self = CHelloWorldBasicAppView::NewLC(aRect);
CleanupStack::Pop(self);
return self;
}
CHelloWorldBasicAppView* CHelloWorldBasicAppView::NewLC(const TRect& aRect)
{
CHelloWorldBasicAppView* self = new (ELeave) CHelloWorldBasicAppView;
CleanupStack::PushL(self);
self->ConstructL(aRect);
return self;
}
void CHelloWorldBasicAppView::ConstructL(const TRect& aRect)
{
// Create a window for this application view
CreateWindowL();
// Set the windows size
SetRect(aRect);
// Activate the window, which makes it ready to be drawn
ActivateL();
}
先看NewLC函数, 它创建一个CHelloWorldBasicAppView的实例, 然后把他放在清洁栈上,然后调用它的第二层构建函数(可以离开的), 然后并没有把实例从清洁
栈上取下就返回了。这样的好处就是如果我们用NewLC创建一个对象,然后调用它的可以离开的函数就不必把它放到清洁栈上,因为它已经在上面了。NewL的功能和
NewLC 是一样的,只是在返回实例前把它取下清洁栈。一般的C类都提供NewL,不是所有的C类都会提供NewLC.
使用清洁栈需要注意:
1. 成员变量一般不需要放在清洁栈上, 因为当执行可离开的成员函数时,它们所在的类的实例本身就应该放在清洁栈上。 所有的成员变量所占的资源都和此实例共
存亡。
Symbian OS 开发初级手册 7 Descriptors
本文简介Descriptor。 Symbian OS 中不使用我们所熟悉的c 字符串(char*) 或者c++ 的string。 Symbian 的字符串是通过descriptor实现的。我们的第一个Symbian
程序,文字版Helloworld中是这样使用的: _LIT(KHelloWorldText,"Hello world!n");
console->Printf(KHelloWorldText);
_LIT()是一个宏,它声明了一个descriptor:KHelloWorldText, 其内容是"Hello world!"。下面console->Printf的参数就是一个descriptor.
Descirptor是一个灵活的设计, 它充分考虑到小型设备的各种局限性。 为此提供了很个类,有些是可以修改的,有些是不能修改的, 有些放在stack上,有些放在
heap上。 NewLC网站上有一个descriptor的结构图:http://www.newlc.com/article.php3?id_article=12 。
其中包含了八个类, 这些并不是全部的descriptor类, 但是已经足够我们课程目前的使用。其中TDesC是最基本的类。 其他所有的descriptor类都是由它继承而来。
TDes是最基本的可以修改的类。凡是以C后缀的类都是不可修改的。
结构图下面是5各最常用的descriptor类的内存示意图。
TBufC<5> 中的5是它的长度, 它表示的是"NewLC"这个字符串, 是不可更改的。
TBuf<8> 8是它的最大长度, 而当前只使用了5个字节,它的内容是可更改的,但是注意内容长度不可以大于他的最大长度
TPtrC 是一个descriptor 指针类, 它是一个不可修改的指针, 指向不可修改的"NewLC"的地址。
TPtr 是一个可修改的descriptor指针类, 指向可修改的"NewLC"的地址。
HBufC 的H代表Heap,是专门用来在Heap上创建字符串, 其他的descriptor类的字符串一般都放在stack上。
一般来说要尽可能的使用heap内存, 因为stack的内存要更有限, 如果在stack上访大量的字符串就很容易导致内存不足。
这些类都由很多函数来使用,修改,查询他们的字符串, 大家可以参考Developer Library中的API Reference.
_LIT()还有一个更简单的版本, helloworld中的两行可以改写为:
console->Printf(_L("Hello world!n"));