Symbian笔记.docx
《Symbian笔记.docx》由会员分享,可在线阅读,更多相关《Symbian笔记.docx(18页珍藏版)》请在冰点文库上搜索。
Symbian笔记
第一部分Symbian开发环境
一、开发与编译
1、运行一个已经存在的Symbian程序
1)进入group目录
2)Bldmakebldfiles
3)abldmakefilewinsudeb
?
要搞清楚一下命令的区别,已经symbian安装目录下的文件夹结构
Abldbuildvc6
Abldmakefilevc6
产生VC6的项目文件和工作空间文件(.dsp和.dsw)
Epoc32\Build子文件夹中
Abldbuildwinsudeb//编译针对WINS平台的调试版本
2、建立一个自己的symbian程序
通过symbian自带的工具Applicationwizard
3、将Symbian应用程序安装到手机上
1、进入group
2、Bldmakebldfiles
3、Abldbuildarmiurel
4、进入sis目录
5、修改*.pkg文件中的路径
6、Makessis*.pkg
7、通过蓝牙传输到手机中
二、模拟器
1)模拟器有两种版本:
1、包括符合调试信息
C:
\Symbian\8.0a\S60_2nd_FP2_SC\epoc32\release\wins\udeb\EPOC.EXE
2、发行版本
C:
\Symbian\8.0a\S60_2nd_FP2_SC\epoc32\release\wins\urel\EPOC.EXE
2)在命令行中运行模拟器:
Epro
Epro-rel
三、工程文件
组件定义文件bld.inf
●列出组件中所有的工程mmp
●给出附加的编译命令
项目定义文件*.mmp
●描述工程
●定义资源文件和应用程序附加文件
Bld.inf
//Helloworldbld.inf
PRJ_MMPFILES
//列出项目所需要的.mmp文件
HelloWorldBasic.mmp
Bldmake工具处理当前目录中的组件定义文件(bld.inf),并且生成批处理文件abld.bat和一些批处理生成文件(.make)。
使用bldmakeclean将清除这些文件。
helloworld.mmp
TARGETHelloWorldBasic.app//应用程序的名称,必须具有正确的扩展名
TARGETTYPEapp//文件的类型,app为GUI程序,确定UID1,确定文件是文档还是可执行码
//ChangethesecondnumberheretochangetheUIDforthisapplication
UID0x100039CE0x10005B91//UID2确定文档类型,UID3应用程序标识,唯一
TARGETPATH\system\apps\helloworldbasic//最终生成的应用程序及其组件所在位置
SOURCEPATH..\src//源文件的位置路径
SOURCEHelloWorldBasic.cpp//项目源文件名称
SOURCEHelloWorldBasicApplication.cpp
SOURCEHelloWorldBasicAppView.cpp
SOURCEHelloWorldBasicAppUi.cpp
SOURCEHelloWorldBasicDocument.cpp
SOURCEPATH..\data//资源文件位置路径
RESOURCEHelloWorldBasic.rss//资源文件名称
RESOURCEHelloWorldBasic_caption.rss
USERINCLUDE..\inc//用户头文件位置
SYSTEMINCLUDE\epoc32\include//系统头文件位置
LIBRARYeuser.lib//库文件位置,绝大多数为动态库
LIBRARYapparc.lib
LIBRARYcone.lib
LIBRARYeikcore.lib
LIBRARYavkon.lib
LIBRARYcommonengine.lib
LANGSC//语言
AIFHelloWorldBasic.aif..\aifHelloworldBasicAif.rssc12qgn_menu_demo_cxt.bmpqgn_menu_demo_cxt_mask.bmpqgn_menu_demo_lst.bmpqgn_menu_demo_lst_mask.bmp//指定AIF文件,资源文件,色深,图标文件
//EndofFile
一个计划发布的应用程序还必须提供应用程序信息文件.aif(在开发期间不一定要提供该文件),AIF文件包含图标、所支持语言的可选标题,并指定某些应用程序属性。
如果没有提供AIF文件,应用程序仍然可以运行,只是将取所有系统的默认值
多个SDK的选择:
通过devices命令完成特定SDK的选择
直接运行devices命令将显示当前默认的SDK平台
输入:
devices–setdefault@devicesID来切换不同的设备
在symbian中较小的对象应该放在栈上;所有较大的对象一般都分配在堆上,默认情况下,应用程序的栈大小是8K,堆的大小是1M,可在mmp文件中用EPROCSTACKSIZE来指定栈的大小,EPOCHEAPSIZE来指定堆的大小。
一个最小的GUI应用程序至少需要两个文件:
*.app和*.rsc。
app是可执行文件,rsc是资源编译器产生的二进制资源文件。
为了使系统可以自动识别,所有的GUI应用程序必须遵循一个位置约定—它们必须位于特定的文件夹,例如:
\system\apps\appname\。
,因此,在HelloWorld示例中,应该将前面描述的文件(HelloWorld.app、HelloWorld.rsc、HelloWorld_caption.rsc和HelloWorld.aif)放到名叫\system\apps\HelloWorld的文件夹中。
这个文件夹必须位于c:
或z:
等目标设备驱动器或模拟驱动器的根目录中。
为了正确地实现特定应用程序的功能,可能必须使用其他许多文件,通常将这些必要的文件放在和关键的应用程序文件相同的位置中,及\system\apps\appname\
编译工具自动编译资源文件和AIF文件到\system\apps\appname\目录,并且会在epoc32\data\z保留一个备份。
HelloWorld项目文件和位置
文件夹
内容
\aif
HelloWorld.aif和aif文件的源位图(.bmp)
\data
HelloWorld.rss和HelloWorld_Caption.rss——用于产生HelloWorld.rsc和HelloWorld_caption.rsc的应用程序资源文件
\group
HelloWorld.mmp和bld.inf等平台无关的项目文件
\inc
HelloWorld头文件(.h)和其他通过#include包括的文件,例如HelloWorld.loc和HelloWorld.l01
\sis
通过makesis.exe创建安装文件(.sis)的HelloWorld.pkg包文件。
HelloWorld.sis文件在生成时放于此处
\src
HelloWorld源文件(.cpp)
源文件和头文件
文件名
内容
类模块
注释
HelloWorldApp.h
类和函数声明
应用程序
应用程序创建/初始化
HelloWorldApp.cpp
类和函数定义
HelloWorldAppUi.h
类和函数声明
应用程序UI
命令处理器/控制器
HelloWorldAppUi.cpp
类和函数定义
HelloWorldDocument.h
类和函数声明
文档
拥有数据或应用程序模型
HelloWorldDocument.cpp
类和函数定义
HelloWorldContainer.h
类和函数声明
应用程序的数据视图
显示应用程序的数据
HelloWorldContainer.cpp
类和函数定义
应用程序资源文件
应用程序使用RSS文件(HelloWorld.rss)定义GUI应用程序在屏幕上的显示方式。
定义应用程序外观、行为和功能等信息。
经过资源编译器编译后将生成扩展名为rsc的资源编译文件和扩展名为rsg的头文件,改文件包含一系列符合名/数字常量对,用于标识每一个用户界面元素。
只要引用或使用相关的资源,则必须将该文件通过#include包含在.cpp文件中。
另一个可选的资源文件HelloWorld_caption.rss,用于定义应用程序的标题
其他头文件
HelloWorld.rhr包含可以用于源文件.cpp和资源文件.rss中的任意枚举常量定义。
这些常量特定于某个应用程序,并且一般用于菜单命令、键的处理、视图标识符或对话框中的命令处理。
在带有.rh扩展名的文件中定义UI资源和控件的结构,该结构用于应用程序的资源文件。
控制台应用程序
扩展名为exe,该程序的入口点是E32Main()。
该函数建立内存泄漏宏、清除栈和装置,用于处理callExample()函数中发生的任何异常。
该函数创建一个文本控制台以及从CControlBase中派生而来的控制台类对象,并且调用doExampleL()。
应该在doExampleL()函数中放置或调用测试代码。
文件类型
文件扩展名
描述
.h
C++头文件
.hrh
资源和c++头文件
.cpp
C++源文件
.mmp
Symbian工程文件
.rh
资源头文件
.rss
资源文件
.loc
本地字符串资源头文件
.rsc
编译资源文件(无语言版本)
.rXX
编译资源文件(XX语言代码)
.aif
应用程序信息文件
.app
GUI程序
.lib
库文件
目录结构
模拟器编译后,生成文件在epoc32\release\wins\udeb\z\system\apps\appname
有四个文件*.aif,*.app,*.res,*.res
其中有几个文件备份在epoc32\data\z\apps\appname中
*.aif,*.res,*.res
真机编译后,生成文件在epoc32\release\arm\urel\z\system\apps\appname\*.app
其他文件在epoc32\data\z\system中
手机的盘分c:
e:
存贮卡
z:
rom,存储着操作系统以及所有内置的中间件和应用程序。
Z盘相当于Rom,位于epoc32\release\wins\udeb\z\
C盘相当于Ram,位于epoc32\wins\c被处于激活状态的应用程序和系统本身使用。
4M~32M
在开发时将应用程序放在z:
\system\apps要好于c:
\system\apps7
类的命名约定:
用一个前缀大写字母+类名(每个单词第一个字母大写)。
注意:
仅由静态成员函数组成的类没有任何前缀字母
T类:
简单类。
●没有析构函数,通常没有指针(没有分配给堆的内存)
●没有拷贝构造函数(按位拷贝)
●没有公共基类
●往往分配在栈上
C类:
从CBase类派生
●在堆上进行构造
●有一个虚的析构函数
●有一个重载的new运算符(new(Eleave)),该运算符将所有的成员数据初始化为0.
●有一个默认的拷贝构造函数
R类:
资源客户端句柄
●没有公共基类
●在栈上创建
●要在析构函数中关闭资源
M类:
接口类,没有具体的实现,抽象类,只包含纯虚函数。
●没有任何成员数据,无法实例化
●唯一允许使用多重继承的类
静态类:
完全由不能实例化为对象的静态函数组成,这种类是库函数的有用容器。
数据命名约定:
●所有的类属性(成员数据)带有小写字母“i”前缀
●所有函数的形参都带有小写字母“a”前缀
前缀:
E——枚举常量、K——常量、
基本数据类型:
e32def.h
TInt8、TUint8、TInt16、TUInt16、TInt32、TUint32、TInt、TUint
TReal32、TReal64、TReal
TText8、TText16
TBool
TAny
●TUint8常常用于内存数据
●除非迫不得已,尽量不要使用浮点数。
●总使用Symbian操作系统的typedef,不要使用原来的C++类型,以保持编译器的独立性。
函数命名约定:
●首字母大写,每个单词首字母大写
●异常退出函数以L结尾
●将元素保留在清除栈中以LC结尾
C++中创建对象的安全访问的方法:
CMyClass*myPtr=newCMyClass;
if(myPtr)
{
myPtr->Foo();
}
deletemyPtr;
分配不成功则指针为空
在Symbian中:
CMyClass*myPtr=new(ELeave)CMyClass;
myPtr->Foo();
deletemyPtr;
当分配不成功时将抛出异常,下一条指令将不会执行
注意:
C++的delete操作并不把指针置为0,如果要从类的析构函数外部删除成员对象,必须把成员指针设置为NULL。
异常处理:
1、捕获装置TRAP、TRAPD
Tinterror;
TRAP(error,TestL());
If(error!
=KErrNone)
{…}
Else
{…}
TRAPD=(Tinterror,TRAP(error,函数名))
要注意:
error变量名不能重复;
注意:
一旦一个TRAP将错误捕获,该错误将不会继续传递到上级,除非用Leave
2、用User:
:
Leave(),User:
:
LeaveIfError(),User:
:
LeaveNoMemory()或User:
:
LeaveIfNull()来抛出异常;
Tinterr=TestL();
User:
:
LeaveIfError(err);
User:
:
LeaveNoMemory()不带任何参数,是User:
:
Leave(KerrNoMemory)的高效简写形式,最后,如果传递的指针是NULL,则User:
:
LeaveIfNull()以KErrNoMemory值退出。
3、用清除栈来确保异常发生时清除存储在堆上的任何应用程序资源;
注意:
绝对不要将实例数据(类实例拥有的数据)推入清除栈。
CExample*p=newCEmaple();//无内存泄漏,如果产生异常,则实际没有分配内
存
p->SetCountL(100);//有内存泄漏,如发生异常,则没有析构,造成泄漏
deletep;
CExample*p=newCEmaple();
TRAPD(err.p->SetCountL(100)),由于TRAP开销比较大,这不是最优的解决办法
if(error)
{
deletep;
User:
:
Leave(error);
}
deletep;
新的解决方法
CExample*p=newCEmaple();
//step1把p放在一个可以自动析构的地方
CleanupStack:
:
PushL(p);
p->SetCountL(100);
//step2如果顺利执行到这里,我们把p从自动析构的地方拿出来;没有执行到这也没关系,p会自动被析构
CleanupStack:
:
Pop();
deletep;
CleanupStack:
:
PopDestroy(p);={CleanupStack:
:
Pop();deletep;}
voidFunction2L()
{
CExample*example1=new(ELeave)CExample();
CleanupStack:
:
PushL(example1);
TDate*date1=new(ELeave)TDate();
CleanupStack:
:
PushL(date1);
example1->SetCountL(100);
CleanupStack:
:
PopAndDestory
(2);
}
如果对象是另一个类的成员变量(而不是象x一样的自动变量),那么它会由类的析构函数销毁,因此绝对不应该把成员变量压入清除栈
classCExample
{
private:
CX*iX;
};
CExample:
:
CExample()
{
iX=CX:
:
NewLC();
}
CExample:
:
~CExample()
{
CleanupStatck:
:
PopAndDestroy(iX);
}
CExample*pE=CExample:
:
NewLC();
pE->FunL();
..........
在CExample*pE=CExample:
:
NewLC();执行时,先执行构造函数,ix入栈,然后将pe入栈
结果导致重复删除
---------清楚栈----------
iX;(先析构iX)
pE;(再一次析构了iX)
清除栈上的数组:
p是一个数组指针
CExample*p=newCExample[10];
CleanupStack:
:
PushL(p);
...
CleanupStack:
:
PopAndDestroy();错误:
因为delete
显式的告诉系统,p是数组指针
CleanupArrayDeletePushL(p);
清理时
CleanupStatck:
:
PopAndDestory();
系统用delete[]去"Destroy"p;
清除栈上的R类:
需要能把R类对象推入清除栈,可以有两种选择:
●使用函数CleanupClosePushL();
●直接完成:
产生一个TCleanItem,它由一个指向该R类对象的指针和一个关闭该R类对象的静态函数组成。
RTimertimer;
timer.CreateLocalL();
CleanupClosePushL(timer);
CleanupStatck:
PopAndDestroy();
"Destroy"
timer.Close();
4、两阶段构造对象
注意:
如果提供NewL()或NewLC()函数,良好的习惯通常是,建立私有(或受保护)的对应C++构造函数和ConstractL()。
这可以防止多次调用ConstructL()和覆盖前面已分配成员数据的指针。
同样,私有(或受保护)的C++构造函数可以防止在栈上进行构造,从而防止使用只有部分构造的对象。
私有构造函数可以防止类的派生。
如果希望允许派生,则可以建立受保护的构造函数。
传统的构造
CExample()
{
initL();
}
CExample*p=new(Eleave)CExample//在构造函数中存在异常,发生泄漏
CleanupStack:
:
PushL(p);
p->UnsafeL();
CleanupStack:
:
PopAndDestroy();
修改
CExample()
{
//initL();
}
CExample*p=new(Eleave)CExample
CleanupStack:
:
PushL(p);
p->initL();
p->UnsafeL();
CleanupStack:
:
PopAndDestroy();
我们约定俗成的将二阶段构造函数的名称命名为ConstructL()
staticCExample*Cexample:
:
NewL()
{
CExample*p=new(Eleave)CExample;
CleanupStack:
:
PushL(p);
p->ConstrucL();
CleanupStack:
:
Pop;
returnp;
}
NewL做了两件事:
1、绝对安全的第一阶段标准C++构造(内存没有申请成功不会发生内存泄漏)
2、危险的第二阶段构造ContructL,但是在执行二阶段构造前我们以及将对象指
指针压入清除栈了;
CExample*p=CExample:
:
NewL();
CleanupStack:
:
PushL(p);
p->UnsafeL();
CleanupStack:
:
PopAndDestroy();
staticCExample*Cexample:
:
NewLC()
{
CExample*p=new(Eleave)CExample;
CleanupStack:
:
PushL(p);
p->ConstructL();
returnp;
}
p指针仍然在清除栈上
NewL=NewLC+Pop()
staticCExample*Cexample:
:
NewL()
{
CExample*self=NewLC();
CleanupStack:
:
Pop(self);
returnself;
}
CExample*p=CExample:
:
NewLC();
p->UnsafeL();
CleanupStack:
:
PopAndDestroy();
成员变量用NewL
析构时用delete
如何区分是否为异常退出函数?
如果任意行可以异常退出,并且不是本地捕获这个异常退出,这就是一个异常退出函数。
安全内存管理的原则:
●总是从类的析构函数中删除类拥有的对象
●不要删除非拥有对象(也就是那些你只使用的对象)
●不要分配两次(这会导致内存泄漏)
●不要删除两次(这会破坏堆)
●当在析构函数之外删除的时候,立刻让指针归零
●当重新分配的时候,必须使用“删除、指针归零、分配”序列,防止分配失败;
●使用new(ELeave)而不是简单的new;
●在任何一个可能异常退出的函数名尾部加上L
●只在需要的地方使用捕获,比如,函数不能异常退出而且还必须自己处理错误时
●如果A对象只是被一个自动变量指针引用;B将要调用一个可能在该对象的生存期内异常退出的函数,那么就要把该对象压入清除栈
●绝不要把成员变量压入清除栈,使用iMember命名约定来帮助你定位这样的错误
●让所有基于堆的类名都有C开头,并且让它们从CBase派生而来。
使用CBase去零初始化所有的数据,包括所有指针,利用CBase的虚析构函数进行清理
●绝不要从C++构造函数中异常退出
●绝不要从C++构造函数中分配内存
●将可能异常退出的构造函数放入一个第二阶段构造函数中,如ConstructL()
●使用NewL()和NewLC()来封装分配和构造,而不要显式地调用ConstructL()或者C++构造函数,你可以通过把C++构造函数定为私有函数来强化执行这一点。