symbian之初见

Symbian简介

 

Symbian Ownership

    Symbian是一个software licensing公司,由Nokia,sonyericsson,松下,西门子,三星等公司控股,其中nokia股份最高,占到近50%。

Symbian Licensees

    Symbian本身不生产手机设备,它只是将license提供给其他手机设备生产厂商,除控股公司外,其他还有如摩托罗拉,联想,三菱,夏普,明基,富士通等等。

Symbian Devices:

   2003年的Nokia 6600;2004年的Nokia 6630;2005年的Nokia N70,2006年的Nokia 3250等都是经典手机。

Symbian OS (EPOC)

    Symbian是专为移动设备设计的操作系统,具有small memory footprint和low power consumption的特点。

Symbian OS Layers

    自上而下一共有6层,每层以及其特点如下所述:

  • UI Layer:用户界面
  • Application Engine Layer:提供对应用程序所需要的数据的访问
  • System Layer:提供系统所有核心功能
  • Kernel Layer:提供内核级服务,如进程和线程
  • Hardware Adaption Layer:硬件接口层,软件的最下层,与硬件相关。
  • Hardware:物理硬件

UI Design Platforms

  • S60:最通用的用户界面层,为市面上大多数手机所采用,为单手操作而设计。
  • UIQ:为UIQ Technology所拥有,采用此界面的有sonyericsson/Index.html">索爱,摩托罗拉,明基,Arima等。
  • S80:为商务用途所设计,Nokia 9系列手机所采用。
  • Nokia 7710:曾被称作S90,唯一一款。
  • FOMA:专为日本的NTT DoCoMo的3G FOMA网络设计,采用此界面的有富士通和三菱。
  • 下图为S60平台的界面示意图: 

 Symbian platform Evolution

Development Requirements

  • SDK:本教程的实验采用S60第三版的SDK
  • 开发环境:采用Carbide.C++ Express

上述两种工具都可以直接从forum.nokia网站上下载得到,需要先注册成为Forum.nokia的会员。

 S60 3rd Edition SDK

    关于该SDK的一些重要信息:

  • 文档:<EPOCROOT>/S60Doc
  • 示例程序:<EPOCROOT>/S60Ex
  • 模拟器:<EPOCROOT>/EPOC32/release/winscw/udeb/EPOC.exe
  •  API头文件:<EPOCROOT>/EPOC32/include
  • 手机二进制库文件:<EPOCROOT>/EPOC32/release/armv5
  • 模拟器二进制库文件: <EPOCROOT>/EPOC32/release/winscw/udeb

Symbian Projects 

    一个Symbian工程产生一个二进制文件:或者是exe,或者是dll。

    包含以下内容:

  • 头文件:这个不用解释吧。
  • 源代码文件:由编译器生成目标代码,然后通过链接器生成exe或者dll。
  • 资源文件:定义资源的文本文件,生成二进制资源文件。
  • 本地化文件:根据不同语言定义的字符串。
  • 图像文件:同时支持位图(bitmaps)和向量图(scalable vector)。
  • 配置文件:如MMP文件。

==================================================================================

Carbide.C++开发环境

==================================================================================

Carbide.C++ IDE

    基于Eclipse的全功能IDE,与CodeWarrior保持一定的兼容性,分三个版本:

  • Express:入门级开发工具,可免费从Forum.nokia网站获得。
  • Developer:提供更多的工具,以及支持On-Target Debugging。
  • Proffesional:为开发Symbian设备以及高性能应用程序开发者提供。

Carbide.c++基本概念 

  • workbench:桌面开发环境,可以认为是Carbide.c++本身。
  • workspace:Symbian工程的存储目录,是文件系统中的一个文件夹,如c:/carbide。
  • Perspectives:在workbench中的view的位置及分布情况,有Debug,Symbian,C++等perspective切换。
  • views:UI组件,提供浏览信息的一种方式:如工程,调试信息,源代码等。
  • editors:编辑器,用于编辑文件。 

 Creating a New Workspace

    workspace是文件系统中的一个目录,保存了所有Carbide.C++创建的项目、文件夹、以及文件。

    File->Switch Workspace... , 名字中间不能有空格,而且必须和SDK在同一个盘,最好都在C盘。

    File -> New -> C++ Application For S60 Project,然后跟着向导一步一步走就可以了。

