Symbian开发决窍.docx
《Symbian开发决窍.docx》由会员分享,可在线阅读,更多相关《Symbian开发决窍.docx(11页珍藏版)》请在冰豆网上搜索。
![Symbian开发决窍.docx](https://file1.bdocx.com/fileroot1/2023-4/21/ed6ff782-1757-4bc3-b8f7-6ce3af8f7a31/ed6ff782-1757-4bc3-b8f7-6ce3af8f7a311.gif)
Symbian开发决窍
设计诀窍
1.最重要的设计诀窍是将‘引擎’代码与用户界面(UI)代码分开,分为不同的模块。
这样设计有助于您的程序在不同用户界面系统之间的移植,SymbianOS本身也采用这种设计。
方法之一是在二进制级就进行这个区分。
将所有非用户界面相关代码放到独立的引擎.DLL文件中。
您的程序中用户界面相关的代码可以链接到这个。
DLL文件,从而访问引擎获取其提供的功能。
另一种方法是在源代码级做出区分。
应用程序生成为一个单一的.APP文件,但将‘引擎’相关代码和用户界面相关代码分到不同的.CPP和.H文件中,以便于管理和调试。
编码时采用这种设计,您可以减轻移植到新的用户界面平台时产生的负担;纯引擎代码经常可以不经修改就在任何用户界面平台上运行。
这意味着移植到新用户界面时您只需要移植和优化已经单独分开的用户界面层就行了。
2.设计时要牢记地方化支持。
千万不要将字符串或文字‘硬编写’到您的源文件中——应使用SymbianOS提供的资源文件机制来储存字符串。
3.应坚持使用软件开发包(SDK)和SymbianOS发布版本明确支持并提供文档的API。
使用不受支持或‘不推荐使用’(deprecated)的API将给您的应用程序带来潜在的问题——Symbian保留在未来的版本中更
改或者移除不应被外部开发者使用的API的权利。
4.不要假定所有的‘系统’文件都会出现在所有电话上。
持有SymbianOS执照的厂商可以灵活的改制其手机;基于同一平台的电话如果由不同的生产商生产很可能包涵不同的支持文件——从而,比如说,如果为您想要播放的音响片段编写固定的地址,此代码将不会保证具有良好的移植性。
您至少应该考虑如果未来的手机没有这个系统文件,您将如何处理出错状态的问题。
编码诀窍
以下是您在编写代码时应该记住的一些一般诀窍。
1.确保您的应用程序能够对系统关机事件做出响应。
在您的AppUi:
:
HandleCommandL()方法中,必须要对EEikCmdExit(以及任何特定平台相关的事件,例如Series60上的EAknSoftkeyBack)做出响应。
2.要对外来系统事件做出响应。
请牢记,您的应用程序是在一个多任务电话系统上运行,您需要将注意力集中于刚获得的/丢失的事件上,以确保当用户获得一个高优先级的通知时您能够做出正确响应。
例如,正打进来的电话会干扰您的应用程序的运行,这时应确保您已保存了系统状态和数据(即:
您需要对标准的“背景”事件采取适当的行动——请参阅SDK)。
一般说来,系统框架会处理这个问题,您不需要采取任何特殊行动——但一定要确保您没有妨碍系统框架的正常操作。
3.内存处理是SymbianOS需要考虑的一个重要课题。
在这一点上,应注意电话有时会不同于模拟器。
因此在将您的应用程序呈交给“Symbian认证签名”进行测试之前,务必确保已在实际电话设备上测试了您的程序。
4.内存堆栈空间有限!
应尽可能将对象放到内存堆中,而不要放到栈里。
KERN-EXEC3异常(panic)发生的主要原因之一就是栈的破坏/溢出。
5.应用程序发生异常(panic)表明您的代码中一定有错误。
以下是一些主要、常见的错误:
•忘记将非对象成员、被分配到堆的变量加到CleanupStack上。
•将成员变量放到CleanupStack上——这一点要千万避免;在析构函数中将这些变量删除就可以了。
•‘重复删除’——例如,没有正确的从CleanupStack上Pop()出已经被销毁的对象,造成CleanupStack以后试图再次删除它。
或者使用过一个对象之后将其删除但忘记将其值设成NULL,从而在析构函数又试图删除一次。
•用可能不存在于您的析构函数中的变量调用函数。
例如,以下代码可能导致异常,因为有可能您在分配内存之前您的对象已经被销毁,或者在应用程序的另一处已经删除了该内存,这样iSomeServer就会处于NULL:
CMyClass:
:
~CMyClass()
{
iSomeServer->Close();
deleteiSomeServer;
}
应该如下编写代码:
CMyClass:
:
~CMyClass()
{
if(iSomeServer)
{
iSomeServer->Close();
deleteiSomeServer;
}
}
•在NULL指针上调用函数。
•函数调用另一个函数,而其使用的变量已经超出范畴,
例如:
把一个栈变量传送到一个异步函数的回调(callback)里。
在系统资源不够的情况下,得体的处理失效情况是非常重要的。
最受限制的资源通常是系统RAM,因此您需要注意正确的处理内存不足的情况。
采用‘两段构造方法’和如下所述的CleanupStack机制,对这种防御性编程来说是必不可少和极其重要的。
7.对带“R”字头、具备Close()方法的类,总是使用CleanupClosePushL()。
这将确保当Leave事件发生时,它们会被恰当的清除。
例如:
RFilefile;
User:
:
LeaveIfError(file.Open(…));
CleanupClosePushL(file);
…
CleanupStack:
:
PopAndDestroy(&file);
对用Release()或Destroy()的‘R’类,亦可使用
CleanupDeletePushL()及
CleanupReleasePushL()来取代Close()。
8.另外,请记住CleanupStack机制是可扩展的,面对所有Leave事件,都可以用它来有效的清除任何对象。
即使您需要处理的是较复杂的情况,也不应该忽略采用正规的清理机制。
欲进一步了解TCleanupItem,请参阅SymbianOSLibrary文档。
9.倘若您意图对HBufC变量重新分配资源,在清除它们之后,总是将其设为NULL。
由于HBufC的资源分配或其再分配可能会导致Leave事件的发生,从而可能会出现析构函数试图删除已经不存在的HBufC变量的情况。
当然,对于任何由堆分配资源的变量而言都应如此,对HBufC变量采取此做法更是已经成为普遍的使用模式。
10.当必须采用自己的TRAP时,请勿忽略所有的报错。
常见的编码错误是:
TRAPD(err,DoSomethingL());
if(err==KErrNone||err==KErrNotFound)
{
//Dosomethingelse
}
这意味着其他错误码都被忽略。
然而,倘若您非用上述模式不可,应采用Leave机制来处理其他错误:
TRAPD(err,DoSomethingL());
if(err==KErrNone||err==KErrNotFound)
{
//Dosomethingelse
}
else
User:
:
Leave(err);
11.不要延误将对象PushL()到CleanupStack上。
所有新创建的对象(成员变量除外)应被立即压入该堆
栈。
例如,下面的作法是错的:
voiddoExampleL()
{
CSomeObject*myObject1=new(ELeave)CSomeObject;
CSomeObject*myObject2=new(ELeave)CSomeObject;…
//Dosomethingherewiththevariables
CleanupStack:
:
PushL(myObject1);
CleanupStack:
:
PushL(myObject2);
//Dosomethingmorewiththevariables…
CleanupStack:
:
PopAndDestroy
(2);
//myObject2,myObject1
}
因为myObject2的创建可能失败,造成myObject1“悬”在那里不能被清理。
应该这样来实现:
voiddoExampleL()
{
CSomeObject*myObject1=new(ELeave)CSomeObject;
CleanupStack:
:
PushL(myObject1);
CSomeObject*myObject2=new(ELeave)CSomeObject;
CleanupStack:
:
PushL(myObject2);
…
//Dosomethingherewiththevariables…
CleanupStack:
:
PopAndDestroy
(2);
//myObject2,myObject1
}
12.注意,那些名称有大写字母C结尾的函数(例如NewLC())会自动把其对象置于CleanupStack。
您不应该自己来将这些对象压入CleanupStack,否则该对象会入栈两次。
当您创建非成员变量并为其分配内存时,这些由C结尾的函数很有用。
13.“两段构造方法”是SymbianOS内存管理的关键部分。
基本原则是SymbianOS中的构造函数或析构函数永远不应该发生Leave。
倘若一个C++构造函数Leave,构造过程未完成的对象得不到清理,因为还没有生成指针指向该对象。
为此,SymbianOS中的构造函数仅将该对象实例化,而后调用该对象的ConstructL()函数,在其中将成员数据实例化。
一旦ConstructL()发生Leave,标准的析构函数将被调用来清除所有至此已被成功分配的成员变量。
在您的编码中照用这一设计模式来防止内存泄漏,至为关键。
当您写每一行代码时,都应该问自己:
“这一行代码能否发生Leave?
”假如回答为“是”,则应考虑“是否所有资源都将被释放?
”。
14.编码中请勿使用_L()宏——而应使用_LIT()。
_L()自SymbianOSv5起已是‘不推荐使用’(deprecated),它的问题在于它将调用TPtrC(constTText*)构造函数,该构造函数会调用strlen()函数来计算该字串的长度。
虽然这不会带来额外的RAM开销,却会在运行时占用更多CPU周期。
相反,宏_LIT()直接创建了一个在编译时就全部实例化的对象,节省了构造TPtrC的CPU开销。
当然,您首先应该考虑的是否应该使用硬编码的字符串常量,因为当您将来地方化(localize)您的程序时,这种常量类型的描述符
(descriptor)可能需要重新编码。
15.当在函数参数中使用描述符(descriptor)时,应缺省使用基类。
在大多数情况下,以constTDesC&形
式来传递描述符。
对可修改的描述符,则应使用TDes&。
16.当在函数中传递或返回对象时,应确保如果您拥有该对象的所有权,您应负责将其清除!
Symbian采取的约定是:
函数中的指针表示所有权转移到调用者,而使用引用则表示被传递对象的所有权仍属于原所
有者。
17.ActiveObjects是SymbianOS的重要特性之一。
请仔细研究SDK文档、SymbianDeveloperNetwork白皮书,以充分理解其工作原理。
下面是一些有用的窍门:
•在RunL()内无需使用TRAP()。
ActiveScheduler本身会TRAP函数RunL()并在其发生Leave时调用CActive:
:
RunError()。
•为此,您应实现自己的RunError()函数来处理从RunL()的Leave事件。
•保证RunL()操作尽可能简短。
长时间运行的RunL()将阻塞其他ActiveObjects。
•总是实现DoCancel()函数,总是在AO析构函数中调用Cancel()。
18.您应尽可能利用ActiveObject框架机制。
对于使用电池供电的设备,在一个循环中紧密不断地进行轮流检测(polling)是极其不适当的,将带来大量耗电。
写游戏时,对此尤需特别注意,详情参阅Symbian
DeveloperNetwork网站的技术文档(
19.ViewSrv11异常对于繁忙运行的程序(例如游戏)是一个潜在的问题。
当您的,或者其他任何程序中的ViewSrvactiveobject不能及时响应ViewServer时就会导致此种异常。
典型的最长回应时间是10-20秒。
FAQ-0900有详细解释,FAQ-0920有针对如何避免此类问题的实用技巧。
二者均可从网页上的SymbianOSFAQ数据库获取。
20.您无需使用HBufC:
:
Des()来进入一个HBufC对象。
只需采用*操作符来为HBufC对象解除引用(dereference)。
这对于向某个接受TDesC&(上文的推荐做法)的函数传递HBufC参数时尤其有用。
21.当使用标准的程序.INI文件的功能时,(即在您的应用UI类中使用Application()->OpenIniFileLC();API时),确保将版本号信息写入流(stream)中。
这样使您能够在未来新版本的程序中建立新的流,意味着即使某个最终用户将来安装您的软件的新版本时,不会因为在旧的.INI文件中找不到正确配置或流时发生异常。
22.在您的程序中实现框架类(frameworkclass)时要小心。
应该始终从所提供的平台相关的框架类中继承。
例如,对UIQ而言,不要从CEikAppUi继承您的AppUi类,而应从CQikAppUi继承。
所有的应用基类(CQikAppUi、CQikApplication、CQikDocument)添加的功能支持更广的框架范围来保证应用程序正确运行。
测试诀窍
1.最重要的测试诀窍是,在用模拟器时(emulator),正确的退出您的程序,而不是仅仅简单地关闭整个模拟器。
在调试模式中,在应用程序框架(applicationframework)的关闭函数前后有内存及句柄检测代码,
当您退出应用程序时,此代码将被调用,从而可检测到是否有内存泄漏或遗留句柄(例如R对象)发生。
对UIQ程序而言,为此目的习惯上在调试模式里提供一个Exit菜单选项。
2.另外一个至关重要的诀窍是在发布您的程序之前确保在.PKG文件中包含了正确的平台相关信息。
相关平台相关信息字符串的详情请参照该平台的SDK。
上的SymbianOSFAQ数
据库中的FAQ-0853提供了有用的相关信息。
3.在写.PKG文件时,也要确保恰当的使用了“!
:
\”语法。
一般来说,您的应用程序应可从最终用户手机中的任何盘中安装、运行。
仅有极少量的文件需要放置于C:
\盘中(例如.INI文件)。
调试诀窍
1.总应先使用模拟器调试;大部分同时发生在模拟器与硬件上的问题,使用模拟器调试会容易得多。
2.在编写和调试新的控制类时,把iEikonEnv->WsSession().SetAutoFlush(ETrue)置于您的AppUi的ConstructL()函数中。
这意味着gcdraw指令会在模拟器中立即显示,而不是在下一次冲刷(flush)视窗服务器客户端缓冲时。
编辑WSINI.INI文件(\epoc32\release\winscw\udeb\system\data\),确保不存在关键字FLICKERFREEREDRAW。
这意味着您可以逐步运行draw代码,并看到每行代码的效果。
然而,应确保此行代码不留在发布的软件中,因为这会影响性能。
3.应定时对源文件运行LeaveScan工具。
该工具可以检测到所有可以Leave的函数,并在其名称不以L结尾时报错,并提醒源文件中潜在的缺陷或疏忽。
在检查哪些代码应被允许Leave,并确保已正确处理此状况时,该工具非常有效。
参见上的SymbianOSFAQ数据库里的FAQ-0291,下载该工具并进一步了解。
4.如果您的程序在正常关闭时因内存泄漏而发生异常,在MSVisualStudio中可将泄漏的地址转换为CBase*类型,从而查看该泄漏对象的类型。
5.最近为对SymbianOS开发者提供了新的功能:
设备上调试(on-targetdebugging)。
虽然不是所有的SDK及工具均已具备此功能,但大多数最近发布的SDK及IDE支持此功能。
如可使用此功能,请在发布您的程序前使用它来检测任何潜在的手机硬件相关的缺陷。
进一步的信息,请阅读SDK及IDE文档。
6.确保‘JustinTime’调试被启用:
•确保在文件“\epoc32\data\epoc.ini”里删除了宏“JustInTime0”
•注册表值做如下设定:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\AeDebug]"UserDebuggerHotKey"=dword:
00000000"Debugger"="\"C:
\\apps\\Metrowerks\\bin\\IDE.exe\"-p%ld-e%ld""Auto"="0"20
7.审阅%Temp%epocwind.out所含的纠错讯息。
8.使用所有可用的开发工具,包括Lint、Leavesan、HookLogger、Panix、D_EXC来检查内存泄漏及其他错误。
这些工具及其他更多有用的工具可在SymbianDeveloperNetwork下载(
9.FAQ-1344教您如何诊断并调试违反平台安全(KErrPermissionDenied)的故障,这有助于识别您的程序所缺少的权限(capability)。
参考读物
[1]SymbianDeveloperNetworknewsletter
[2]SymbianOSFAQdatabase
[3]SymbianOSC++CodingStandardspaper
[4]CodingIdiomspaper
[5]SymbianPress
[6]GamesWritingpaper
[7]ActiveObjectspaper
or
开发者资源
SymbianDeveloperNetwork
SymbianDeveloperNetworkNewsletter
SymbianOSToolsProviders
SonyEricssonDeveloperWorld
ForumNokia
SunMicrosystemsDeveloperServices