一般诀窍
1.‘Symbian 开发师网络’提供大量信息资源,协助您为Symbian OS 操作系统编写应用程序。您应经常访问
www.symbian.com/developer 网站以便获取最新的SDK、技术资料、编码范例和白皮书。您从开发师网络网站能够获得:
• Symbian OS‘常见问题’(FAQ) 库。这是一个对开发者来说极有价值的信息库,涵盖了最常见的设计和编码
问题。
• 许多白皮书。其中包括《Symbian OS C++ 编码标准》。在Symbian OS 本身的开发过程中,Symbian 设定了
若干重要的编码习惯方法和规范。《编码习惯方法白皮书》向外部程序员解释这些习惯方法。通过采用这些约定俗成并经过试用和验证的规范,您可以通过开发Symbian OS 软件受益到Symbian OS 在自身开发中获得的宝贵经验。
• Symbian OS Developer Library,标准API 参考文档、Symbian OS 指南。
• 下载应用程序范例、应用程序库、属于Symbian OS但不一定被装到所有手机上的‘可选’系统API、使较新Symbian OS 版本上的API 能够在较旧版本上运行的移植程序库等等程序工具。
• 关于调试工具和开发工具方面的信息。
• 新闻组论坛
(www.symbian.com/developer/leters/forum.html),使您能够从Symbian 工程师和更广的开发者团体那里获得指导。有各种不同论坛,从针对C++ 和Java 的具体问题、到硬件和Symbian 签名等等,涉及到开发工作的各个方面。
2. 注册并订阅Symbian 开发者网络期刊(www.symbian.com/developer/letters/index.html) ——该期刊通过电子邮件每月发布一次, 是了解与Symbian 开发者相关的最新消息的最佳途径。
3. 采用Symbian OS 的移动电话生产商也拥有自己的开发者项目。您也应该在他们的网站注册,以便获取针对某种具体型号的电话的最新信息和诀窍。
4. 获得认证!(www.symbian.com/developer/academy/index.html)《Symbian 认证程序员》证书能够向雇主显示您是一名合格的Symbian 工程师,并有助于客观地评估您所掌握的Symbian OS 相关技术的各项能力。
5.‘Symbian 签名’(Symbian Signed)旨在推广设计Symbian OS 应用程序的各种最佳范例。通过Symbian
签名的应用程序均遵守行业认同的质量标准并迎合网络运营商对签名应用程序的要求。请到‘Symbian 签名’网站(www.symbiansigned.com)获取关于Symbian 签名的更多信息。
6. 除了Symbian 和相关生产商之外还有一个活跃的开发者社区。请访问www.symbian.com/developer/about/trdprtysites.html以查找众多知名的第三方开发者网站的链接。
7. 最后,现在关于Symbian OS 的出版物日益增多。Symbian 出版社(www.symbian.com/developer/books/index.html)出版的一系列Symbian OS 相关主题的书籍,目的都是帮助您更轻松的编写更可靠的Symbian OS 代码。
设计诀窍
1. 最重要的设计诀窍是将‘引擎’代码与用户界面(UI)代码分开,分为不同的模块。这样设计有助于您的程序在不同用户界面系统之间的移植,Symbian OS 本身也采用这种设计。
方法之一是在二进制级就进行这个区分。将所有非用户界面相关代码放到独立的引擎.DLL 文件中。您的程序中用户界面相关的代码可以链接到这个。DLL 文件,从而访问引擎获取其提供的功能。另一种方法是在源代码级做出区分。应用程序生成为一个单一的.APP 文件,但将‘引擎’相关代码和用户界面相关代码分到不同的.CPP 和.H 文件中,以便于管理和调试。编码时采用这种设计,您可以减轻移植到新的用户界面平台时产生的负担;纯引擎代码经常可以不经修改就在任何用户界面平台上运行。这意味着移植到新用户界面时您只需要移植和优化已经单独分开的用户界面层就行了。
2. 设计时要牢记地方化支持。千万不要将字符串或文字‘硬编写’到您的源文件中——应使用Symbian OS 提供的资源文件机制来储存字符串。
3. 应坚持使用软件开发包(SDK)和Symbian OS 发布版本明确支持并提供文档的API。使用不受支持或‘不推荐使用’(deprecated)的API 将给您的应用程序带来潜在的问题—— Symbian 保留在未来的版本中更改或者移除不应被外部开发者使用的API 的权利。
4. 不要假定所有的‘系统’文件都会出现在所有电话上。持有Symbian OS 执照的厂商可以灵活的改制其手机;基于同一平台的电话如果由不同的生产商生产很可能包涵不同的支持文件——从而,比如说,如果为您想要播放的音响片段编写固定的地址,此代码将不会保证具有良好的移植性。您至少应该考虑如果未来的手机没有这个系统文件,您将如何处理出错状态的问题。
编码诀窍
以下是您在编写代码时应该记住的一些一般诀窍。
1. 确保您的应用程序能够对系统关机事件做出响应。在您的AppUi::HandleCommandL()方法中,必须要对EEikCmdExit(以及任何特定平台相关的事件,例如Series 60 上的EAknSoftkeyBack)做出响应。
2. 要对外来系统事件做出响应。请牢记,您的应用程序是在一个多任务电话系统上运行,您需要将注意力集
中于刚获得的/丢失的事件上,以确保当用户获得一个高优先级的通知时您能够做出正确响应。例如,正打进来的电话会干扰您的应用程序的运行,这时应确保您已保存了系统状态和数据(即:您需要对标准的“背景”事件采取适当的行动——请参阅SDK)。一般说来,系统框架会处理这个问题,您不需要采取任何特殊行动——但一定要确保您没有妨碍系统框架的正常操作。
3. 内存处理是Symbian OS 需要考虑的一个重要课题。在这一点上,应注意电话有时会不同于模拟器。因此
在将您的应用程序呈交给“Symbian 认证签名”进行测试之前,务必确保已在实际电话设备上测试了您的程序。
4. 内存堆栈空间有限!应尽可能将对象放到内存堆中,而不要放到栈里。KERN-EXEC 3 异常(panic)发生的
主要原因之一就是栈的破坏/溢出。
5. 应用程序发生异常(panic) 表明您的代码中一定有错误。以下是一些主要、常见的错误:
• 忘记将非对象成员、被分配到堆的变量加到CleanupStack 上。
• 将成员变量放到CleanupStack 上——这一点要千万避免;在析构函数中将这些变量删除就可以了。
• ‘重复删除’——例如,没有正确的从CleanupStack上Pop()出已经被销毁的对象,造成CleanupStack以后试图再次删除它。或者使用过一个对象之后将其删除但忘记将其值设成NULL,从而在析构函数又试图删除一次。
• 用可能不存在于您的析构函数中的变量调用函数。例如,以下代码可能导致异常,因为有可能您在分配内存之前您的对象已经被销毁,或者在应用程序的另一处已经删除了该内存,这样iSomeServer 就会处于NULL:
CMyClass::~CMyClass()
{
iSomeServer->Close();
delete iSomeServer;
}
应该如下编写代码:
CMyClass::~CMyClass()
{
if (iSomeServer)
{
iSomeServer->Close();
delete iSomeServer;
}
}
• 在NULL 指针上调用函数。
• 函数调用另一个函数,而其使用的变量已经超出范畴,例如:把一个栈变量传送到一个异步函数的回调
(callback) 里。
6. 在系统资源不够的情况下,得体的处理失效情况是非常重要的。最受限制的资源通常是系统RAM,因此
您需要注意正确的处理内存不足的情况。采用‘两段构造方法’和如下所述的CleanupStack 机制,对这种防御性编程来说是必不可少和极其重要的。
7. 对带“R”字头、具备Close()方法的类,总是使用CleanupClosePushL()。这将确保当Leave 事件发生时,它们会被恰当的清除。例如:
RFile file;
User::LeaveIfError(file.Open(…));
CleanupClosePushL(file);
…
CleanupStack::PopAndDestroy(&file);
对用Release()或Destroy()的‘R’ 类,亦可使用CleanupDeletePushL()及CleanupReleasePushL()来取代Close()。
8. 另外,请记住CleanupStack 机制是可扩展的,面对所有Leave 事件,都可以用它来有效的清除任何对象。即使您需要处理的是较复杂的情况,也不应该忽略采用正规的清理机制。欲进一步了解TCleanupItem,请参阅Symbian OS Library 文档。
9. 倘若您意图对HBufC 变量重新分配资源,在清除它们之后,总是将其设为NULL。由于HBufC 的资源分配或其再分配可能会导致Leave 事件的发生,从而可能会出现析构函数试图删除已经不存在的HBufC变量的情况。当然,对于任何由堆分配资源的变量而言都应如此,对HBufC 变量采取此做法更是已经成为普遍的使用模式。
10. 当必须采用自己的TRAP 时,请勿忽略所有的报错。
常见的编码错误是:
TRAPD(err, DoSomethingL());
if (err == KErrNone || err ==
KErrNotFound)
{
// Do something else
}
这意味着其他错误码都被忽略。然而,倘若您非用上述模式不可,应采用Leave 机制来处理其他错误:
TRAPD(err, DoSomethingL());
if (err == KErrNone || err ==
KErrNotFound)
{
// Do something else
}
else
User::Leave(err);
11. 不要延误将对象PushL()到CleanupStack 上。所有新创建的对象(成员变量除外)应被立即压入该堆栈。例如,下面的作法是错的:
void doExampleL()
{
CSomeObject* myObject1=new (ELeave)
CSomeObject;
CSomeObject* myObject2=new (ELeave)
CSomeObject;
…
// Do something here with thevariables
CleanupStack::PushL(myObject1);
CleanupStack::PushL(myObject2);
// Do something more with the variables
…
CleanupStack::PopAndDestroy(2);
// myObject2, myObject1
}
因为myObject2 的创建可能失败,造成myObject1“悬”在那里不能被清理。应该这样来实现:
void doExampleL()
{
CSomeObject* myObject1=new (ELeave)
CSomeObject;
CleanupStack::PushL(myObject1);
CSomeObject* myObject2=new (ELeave)
CSomeObject;
CleanupStack::PushL(myObject2);
…
// Do something here with the variables
…
CleanupStack::PopAndDestroy(2);
// myObject2, myObject1
}
12. 注意,那些名称有大写字母C 结尾的函数(例如NewLC())会自动把其对象置于CleanupStack。您不应该自己来将这些对象压入CleanupStack,否则该对象会入栈两次。当您创建非成员变量并为其分配内存时,这些由C 结尾的函数很有用。
13.“两段构造方法”是Symbian OS 内存管理的关键部分。基本原则是Symbian OS 中的构造函数或析构函数永远不应该发生Leave。倘若一个C++ 构造函数Leave,构造过程未完成的对象得不到清理,因为还没有生成指针指向该对象。为此,SymbianOS 中的构造函数仅将该对象实例化,而后调用该对象的ConstructL()函数,在其中将成员数据实例化。一旦ConstructL()发生Leave,标准的析构函数将被调用来清除所有至此已被成功分配的成员变量。在您的编码中照用这一设计模式来防止内存泄漏,至为关键。当您写每一行代码时,都应该问自己:“ 这一行代码能否发生Leave ?”假如回答为“是”,则应考虑“是否所有资源都将被释放?”。
14. 编码中请勿使用_L()宏——而应使用_LIT()。_L()自Symbian OS v5 起已是‘不推荐使用’(deprecated),
它的问题在于它将调用TPtrC(const TText*)构造函数,该构造函数会调用strlen()函数来计算该字串的长度。虽然这不会带来额外的RAM 开销,却会在运行时占用更多CPU 周期。相反,宏_LIT()直接创建了一个在编译时就全部实例化的对象,节省了构造TPtrC 的CPU 开销。当然,您首先应该考虑的是否应该使用硬编码的字符串常量,因为当您将来地方化(localize)您的程序时,这种常量类型的描述符(descriptor) 可能需要重新编码。
15. 当在函数参数中使用描述符(descriptor) 时,应缺省使用基类。在大多数情况下,以const TDesC& 形式来传递描述符。对可修改的描述符,则应使用TDes&。
16. 当在函数中传递或返回对象时,应确保如果您拥有该对象的所有权,您应负责将其清除! Symbian 采取
的约定是:函数中的指针表示所有权转移到调用者,而使用引用则表示被传递对象的所有权仍属于原所有者。
17. Active Objects 是Symbian OS 的重要特性之一。请仔细研究SDK 文档、Symbian Developer Network 白皮书,
以充分理解其工作原理。下面是一些有用的窍门:
• 在RunL()内无需使用TRAP()。Active Scheduler本身会TRAP 函数RunL()并在其发生Leave 时调用CActive::RunError()。
• 为此,您应实现自己的RunError()函数来处理从RunL()的Leave 事件。
• 保证RunL()操作尽可能简短。长时间运行的RunL()将阻塞其他Active Objects。
• 总是实现DoCancel()函数,总是在AO 析构函数中调用Cancel()。
18. 您应尽可能利用Active Object 框架机制。对于使用电池供电的设备,在一个循环中紧密不断地进行轮流检测(polling) 是极其不适当的,将带来大量耗电。写游戏时,对此尤需特别注意,详情参阅Symbian Developer Network 网站的技术文档
(www.symbian.com/developer/techlib/papers/porting_3D_games/XenGames_paper.pdf)。
19. ViewSrv 11 异常对于繁忙运行的程序(例如游戏)是一个潜在的问题。当您的,或者其他任何程序中的
ViewSrv active object 不能及时响应View Server 时就会导致此种异常。典型的最长回应时间是10-20 秒。FAQ-0900 有详细解释,FAQ-0920 有针对如何避免此类问题的实用技巧。二者均可从www3.symbian.com/faq.nsf 网页上的Symbian OSFAQ 数据库获取。
20. 您无需使用HBufC::Des()来进入一个HBufC 对象。只需采用* 操作符来为HBufC 对象解除引用(dereference)。这对于向某个接受TDesC&(上文的推荐做法)的函数传递HBufC 参数时尤其有用。
21. 当使用标准的程序.INI 文件的功能时,(即在您的应用UI 类中使用Application()->OpenIniFileLC();API 时),确保将版本号信息写入流(stream) 中。这样使您能够在未来新版本的程序中建立新的流,意味着即使某个最终用户将来安装您的软件的新版本时,不会因为在旧的.INI 文件中找不到正确配置或流时发生异常。
22. 在您的程序中实现框架类(framework class) 时要小心。应该始终从所提供的平台相关的框架类中继承。例如,对UIQ 而言,不要从CEikAppUi 继承您的AppUi 类,而应从CQikAppUi 继承。所有的应用基类(CQikAppUi、CQikApplication、CQikDocument)添加的功能支持更广的框架范围来保证应用程序正确运行。
测试诀窍
1. 最重要的测试诀窍是,在用模拟器时(emulator),正确的退出您的程序,而不是仅仅简单地关闭整个模拟
器。在调试模式中,在应用程序框架(applicationframework)的关闭函数前后有内存及句柄检测代码,当您退出应用程序时,此代码将被调用,从而可检测到是否有内存泄漏或遗留句柄(例如R 对象)发生。对UIQ 程序而言,为此目的习惯上在调试模式里提供一个Exit 菜单选项。
2. 另外一个至关重要的诀窍是在发布您的程序之前确保在.PKG 文件中包含了正确的平台相关信息。相关平台相关信息字符串的详情请参照该平台的SDK。www3.symbian.com/faq.nsf 上的Symbian OS FAQ 数据库中的FAQ-0853 提供了有用的相关信息。
3. 在写.PKG 文件时,也要确保恰当的使用了“!:\”语法。一般来说,您的应用程序应可从最终用户手机中
的任何盘中安装、运行。仅有极少量的文件需要放置于C:\ 盘中(例如.INI 文件)。
调试诀窍
1. 总应先使用模拟器调试;大部分同时发生在模拟器与硬件上的问题,使用模拟器调试会容易得多。
2. 在编写和调试新的控制类时,把iEikonEnv->WsSession().SetAutoFlush(ETrue)置于您的AppUi 的ConstructL()函数中。这意味着gc draw 指令会在模拟器中立即显示,而不是在下一次冲刷(flush)视窗服务器客户端缓冲时。编辑WSINI.INI 文件(\epoc32\release\winscw\udeb\system\data\),确保不存在关键字FLICKERFREEREDRAW。这意味着您可以逐步运行draw 代码,并看到每行代码的效果。然而,应确保此行代码不留在发布的软件中,因为这会影响性能。
3. 应定时对源文件运行LeaveScan 工具。该工具可以检测到所有可以Leave 的函数,并在其名称不以L 结尾时报错,并提醒源文件中潜在的缺陷或疏忽。在检查哪些代码应被允许Leave,并确保已正确处理此状况时,该工具非常有效。参见www3.symbian.com/faq.nsf 上的Symbian OS FAQ 数据库里的FAQ-0291,下载该工具并进一步了解。
4. 如果您的程序在正常关闭时因内存泄漏而发生异常,在MS Visual Studio 中可将泄漏的地址转换为CBase*
类型,从而查看该泄漏对象的类型。
5. 最近为对Symbian OS 开发者提供了新的功能:设备上调试(on-target debugging)。虽然不是所有的SDK及工具均已具备此功能,但大多数最近发布的SDK 及IDE 支持此功能。如可使用此功能,请在发布您的程序前使用它来检测任何潜在的手机硬件相关的缺陷。进一步的信息,请阅读SDK 及IDE 文档。
6. 确保‘Just in Time’调试被启用:
• 确保在文件“\epoc32\data\epoc.ini”里删除了宏“JustInTime 0”
• 注册表值做如下设定:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
Windows NT\CurrentVersion\AeDebug]
"UserDebuggerHotKey"=dword:00000000
"Debugger"="\"C:\\apps\\Metrowerks\\bin\
\IDE.exe\" -p %ld -e %ld"
"Auto"="0"
7. 审阅%Temp%epocwind.out 所含的纠错讯息。
8. 使用所有可用的开发工具,包括Lint、Leavesan、HookLogger、Panix、D_EXC 来检查内存泄漏及其他错误。这些工具及其他更多有用的工具可在SymbianDeveloper Network 下载
(www.symbian.com/developer/downloads/tools.html)。
9. FAQ-1344 教您如何诊断并调试违反平台安全(KErrPermissionDenied) 的故障,这有助于识别您的程序所缺少的权限(capability)。
参考读物
[1] Symbian Developer Network newsletter
http://www.symbian.com/developer/faq/index.html
[2] Symbian OS FAQ database
http://www.symbian.com/developer/techlib/faq.html
[3] Symbian OS C++ Coding Standards paper
http://www.symbian.com/developer/techlib/papers/coding_stds/2003-01_SyOSCodStn.pdf#one
[4] Coding Idioms paper
http://www.symbian.com/developer/techlib/papers/coding_idioms/2002_10_09_codingSymbianOS.pdf
[5] Symbian Press
http://www.symbian.com/developer/books
[6] Games Writing paper
http://www.symbian.com/developer/techlib/papers/porting_3D_games/XenGames_paper.pdf
[7] Active Objects paper
http://nds2.ncsp.nokia.com/download/?asset_id=11981;ref=symbian
or http://www.forum.nokia.com/main/1,,1_32_10_5,00.html
开发者资源
Symbian Developer Network
http://www.symbian.com/developer
Symbian Developer Network Newsletter
http://www.symbian.com/developer/newsletter.html
Symbian OS Tools Providers
http://www.symbian.com/developer/downloads/tools_providers.html
Sony Ericsson Developer World
http://developer.sonyericsson.com/site/znch/home/p_home.jsp
Forum Nokia
http://forum.nokia.com
Sun Microsystems Developer Services
http://gceclub.sun.com.cn/NASApp/sme/controller/teclist?tid=01