Importing an S60 Application

   File -> Import... ,一般选择"Symbian MMP File",“Symbian Bld.inf“可以import多个项目。

Editing Project Properties

    Porject -> Properties, 或者在Build Configuration view里面选择右上角那个"i"按钮,即可弹出project properties dialog。

    Carbide.c++不需要MMP文件,它为你保存所有的设置信息,提供project properties dialog进行修改。

Adding a new link library

    这是Symbian开发过程中最常见的操作之一,可以在project properties dialog中选择WINSCW C/C++ Linker的libraries进行添加。

Building a Project

    选中C/C++ Projects中的当前项目,然后Project -> Build All

Running On The Emulator

    选中C/C++ Projects中的当前项目,然后在Run菜单中选择Run As -> Run Symbian OS Application

 Debugging with Carbide

    当采用Debug而非Run的时候,Carbide会自动切换到调试perspective。

设置断点:双击代码边上的空白栏(marker bar)

在Variables中可以直接看到描述符中所包含的字符串,这个比较有用。

Building for Target

    最后是放到真机上测试。在build configurations view中选择GCCE,然后Project -> Build All就可以了。

    如果要对sis进行签名,可以在project properties dialog的Create Sis中加入cer和key文件的地址和默认密码,这样生成过程就会自动帮你把最后生成的sis签名了。

================================================

基础数据类型和命名规范

================================================

Basic Types

 在Symbian中,很多C++基本类型都被重新定义了,最好使用Symbian的,理由如下:

  • 所有Symbian API都是用的Symbianc重定义的
  • 将来Symbian OS由32位转为64位时,支持性更好
  • 这本身就是Symbian C++ Coding Standards所要求的
 Integers

    typedef signed int TInt;  C++中的signed int,32位,基本用法类似。

    typedef unsigned int TUint;  一般用于计数器(Counter)或者标记(Flags)。

其他Int类型:TInt64, TInt32, TInt16,TInt8; 同时有一份TUint的版本。

Text

 text类型在Symbian编程中基本不用,而一般采用描述符(descriptor)。TText默认是16位的。

Boolean 

    typedef int TBool; 有两个枚举值:ETrue和EFalse。TBool变量最好不要直接和ETure和EFalse比较。如下:

TBool flag = ETrue;
if (flag)  // if (!flag)

    flag = EFalse; 
}

 Floating Point

    对浮点数的支持视处理器而定,如果没有FPU,效率非常低,所以最好是不要用浮点数。 如果一定要用,尽量转化为整数操作。

typedef float TReal32;  typedef double TReal64; typedef double TReal;

TAny

    typedef void TAny;

TAny一般只用作指针,其他情况下用void比较好。

TAny* MyFunction();     void MyOtherFn();

TAny* 在很多Symbian API中都用到了,如:

static TUint8* Copy( TAny* aTrg, const TAny* aSrc, TInt aLength);

Enumerations

enum TState {EOff, Eon, EInit};

Enumeration类型应该以T开头,而枚举值应该以E开头。

