【转】Symbian C++?
1.简介
本文讨论了当一个典型的Windows C++程序员初次接触Symbian操作系统 时可能遇到的问题。我们开发过三个成功 版本Symbian操作系统的经验使我们十分清楚在这个不算丰富稳定的环境中工作什么才是困难的。Symbian成功的一个原因是许多手机生产商非常不希望被绑在微软这条贼船上,另一个原因是Symbian集成了轻量级、一流的系统,同时又可以提供如此广泛的功能。这里的一些提示也许会对开发成功的Symbian操作系统应用程序 有所帮助。
2.文档和资源
对于一个普通的Windows程序员来说,他对Symbian操作系统首先注意到的方面就是相比起微软的高级精良装备来说,Symbian开发文档实在是太少了。虽然这种情况正在得到改善,某些API类仍然没有相关文档。比如CEikRichTextEditor这个类在文档中就没有独立条目。CRichText这个类包括了大多数相关功能,被收入了文档,但你可能并不知道何时第一次使用这种控制 (参见我们下面关于对象结构的说明)。
相对花费大量的人力物力去完善文档,更简便的方法基于这样一种事实:Symbian提供了大量的源代码(虽然还不是整个操作系统)和例程。程序员无需猜测许多API的行为——他们只要看看执行情况。要是这样还不够,Symbian对一些组件,例如Word,一样提供了源代码。API和例程相结合足以满足大多数程序员的需要。
相对花费大量的人力物力去完善文档,更简便的方法基于这样一种事实:Symbian提供了大量的源代码(虽然还不是整个操作系统)和例程。程序员无需猜测许多API的行为——他们只要看看执行情况。要是这样还不够,Symbian对一些组件,例如Word,一样提供了源代码。API和例程相结合足以满足大多数程序员的需要。
从6.0版开始,Symbian操作系统95%的源代码是对其白金合作伙伴 项目 (PlatinumPartner Program,http://www.symbian.com/partners/part-platnm.html)成员开放的。这个需要额外付费注册。这个项目的影响之一就是某些老版本开发包的完整源代码不再提供,取而代之的是Symbian或者Nokia公司一边继续完善文档,一边提供不断扩充的例程。比如随Series 60提供的文档就有一个名为“如何操作rich text”的独立条目(虽然CEikRichTextEditor仍然没有条目)。文档中还有许多其他有用的,写得很好的“How to”文章。在Series 60中,文档和例程的结合使用是显而易见的——文档中对每一个Avkon UI类都直接引用有例程。Windows程序员还有一个要习以为常的事情是缺乏有用的外部资源,虽然这种情况正在得到改善。《专业Symbian编程 》(‘Professional Symbian Programming’ (PSP))包含了许多有用的信息,但是作为快速参考就不太合适,而且它成书于Symbian操作系统第5版的时候——新版将于2003年早些时候面世。PSP还不算过时,但情况有所改变,尤其是在UI层次上。有本书叫《Symbian设备无线Java》(‘Wireless Java for Symbian Devices’)也许更新一些,可对C++开发者没什么用处。还有《Symbian操作系统通讯编程》(‘Symbian OS Communications Programming’)和《Series 60及Symbian操作系统编程》(‘Programming for Series 60 and Symbian OS’)也已上架。更多细节可以在http://www.symbian.com/books/index.html找到。
在线文档和Q-A资源确实有助于弥补空白。Symbian自己设立了“Symbian开发者网络”,这是一个集论坛 、FAQ、样例和其它有用信息于一身的网络资源。它的地址是http://www.symbian.com/developer。Nokia也有一个类似的项目叫做“Nokia论坛”,网址位于http://www.forum.nokia.com。到底该去哪里可并不一定,而且你贴出了问题也不一定就会有答案——这依赖于你其它的开发伙伴的与人为善。Symbian和Nokia的内部人员有时的确会出现在这里,但是如果你希望从知道答案的那个人口中得到确切答复,你需要付费注册。Symbian的付费注册地址在http://www.symbian.com/partners/part-servs.html的“Partner Programs”中,Nokia的则可以从Nokia论坛的“Developer Support, Technical Case Solving”中找到。不过还是先看看免费资源吧:这里有大量有用的最新信息和资源,包括定时的开发包升级。
3.对象结构Symbian
操作系统有一个需要花费时间来熟悉的方面在于他非常强大的对象结构。例如,一个列表框(list box)不是一个对象——而是四个:列表框对象(list box object)、模型(model)、视图(view)和绘图器(drawer)。它们之间的功能划分是可以预期并且很符合逻辑的。
编辑控制就复杂的多。乍一看上去,它好像只有两个主要对象:UI控制和一个包含处理文本格式化的文本对象。但是对格式化的操作引入了对字符格式化和对段落格式化的更进一步的对象。这些,按照次序,使用了独立的mask对象来指示显示了你希望对任何给定调用定位的格式化的API调用。所以操作文本显示为黑体还是非黑体,同时还影响到行间距的代码就需要用到CEikRichTextEditor、CRichText、TCharFormat、TCharFormatMask、CParaFormat和CparaFormatMask这几个类。这里有一段这样的代码:
CODE:
TCharFormat defaultFormat;
TCharFormatMask formatMask;
formatMask.SetAttrib(EAttFontStrokeWeight);
CRichText* text = iDisplay->RichText();
text->Reset();
for (int i = 0; i < 10; ++i)
{
TPtrC boldText = getBoldPiece(i);
TPtrC plainText = getRomanPiece(i);
...
TInt insertPos = text->DocumentLength();
text->SetInsertCharFormatL(*iCharFormat, formatMask, insertPos);
text->InsertL(insertPos, boldText);
text->CancelInsertCharFormat();
insertPos = text->DocumentLength();
text->SetInsertCharFormatL(defaultFormat, formatMask, insertPos);
text->InsertL(insertPos, plainText);
text->CancelInsertCharFormat();
}
CParaFormat paraFormat;
TParaFormatMask paraFormatMask;
iDisplay->RichText()->GetParaFormatL(¶Format, paraFormatMask,
0, iDisplay->TextLength());
paraFormatMask.ClearAll();
paraFormatMask.SetAttrib(EAttLineSpacing);
paraFormatMask.SetAttrib(EAttLineSpacingControl);
paraFormat.iLineSpacingControl = CParaFormat::ELineSpacingExactlyInTwips;
CGraphicsDevice* screenDevice = iEikonEnv->ScreenDevice();
TInt paraDelta;
...
TInt lineHeight = screenDevice->VerticalPixelsToTwips(
iRegularFont->HeightInPixels() + paraDelta);
paraFormat.iLineSpacingInTwips = lineHeight;
iDisplay->RichText()->ApplyParaFormatL(
¶Format, paraFormatMask, 0, iDisplay->TextLength());
CODE:
TCharFormat defaultFormat;
TCharFormatMask formatMask;
formatMask.SetAttrib(EAttFontStrokeWeight);
CRichText* text = iDisplay->RichText();
text->Reset();
for (int i = 0; i < 10; ++i)
{
TPtrC boldText = getBoldPiece(i);
TPtrC plainText = getRomanPiece(i);
...
TInt insertPos = text->DocumentLength();
text->SetInsertCharFormatL(*iCharFormat, formatMask, insertPos);
text->InsertL(insertPos, boldText);
text->CancelInsertCharFormat();
insertPos = text->DocumentLength();
text->SetInsertCharFormatL(defaultFormat, formatMask, insertPos);
text->InsertL(insertPos, plainText);
text->CancelInsertCharFormat();
}
CParaFormat paraFormat;
TParaFormatMask paraFormatMask;
iDisplay->RichText()->GetParaFormatL(¶Format, paraFormatMask,
0, iDisplay->TextLength());
paraFormatMask.ClearAll();
paraFormatMask.SetAttrib(EAttLineSpacing);
paraFormatMask.SetAttrib(EAttLineSpacingControl);
paraFormat.iLineSpacingControl = CParaFormat::ELineSpacingExactlyInTwips;
CGraphicsDevice* screenDevice = iEikonEnv->ScreenDevice();
TInt paraDelta;
...
TInt lineHeight = screenDevice->VerticalPixelsToTwips(
iRegularFont->HeightInPixels() + paraDelta);
paraFormat.iLineSpacingInTwips = lineHeight;
iDisplay->RichText()->ApplyParaFormatL(
¶Format, paraFormatMask, 0, iDisplay->TextLength());
但是这还不是全部——关于文本视图对象还另有天地,特别是CTextView和CTextLayout,连同他们的帮助对象一起。当我们想在编辑控制中定位滚动点,以使文本的最后一行可以处于编辑控制底部之上一行的位置,我们就不得不了解到CTextView的存在,以及如何获得它,还有关于CTextView::SetViewL、TViewYPosQualifier::SetHotSpot及TViewYPosQualifier::SetMakeLineFullyVisible。他们看起来是这个样子的:
CODE:
TInt yPos = iDisplay->TextView()->ViewRect().iBr.iY;
TViewYPosQualifier yPosQualifier;
yPosQualifier.SetHotSpot(TViewYPosQualifier::EFViewBottomOfLine);
yPosQualifier.SetMakeLineFullyVisible(
TViewYPosQualifier::EFViewForceLineFullyVisible);
iDisplay->TextView()->SetViewL(
iDisplay->TextLength(),
yPos,
yPosQualifier,
CTextView::EFViewDiscardAllFormat);
CODE:
TInt yPos = iDisplay->TextView()->ViewRect().iBr.iY;
TViewYPosQualifier yPosQualifier;
yPosQualifier.SetHotSpot(TViewYPosQualifier::EFViewBottomOfLine);
yPosQualifier.SetMakeLineFullyVisible(
TViewYPosQualifier::EFViewForceLineFullyVisible);
iDisplay->TextView()->SetViewL(
iDisplay->TextLength(),
yPos,
yPosQualifier,
CTextView::EFViewDiscardAllFormat);
谁会想得到呢?我们的一位同事就是不相信这是就此可行的惟一途径,但是他通过利用许多(各种各样的)存在于各个类之中的各种API,试用了各种不同的方法来证明了这一点。
我们正在描述的这种复杂性其实是因为Symbian操作系统提供的功能实在太丰富了,理解这一点很重要。一旦你了解了这个领域,你就会对Symbian操作系统的对象结构是如此明智感激不尽,然后很快你就会发现你能够预知你需要的函数藏在什么地方。如此重复,你会发现我们刚开始的困惑现在已经变成了一种审美享受。
我们正在描述的这种复杂性其实是因为Symbian操作系统提供的功能实在太丰富了,理解这一点很重要。一旦你了解了这个领域,你就会对Symbian操作系统的对象结构是如此明智感激不尽,然后很快你就会发现你能够预知你需要的函数藏在什么地方。如此重复,你会发现我们刚开始的困惑现在已经变成了一种审美享受。
4.串
Symbian中的串!毫无疑问Symbian中对串的实现是经过深思熟虑的,强壮的和经济的。同样无疑的是这代表着一种富贵病。串是通过Symbian称之为“描述符”的机制来实现的,跟以下几个类有关:TDesC、TBufCBase、TDes、TPtrC、TBufC、HBufC、TBuf和TPtr。这还不包括由TLitC操纵的直接量,它严格来说还算不上是个描述符。我至今也还没有提到Unicode,虽然它显然很受注意。这些类可以使你精确的使用最少量的内存来存储不同类型的串,同时还允许它们“干净”的互相操作。但是每次都要考虑到底使用哪一个实在不是一件有趣的事情,这种情况是无法避免的,因为不同的API有不同的参数或返回值。
这里有个小例子把一个名字转化为样本消息:
CODE:
_LIT(KBoilerplate, "Hello there, %S");
TPtrC name = GetPointerIntoNameDes criptorWithoutAllocatingAnyMemory();
TPtr finishedProduct = HBufC::NewLC(KBoilerplate().Length() + name.Length())->Des();
finishedProduct.Format(KBoilerplate(), &name);
// 这段代码还缺少了一行,在下文会添加并讨论。
这里有个小例子把一个名字转化为样本消息:
CODE:
_LIT(KBoilerplate, "Hello there, %S");
TPtrC name = GetPointerIntoNameDes criptorWithoutAllocatingAnyMemory();
TPtr finishedProduct = HBufC::NewLC(KBoilerplate().Length() + name.Length())->Des();
finishedProduct.Format(KBoilerplate(), &name);
// 这段代码还缺少了一行,在下文会添加并讨论。
根本没有哪个类提供类似于MFC串或是Java串——完全动态的串。Java中对于String和StringBuffer的划分对我们在此讨论的问题几乎没有任何价值。Symbian所做的值得赞扬的事就是他们把这些的文档做得很好。但是你真正需要的文档却不会频繁去读它。Symbian在开发时考虑的是那些资源有限的机器,不可否认对于某些应用方面和平台来说这是必不可少的。但是就目前来说,一个Symbian操作系统的C++程序员会感觉像那些早期的IBM PC程序员,他们在对付的是Intel的分段结构,然而68000芯片又大又单一的寻址空间使得苹果看起来是个更酷的选择。
5.清洁栈及错误处理
Symbian操作系统的错误处理框架,主要是清洁栈,是个Windows程序员不太熟悉的元素。就像描述符一样,它很优雅,Symbian宣布它比C++语言的异常处理机制有效得多。