TState  state = GetState();
if (state == EOn)
{
   //Do something here

Coding Conventions

    T类:只包含值,而不包含指针以及外部的资源,在栈上分配空间。

TVersion osVersion = User::Version();

    C类:所有需要分配内存的类都必须从CBase继承并且以C开头。

class CExample : public CBase
{
private:
   CDesCArrayFlat* iArray;
}    

CExample* example = new (ELeave) CExample;

    R类:包含指向某个资源的handler。

RTimer timer;
timer.CreateLocal();

    M类:定义一个接口,一般只包含纯虚函数,不包含成员数据,减少类之间的依赖,用来接受回调消息。

class MEikStatusPaneObserver
{
   public:
        virtual void HandleStatusPaneSizeChange() = 0;
}

任何实现MEikStatusPaneObserver接口的类都必须实现HandleStatusPaneSizeChange()函数。

Variable Naming Conventions

  •     成员变量以“i”开头
  •     参数以“a”开头
  •     动态变量随便,以小写字母开头
  •     常量以“K”开头
  •     尽量不要使用全局变量,不能使用全局静态变量。

Functions

  •     函数以大写字母开头,如AddFileNameL();
  •     以D结尾表示deletion of an object
  •     以L结尾表示函数可能leave
  •     以C结尾表示一个item被放到cleanup stack

Casting

    Casting用于在类(classes)和类型(types)之间作转化,Symbian中仍然可以使用C中语法。

    dynamic_cast:不支持,Symbian中没有RTTI。

    static_cast:把一个基类转化为一个继承类。 

TInt intValue = 0xff;
TUint8 byteValue = static_cast<TUint8>(intValue);

    reinterpret_cast:把一个指针类型转化为另外一个指针类型,如integer转化为point类型或者相反。

TUint32 fourBytes = 0;
TUint8* bytePtr = reinterpret_cast<TUint8*> (&fourBytes);
bytePtr++;
*bytePtr = 0xFF; 

    const_cast:移除一个类的const属性。

================================================

内存管理

================================================

Why Memory Management

    Symbian OS本身就是为内存和资源受限的设备开发的,应用程序运行过程中很可能碰到内存用光,或者硬件资源不可用的情况。而这种exceptions是通过修改程序无法解决的,所以遵守以下几条:

  • 尽量不要使用不必要的RAM
  • 尽早释放资源,如文件server等
  • 当你每次申请内存时,都须准备处理out-of-memory错误
  • 当 out-of-memory错误发生时,返回到一个stable的状态,并释放所有期间申请到的资源

 Stack and Heap

    Stack:默认大小8kb,自动删除,如 TInt i = 0;

    Heap :至少0.5Mb,由程序员手动删除,如 CMyObj* obj = new (ELeave) CMyObj;

Leaves

    首先介绍Conventional C++ Memory Management,在Symbian看来,这是非常低效率的。

  • NULL Pointer Checking  if ((myObj = new CMyObj( ) ) == NULL) { //Error Handling }
  • ANSI C++ Exeption Handling   try { //throw an Exception } catch (int e) { //Error Handling }

    在Symbian中推荐采用Leave,如果内存或者资源不能分配到,这个代码就会Leave,沿着Call Stack,直到操作系统或者在某个函数中被Handle掉。

    所有可能Leave的函数最好以L结尾,保证该函数的用户知道这个函数可能Leave。

    Leave的例子:

  • 动态内存分配: return new (ELeave) TUint8[1000];
  • 产生一个Leave:User::Leave(KErrNotFound);
  • 内存不足时Leave:User::LeaveNoMemory();
  • NULL的时候Leave:User::LeaveIfNull(aNotify);
  • 当发生错误时Leave:RFs fs; TInt err = fs.Connect(); User::LeaveIfError(err);

    处理Leave:

    操作系统有默认的处理Leave的方式:

  • 在程序启动过程中:直接关闭应用程序。
  • 应用程序启动后:显示一个错误消息。 

    开发者可以通过trap装置来处理Leave。TRAP(_r, _s)和TRAPD(_r, _s),其中:

  • _r:是一个TInt类型的leave code,默认值为TErrNone。
  • _s:一系列可能Leave的C++ Statements。

        TRAPD(err, DoFunctionL());
        if (err != KErrNone)
        { //Error Handling }
        else
        { //Everything is well }

  The Cleanup Stack

    cleanup stack用于存储在leave发生后需要deallocating的局部变量(指针)。即:当一个函数leave了,所有在cleanup stack上的对象会被全部删除掉。

    Cleanup Stack的使用方法:

CleanupStack::PushL(ptr) :当发生leave时所有内存都会被释放
CleanupClosePushL(handle):当发生leave时这个句柄(handler)会被关闭

CleanupStack::Pop(pointer):第一个元素出栈
CleanupStack::PopAndDestroy(pointer):第一个元素出栈并释放内存

    如果一个函数可能leave,检查一下两种情况:

  • 如果leave了,是否所有在堆(heap)上的元素都在cleanup stack中了
  • 如果没有leave,你是否自己恰当地将他cleanup了

 CMyClass* CMyClass::NewL(TInt aBufSize)
{
   CMyClass* self = new (ELeave) CMyClass;
   CleanupStack::PushL(self);
   self->ConstructL(aBufSize);
   CleanupStack::Pop(self);
   return self;
}

    如果某个函数会在cleanup stack上留下一个对象,那么他必须以C结尾。

Two Phase Construction

    C++构造函数一定不能leave。所有内存和资源的分配应该在第二阶段构造函数ConstructL( )中完成。

编码指南,所有用户定义的C类必须:

  • 定义NewL和NewLC函数为public static
  • 定义ConstructL和C++ Constructor为private

Best Practise

     Construction的规则:

  • 默认的C++构造函数中不能含有可能leave的代码
  • 可能发生leave的函数必须在ConstructL中被调用
  • 如果基类也有ConstructL,必须首先调用,不要忘了explicit scoping

    Destruction的规则:

  • C类必须在析构函数中删除它自己所包含的对象
  • 在删除一个对象后,把它的指针设为NULL
  • 不要删除不是本类所拥有的对象
  • 在reallocation前首先删除对象,并且将其指针设为NULL

    Further Discussion:

  • Preserve Stack Memory:每个进程只有8K,以引用的方式传递参数,大的对象放在堆上
  • Preallocation vs last moment allocation:一般的原则是只在使用前分配资源并且在使用后马上释放。但是                     preallocation的好处是节约处理时间,并且在没有内存的情况下照常运行(资源已经分配到了)
  • where to put trap harness:最基本的情况是依靠GUI应用程序的框架。根据应用的不同,可以自定义粒度。
  • Error Code Returns vs. leaving functions:在执行某个处理前检测是否会出现问题,如下代码:

                                    User::LeaveIfError(fs.Connect()); 

Memory Leaks

    如果你的程序有内存泄露,在模拟器上关闭时会crash。尽早发现并解决你的内存泄露,因为你可以追查到你可能导致内存泄露的代码改动。如果实在找不到,可用下面方法:

    Heap Balance Checking:

  • _UHEAP_MARK
  • _UHEAP_MARKEND

    用上述这两个宏放在你要检查的代码的开头和结尾,如果发生panic,则说明这段代码中发生了内存泄露。可以嵌套使用。 

Panics 

    Panic是一个未经处理的exception,暗示着一个无法解决的错误。

一般程序有以下三类错误:

  • 程序错误:如引用一个超过数组范围的元素
  • 环境错误:内存、磁盘空间不够,或缺少其他资源等
  • 用户错误:输入错误数据

    可以使用trap和cleanup stack技术来解决环境和用户错误,但是对于第一类的程序错误,我们无法恢复,最好是使用User::Panic()函数,它带有两个参数,第一个是string,第二个是Tint。

==================================================

描述符

==================================================

Introduction

    描述符(Descriptors)封装了字符串和二进制数据,用于替代C中的以NULL结尾的字符串。它的长度和数据都封装在了描述符中,Symbian API中用的都是描述符。如:

    TPtrC ptr (KHelloWorld);    CEikonEnv::Static()->InfoMsg(ptr);

 Main Types of Descriptors

    主要可以分为以下几类,其中带C的是不可修改的。 

  • Abstract:(TDes、TDesC),其他描述符的基类,不能实例化,一般用作函数的参数。
  • Literal:(TLitC,_LIT()),用于存储literal string,一般使用后者。
  • Buffer:(TBuf,TBufC),数据存储于栈上,大小在编译时确定。
  • Heap:(HBufC),数据存储于堆上,大小在运行时确定。
  • Pointer:(TPtr,TPtrC),引用存储于类之外的数据

Descriptor Modification

    描述符可以是可修改的和不可修改的,通常带C的都是不可修改的,不可修改的是可修改的基类。   

  • Moidfiable:提供了访问和修改数据的API,如TBuf
  • Non-Modifiable:数据只可以被访问,不可修改。但是通过Des()函数可返回一个可修改的指针

Descriptor Width

    在描述符类后加上8或者16影响了存储在描述符中的数据的宽度,默认是16位的,处理二进制或ASCII时采用8位。

  • 8位:(TDesC8),用于二进制数据或者ASCII字符串
  • 16位:(TDesC16),默认,Unicode

下面开始对上述5类描述符进行详细介绍,首先看一下类继承关系

 

                                            这里显示的是8位的,16位默认的类继承关系与此一致

Abstract Descriptors

    除Literal外的所有描述符的基类,提供了基本的接口和基础功能。他们本身不能实例化,一般用作函数参数。

TDesC:提供了比较、复制、搜索、提取部分字符串的函数。

TInt TDesCUtil::SumLengths(const TDesc& aDesC1, const TDesc& aDesC2)
{
   return aDesC1.Length() + aDesC2.Length();

TDes:继承自TDesC,添加了许多用于修改数据的函数。其最大长度是描述符被创建时确定的。

TInt TDesCUtil::AppendL(TDesc& aTarget, const TDesc& aDesC)
{
   TInt sumLen = aTarget.Length() + aDesC.Length();
   if (aTarget.MaxLength() < sumLen)
   {
      User::Leave(KErrOverflow);
   }
   aTarget.AppendL(aDesC);

 Literal Descriptors

    提供了一种将字符串放在只读存储空间中的机制(实际存放在程序的数据区,而不是真的在ROM中)。一般不采用TLitC而直接采用_LIT()宏。    _LIT(KHelloWorld, "Hello World!");

    通过()操作符可以得到 const TDesC&。 TInt length = KHelloWorld().Length();

    在函数参数为const TDesC&可以直接使用KHelloWorld。iLabel->SetTextL(KHelloWorld);

 Buffer Descriptors

    将数据作为本身的一部分存储在stack上,他们的最大长度是在编译时确定的。  

TBuf<16> helloWorld = KHelloWorld;
TInt len = KHelloWorld().Length();
helloWorld[len-1]='?'; 

在内存中如下所示: 

TBufC的用法如下:

_LIT(KHelloWorld, "Hello World");
const TInt maxBuf = 32;
TBufC<maxBuf> buf;
TInt currentLen = buf.Length(); // == 0
buf = KHelloWorld;
currentLen = buf.Length(); // == 11
TText ch = buf[2]; // == 'l'

 TBuf的用法如下:

const TInt bufLen = 6;
TUInt8 objType = 1;
TUInt8 objId = 1;
TUInt8 xCoord = 128;
TUInt8 yCoord = 192;
....
TBuf8<bufLen> buf;
buf.Append(objType);
buf.Append(objId);
...
//we can now do something with the buffer such as writting it to a binary file or send via socket.

Pointer Descriptor

    用于引用存储在其他地方的数据,如:

const unsigned char KBuffer[ ] = {0x00, 0x33, 0x66, 0x99, 0xbb, 0xff};
TPtrC8 bufferPtr( KBuffer, sizeof(KBuffer));
iSocket.Write(bufferPtr, iStatus);

在内存中如下所示:

TPtr的用法:

 _LIT(KHelloWorld, "Hello World");
const TInt maxBuf = 32;
TBufC<maxBuf> buf;
buf = KHelloWorld;
TPtr ptr = buf.Des();
ptr[7] = 'a';  ptr[8] = 'l';  ptr[9] = 'e';  ptr[10] = 's';  
CEikonEnv::Static()->InfoMsg(ptr); // "Hello Wales"

 Heap Descriptors

     动态在堆(heap)上分配,通过HBufC的API,数据可以被set和reset,但是不能被修改。如:

HBufC* heapBuf = HBufC::NewL(KHelloWorld().Length());
*heapBuf = KHelloWorld();
delete heapBuf; 

     在内存中的情况如下图所示:

    HBufC通常在以下几种情况下使用: 

  •  在运行时从资源文件中加载字符串
  •  从用户界面中接收用户输入的字符串
  • 从应用程序引擎中接收字符串,如contacts database中的名字 

     对HBufC中的内容进行修改:

_LIT(KHello, "Hello!");
_LIT(KWorld, "World!"); 
HBufC* heapBuf = HBufC::NewL(KHello().Length());
*heapBuf = KHello;    //buf holds "Hello!"

heapBuf = heapBuf->ReAllocL(KHello().Length() + KWorld().Length());
CleanupStack::PushL(heapBuf);

TPtr ptr (heapBuf->Des());  //DON'T use TPtr ptr = heapBuf->Des(); this will set maxlen to 6 but not 12...
ptr[KHello().Length() - 1] = ' ';
ptr += KWorld;
iTopLabel -> SetTextL(ptr);
CleanupStack::PopAndDestroy();
DrawNow();

下面介绍Descriptors的具体用法: 

 Non-Modifying Methods

     Length(),Size(),Left(),Right(),Mid(),Compare(),Locate(),LocateReverse (),Find(),Match()等。以下代码示例描述了如何在一个descriptor中找到<>中的内容,如果不存在,返回整个字符 串:

static const TUint KAddressStartChar = '<'; 
static const TUint KAddressEndChar = '>';

TPtrC ExtractAddressNumber( const TDesC& aAddressString)
{
    TInt addrStart = aAddressString.Locate(KAddressStartChar ) + 1;
    TInt addrEnd  = aAddressString.LocateReverse(KAddressEndChar ) ;
    if ((addrStart == KErrNotFound) || (addrEnd == KErrNotFound) || (addrStart >= addrEnd) )
    {
        addrStart = 0;
        addEnd    = aAddressString.Length();
    }
    return (aAddressString.Mid(addrStart, (addrEnd - addrStart) ) );

 Modifying Methods

     Zero(),Copy(),Num(),Format(),Insert(),Replace(),Delete(),Append(),Trim()等。代码示例:

_LIT(KText, "Hello World!");
_LIT(KNewText, "New Text");
_LIT(KReplaced, "Replaced");

TBuf<16> buf1(KText); 
buf1.Delete(6, 6); // length is now 6, leaving "Hello" in the buffer

TBuf<16> buf2(KNewText);
buf2.Copy(KReplaced); // buf2 now contains "Replaced"
buf2.Append(KNewText);  //buf2 now contains "Replaced New Text"

buf2.Delete(99, 1); //Will Cause a PANIC!!! 

 Descriptors in Method Declarations

  • 在函数参数中尽量使用基类
  • 使用中性的描述符,一般情况下使用TDesC而不是TDesC8或者TDesC16
  • 当描述符内容不应该改变时,使用const修饰符 
  • 经典用法:void SetText(const TDesC& aText);    TPtrC Text() const; 

  Character Conversions   

     CCnvCharacterSetConverter类提供了在Unicode和其他字符集编码之间转换的方法。

ASCII本来就是Unicode的一个子集,无须使用该类。和Unicode之间的转换方法如下所示:

TBuf16<64>  UnicodeBuf;
_LIT8(KAsciiStr, "Hello");
UnicodeBuf.Copy(KAsciiStr);

Unicode和拉丁语系之间的转化可使用如下的代码:

 TBuf8<64> Latin1Buf;
_LIT16(KUnicodeStr1, "hello");
_LIT16(KUnicodeStr2, "I have got 10/x20AC.");  ///x20AC is a euro
Latin1Buf.Copy(KUnicodeStr1); //OK
Latin1Buf.Copy(KUnicdoeStr2); //Not as you wanted.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值