Symbian OS C++ for Windows C++ programmers
Andy Weinstein, Degel Software Ltd
Version 1.0, Oct 2002
1.簡(jiǎn)介。
本文討論了當(dāng)一個(gè)典型的Windows C++程序員初次接觸Symbian操作系統(tǒng)時(shí)可能遇到的問(wèn)題。我們開(kāi)發(fā)過(guò)三個(gè)成功版本Symbian操作系統(tǒng)的經(jīng)驗(yàn)使我們十分清楚在這個(gè)不算豐富穩(wěn)定的環(huán)境中工作什么才是困難的。Symbian成功的一個(gè)原因是許多手機(jī)生產(chǎn)商非常不希望被綁在微軟這條賊船上,另一個(gè)原因是Symbian集成了輕量級(jí)、的系統(tǒng),同時(shí)又可以提供如此廣泛的功能。這里的一些提示也許會(huì)對(duì)開(kāi)發(fā)成功的Symbian操作系統(tǒng)應(yīng)用程序有所幫助。
2.文檔和資源。
對(duì)于一個(gè)普通的Windows程序員來(lái)說(shuō),他對(duì)Symbian操作系統(tǒng)首先注意到的方面就是相比起微軟的精良裝備來(lái)說(shuō),Symbian開(kāi)發(fā)文檔實(shí)在是太少了。雖然這種情況正在得到改善,某些API類(lèi)仍然沒(méi)有相關(guān)文檔。比如CEikRichTextEditor這個(gè)類(lèi)在文檔中就沒(méi)有獨(dú)立條目。CRichText這個(gè)類(lèi)包括了大多數(shù)相關(guān)功能,被收入了文檔,但你可能并不知道何時(shí)次使用這種控制(參見(jiàn)我們下面關(guān)于對(duì)象結(jié)構(gòu)的說(shuō)明)。
相對(duì)花費(fèi)大量的人力物力去完善文檔,更簡(jiǎn)便的方法基于這樣一種事實(shí):Symbian提供了大量的源代碼(雖然還不是整個(gè)操作系統(tǒng))和例程。程序員無(wú)需猜測(cè)許多API的行為——他們只要看看執(zhí)行情況。要是這樣還不夠,Symbian對(duì)一些組件,例如Word,一樣提供了源代碼。API和例程相結(jié)合足以滿(mǎn)足大多數(shù)程序員的需要。
從6.0版開(kāi)始,Symbian操作系統(tǒng)95%的源代碼是對(duì)其白金合作伙伴項(xiàng)目(Platinum
Partner Program,http://www.symbian.com/partners/part-platnm.html)成員開(kāi)放的。這個(gè)需要額外付費(fèi)注冊(cè)。這個(gè)項(xiàng)目的影響之一就是某些老版本開(kāi)發(fā)包的完整源代碼不再提供,取而代之的是Symbian或者Nokia公司一邊繼續(xù)完善文檔,一邊提供不斷擴(kuò)充的例程。比如隨Series 60提供的文檔就有一個(gè)名為“如何操作rich text”的獨(dú)立條目(雖然CEikRichTextEditor仍然沒(méi)有條目)。
文檔中還有許多其他有用的,寫(xiě)得很好的“How to”文章。在Series 60中,文檔和例程的結(jié)合使用是顯而易見(jiàn)的——文檔中對(duì)每一個(gè)Avkon UI類(lèi)都直接引用有例程。
Windows程序員還有一個(gè)要習(xí)以為常的事情是缺乏有用的外部資源,雖然這種情況正在得到改善。《專(zhuān)業(yè)Symbian編程》(‘Professional Symbian Programming’ (PSP))包含了許多有用的信息,但是作為快速參考就不太合適,而且它成書(shū)于Symbian操作系統(tǒng)第5版的時(shí)候——新版將于2003年早些時(shí)候面世。PSP還不算過(guò)時(shí),但情況有所改變,尤其是在UI層次上。有本書(shū)叫《Symbian設(shè)備無(wú)線Java》(‘Wireless Java for Symbian Devices’)也許更新一些,可對(duì)C++開(kāi)發(fā)者沒(méi)什么用處。還有《Symbian操作系統(tǒng)通訊編程》(‘Symbian OS Communications Programming’)和《Series 60及Symbian操作系統(tǒng)編程》(‘Programming for Series 60 and Symbian OS’)也已上架。更多細(xì)節(jié)可以在http://www.symbian.com/books/index.html找到。
在線文檔和Q-A資源確實(shí)有助于彌補(bǔ)空白。Symbian自己設(shè)立了“Symbian開(kāi)發(fā)者網(wǎng)絡(luò)”,這是一個(gè)集論壇、FAQ、樣例和其它有用信息于一身的網(wǎng)絡(luò)資源。它的地址是http://www.symbian.com/developer。Nokia也有一個(gè)類(lèi)似的項(xiàng)目叫做“Nokia論壇”,網(wǎng)址位于http://www.forum.nokia.com。到底該去哪里可并不一定,而且你貼出了問(wèn)題也不一定就會(huì)有答案——這依賴(lài)于你其它的開(kāi)發(fā)伙伴的與人為善。Symbian和Nokia的內(nèi)部人員有時(shí)的確會(huì)出現(xiàn)在這里,但是如果你希望從知道答案的那個(gè)人口中得到確切答復(fù),你需要付費(fèi)注冊(cè)。Symbian的付費(fèi)注冊(cè)地址在http://www.symbian.com/partners/part-servs.html的“Partner Programs”中,Nokia的則可以從Nokia論壇的“Developer Support, Technical Case Solving”中找到。不過(guò)還是先看看免費(fèi)資源吧:這里有大量有用的信息和資源,包括定時(shí)的開(kāi)發(fā)包升級(jí)。
3.對(duì)象結(jié)構(gòu)。
Symbian操作系統(tǒng)有一個(gè)需要花費(fèi)時(shí)間來(lái)熟悉的方面在于他非常強(qiáng)大的對(duì)象結(jié)構(gòu)。例如,一個(gè)列表框(list box)不是一個(gè)對(duì)象——而是四個(gè):列表框?qū)ο螅╨ist box object)、模型(model)、視圖(view)和繪圖器(drawer)。它們之間的功能劃分是可以預(yù)期并且很符合邏輯的。
編輯控制就復(fù)雜的多。乍一看上去,它好像只有兩個(gè)主要對(duì)象:UI控制和一個(gè)包含處理文本格式化的文本對(duì)象。但是對(duì)格式化的操作引入了對(duì)字符格式化和對(duì)段落格式化的更進(jìn)一步的對(duì)象。這些,按照次序,使用了獨(dú)立的mask對(duì)象來(lái)指示顯示了你希望對(duì)任何給定調(diào)用定位的格式化的API調(diào)用。所以操作文本顯示為黑體還是非黑體,同時(shí)還影響到行間距的代碼就需要用到CEikRichTextEditor、CRichText、TCharFormat、TCharFormatMask、CParaFormat和CparaFormatMask這幾個(gè)類(lèi)。
這里有一段這樣的代碼:
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());
但是這還不是全部——關(guān)于文本視圖對(duì)象還另有天地,特別是CTextView和CTextLayout,連同他們的幫助對(duì)象一起。當(dāng)我們想在編輯控制中定位滾動(dòng)點(diǎn),以使文本的一行可以處于編輯控制底部之上一行的位置,我們就不得不了解到CTextView的存在,以及如何獲得它,還有關(guān)于CTextView::SetViewL、TViewYPosQualifier::SetHotSpot及TViewYPosQualifier::SetMakeLineFullyVisible。他們看起來(lái)是這個(gè)樣子的:
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);
誰(shuí)會(huì)想得到呢?我們的一位同事就是不相信這是就此可行的惟一途徑,但是他通過(guò)利用許多(各種各樣的)存在于各個(gè)類(lèi)之中的各種API,試用了各種不同的方法來(lái)證明了這一點(diǎn)。
我們正在描述的這種復(fù)雜性其實(shí)是因?yàn)镾ymbian操作系統(tǒng)提供的功能實(shí)在太豐富了,理解這一點(diǎn)很重要。一旦你了解了這個(gè)領(lǐng)域,你就會(huì)對(duì)Symbian操作系統(tǒng)的對(duì)象結(jié)構(gòu)是如此明智感激不盡,然后很快你就會(huì)發(fā)現(xiàn)你能夠預(yù)知你需要的函數(shù)藏在什么地方。如此重復(fù),你會(huì)發(fā)現(xiàn)我們剛開(kāi)始的困惑現(xiàn)在已經(jīng)變成了一種審美享受。
4.串。
串,啊,Symbian中的串!毫無(wú)疑問(wèn)Symbian中對(duì)串的實(shí)現(xiàn)是經(jīng)過(guò)深思熟慮的,強(qiáng)壯的和經(jīng)濟(jì)的。同樣無(wú)疑的是這代表著一種富貴病。串是通過(guò)Symbian稱(chēng)之為“描述符”的機(jī)制來(lái)實(shí)現(xiàn)的,跟以下幾個(gè)類(lèi)有關(guān):TDesC、TBufCBase、TDes、TPtrC、TBufC、HBufC、TBuf和TPtr。這還不包括由TLitC操縱的直接量,它嚴(yán)格來(lái)說(shuō)還算不上是個(gè)描述符。我至今也還沒(méi)有提到Unicode,雖然它顯然很受注意。這些類(lèi)可以使你精確的使用少量的內(nèi)存來(lái)存儲(chǔ)不同類(lèi)型的串,同時(shí)還允許它們“干凈”的互相操作。但是每次都要考慮到底使用哪一個(gè)實(shí)在不是一件有趣的事情,這種情況是無(wú)法避免的,因?yàn)椴煌腁PI有不同的參數(shù)或返回值。
這里有個(gè)小例子把一個(gè)名字轉(zhuǎn)化為樣本消息:
_LIT(KBoilerplate, "Hello there, %S");
TPtrC name = GetPointerIntoNameDescriptorWithoutAllocatingAnyMemory();
TPtr finishedProduct = HBufC::NewLC(KBoilerplate().Length() + name.Length())->Des();
finishedProduct.Format(KBoilerplate(), &name);
// 這段代碼還缺少了一行,在下文會(huì)添加并討論。
根本沒(méi)有哪個(gè)類(lèi)提供類(lèi)似于MFC串或是Java串——完全動(dòng)態(tài)的串。Java中對(duì)于String和StringBuffer的劃分對(duì)我們?cè)诖擞懻摰膯?wèn)題幾乎沒(méi)有任何價(jià)值。Symbian所做的值得贊揚(yáng)的事就是他們把這些的文檔做得很好。但是你真正需要的文檔卻不會(huì)頻繁去讀它。Symbian在開(kāi)發(fā)時(shí)考慮的是那些資源有限的機(jī)器,不可否認(rèn)對(duì)于某些應(yīng)用方面和平臺(tái)來(lái)說(shuō)這是必不可少的。但是就目前來(lái)說(shuō),一個(gè)Symbian操作系統(tǒng)的C++程序員會(huì)感覺(jué)像那些早期的IBM PC程序員,他們?cè)趯?duì)付的是Intel的分段結(jié)構(gòu),然而68000芯片又大又單一的尋址空間使得蘋(píng)果看起來(lái)是個(gè)更酷的選擇。
5.清潔棧及錯(cuò)誤處理。
Symbian操作系統(tǒng)的錯(cuò)誤處理框架,主要是清潔棧,是個(gè)Windows程序員不太熟悉的元素。就像描述符一樣,它很優(yōu)雅,Symbian宣布它比C++語(yǔ)言的異常處理機(jī)制有效得多。在一篇叫做《Symbian操作系統(tǒng)編程術(shù)語(yǔ)》的文章里對(duì)此進(jìn)行了很好的討論,可以從http://www.symbian.com得到。但是,在系統(tǒng)層次上幾乎是一定的“正確的事”卻減慢了應(yīng)用開(kāi)發(fā)。Symbian為需要在有限資源條件下長(zhǎng)時(shí)間運(yùn)行的應(yīng)用程序設(shè)計(jì)了這樣一個(gè)框架,這需要在開(kāi)發(fā)時(shí)間為強(qiáng)壯度付出代價(jià)。使用TRY/CATCH這種類(lèi)型的框架當(dāng)然是簡(jiǎn)單明了受到歡迎的,但是要頻繁的在恰當(dāng)?shù)臅r(shí)候把已某種分配的內(nèi)存壓入和彈出清潔棧意味著你就會(huì)偶爾犯錯(cuò)誤。當(dāng)發(fā)生這種情況時(shí),你就得花費(fèi)大量的時(shí)間去追蹤在你的代碼中造成的不對(duì)稱(chēng)現(xiàn)象。
作為一個(gè)對(duì)描述符和清潔棧不熟悉的程序員是如何陷入困境的例子,讓我們簡(jiǎn)單的看看一個(gè)關(guān)于串的示例:
_LIT(KBoilerplate, "Hello there, %S");
TPtrC name = GetPointerIntoNameDescriptorWithoutAllocatingAnyMemory();
TPtr finishedProduct = HBufC::NewLC(KBoilerplate().Length() + name.Length())->Des();
finishedProduct.Format(KBoilerplate(), &name);
// 下面的就是缺少的一行——見(jiàn)之前的注釋
// CleanupStack::Pop();這段代碼確實(shí)把內(nèi)存推入了清潔棧,但僅僅是對(duì)變量的數(shù)據(jù)類(lèi)型一瞥而過(guò)你就發(fā)現(xiàn)不了這一點(diǎn)。這是因?yàn)槲覀兪褂昧艘环N便利的方法定義了一個(gè)TPtr來(lái)持有finishedProduct并把HBufC::NewLC(...)->Des()的值賦給它。HBufC是在堆上已分配內(nèi)存的標(biāo)識(shí),但是如果我們僅僅看到了左邊,可能就會(huì)忽略掉剛一分配我們就使用了Des()的快捷方式這一事實(shí)。NewLC也直接把分配的內(nèi)存推入清潔棧。一旦我們完成了finishedProduct,調(diào)用CleanupStack::Pop(),否則我們的清潔棧就會(huì)不平衡。問(wèn)題在于,我們可能起先發(fā)現(xiàn)不了這種不平衡,直到它引發(fā)錯(cuò)誤為止。還有比追蹤這種不對(duì)稱(chēng)更有趣的事情呢。
6.應(yīng)用框架。
Symbian操作系統(tǒng)的確提供了一套用以建立應(yīng)用程序的類(lèi),然而同時(shí),使之能夠建立基于非對(duì)話(huà)框的應(yīng)用程序界面的框架代碼卻非常少。要處理像焦點(diǎn)轉(zhuǎn)移這種事件只有靠手工。這個(gè)例子當(dāng)然很容易理解怎樣來(lái)做,但實(shí)際上每個(gè)屏幕畫(huà)面被設(shè)計(jì)的時(shí)候都要仔細(xì)考慮它們可是一件費(fèi)時(shí)費(fèi)力又容易招致錯(cuò)誤的事情。從Symbian操作系統(tǒng)6.1版開(kāi)始,Symbian操作系統(tǒng)增加了一個(gè)很簡(jiǎn)單的機(jī)制使用惟一的ID號(hào)來(lái)實(shí)現(xiàn)不同屏幕之間的切換,但是要做的事情還很多。
這里有一個(gè)適用于大多數(shù)初級(jí)情況的例子:這里沒(méi)有使用API來(lái)給屏幕增加一個(gè)控制,而是使用了視圖對(duì)象中叫做CountComponentControls和ComponentControl的兩個(gè)對(duì)象,后者必須為從0到前者返回值之間的每個(gè)整數(shù)給UI控制返回一個(gè)指針。要添加一個(gè)控制,除了構(gòu)造,調(diào)整大小,在恰當(dāng)?shù)攸c(diǎn)激活,你必須在代碼里對(duì)CountComponentControls的返回值進(jìn)行累加,還要在ComponentControl函數(shù)中給switch語(yǔ)句添加一個(gè)case。
Symbian操作系統(tǒng)確實(shí)提供了根據(jù)資源文件建立對(duì)話(huà)框的完美合理的途徑。當(dāng)你在資源文件里聲明對(duì)話(huà)框時(shí),你不必直接對(duì)這些東西尋址。但是由于對(duì)話(huà)框只允許在一個(gè)豎列中布置控制,對(duì)于一些的應(yīng)用程序中復(fù)雜的屏幕設(shè)置來(lái)說(shuō)可能并不適合。
7.窗口問(wèn)題。
Symbian操作系統(tǒng)不鼓勵(lì)使用“自有窗口”控制。這就是說(shuō),一個(gè)控制并不總是持有自已的窗口(從受按Z軸順序裁剪的窗口系統(tǒng)控制的圖形對(duì)象意義上來(lái)說(shuō))。因?yàn)檫@些對(duì)象使用了系統(tǒng)資源,應(yīng)用程序應(yīng)該簡(jiǎn)單的執(zhí)行一個(gè)合適的繪畫(huà)功能來(lái)規(guī)定它所在矩形的邊界。這對(duì)于習(xí)慣每個(gè)控制有一個(gè)真正窗口的開(kāi)發(fā)者來(lái)說(shuō)可能感覺(jué)不同,但是掌握這一點(diǎn)并不困難。
雖然Symbian操作系統(tǒng)的標(biāo)準(zhǔn)UI控制集十分豐富而且強(qiáng)壯,但是偶爾也會(huì)有誤操作的情況發(fā)生。比如我們已經(jīng)有了一個(gè)帶滾動(dòng)條的編輯框,現(xiàn)在要能夠暫時(shí)用另外一個(gè)控制取代它。僅僅使編輯框不可見(jiàn)并不能解決問(wèn)題。編輯框的某些內(nèi)容,包括滾動(dòng)條,仍然以多種有趣但不可接受的方式存在著。我們認(rèn)為這可能應(yīng)該歸咎于前面提到的編輯框的復(fù)雜的對(duì)象結(jié)構(gòu);看起來(lái)在這種分布式結(jié)構(gòu)中要想進(jìn)行隱藏可不是無(wú)關(guān)緊要的問(wèn)題。我們是通過(guò)在畫(huà)面以外重新定位它解決的。
用這種方法,我們探究了一些其它的可能性,還研究了像控制棧以及Symbian稱(chēng)之為彈出窗口的東西,以后這可以使你按下按鈕激活彈出選擇列表時(shí)順手拈來(lái)。這里進(jìn)行了示范:
iPopout = new (ELeave) CEikColumnListBox;
iPopout->ConstructL(NULL,CEikListBox::EPopout);
iPopout->CreateScrollBarFrameL();
iPopout->ScrollBarFrame()->SetScrollBarVisibilityL(
CEikScrollBarFrame::EOff,CEikScrollBarFrame::EAuto);
iPopout->SetObserver(this);
TSize popoutSize = TSize(width, height);
TPoint popoutOrigin = TPoint(topLeft, bottomRight);
iPopout->Model()->SetItemTextArray(iArray);
iPopout->Model()->SetOwnershipType(ELbmDoesNotOwnItemArray);
iPopout->ItemDrawer()->ColumnData()->
SetColumnWidthPixelL(0, firstColWidth);
iPopout->ItemDrawer()->ColumnData()->
SetColumnWidthPixelL(1, secondColWidth);
iPopout->SetExtent(popoutOrigin, popoutSize);
iPopout->SetCurrentItemIndex(0);
iEikonEnv->AddDialogLikeControlToStackL(iPopout);
iPopout->ActivateL();
iPopout->DrawNow();一種很早也很有趣的學(xué)習(xí)經(jīng)驗(yàn)是必須手工書(shū)寫(xiě)代碼來(lái)刷新和顯示列表框的內(nèi)容。刷屏代碼很復(fù)雜,而且要跟蹤所有的插入和刪除并不是一件輕而易舉的事。無(wú)論什么樣的刷新率,我們都希望代碼可以在刷新過(guò)程的得到正確顯示的結(jié)果而不管刷新了什么,還不能丟掉滾動(dòng)條的位置和選區(qū),也不能產(chǎn)生不必要的閃屏和閃爍感。Symbian操作系統(tǒng)在基礎(chǔ)列表框類(lèi)中提供了叫做HandleItemRemovalL和HandleItemAdditionL的功能。另外還有設(shè)置當(dāng)前項(xiàng)的SetCurrentItemIndex。但是在Symbian操作系統(tǒng)第5版中沒(méi)有關(guān)于這些函數(shù)如何工作的文檔。我們只能通過(guò)研究源代碼才知道HandleItemAdditionL隱式使用了DrawNow,但是HandleItemRemovalL不負(fù)責(zé)重繪。被HandleItemRemovalL所調(diào)用的Reset也不得不通過(guò)源代碼來(lái)理解,當(dāng)前項(xiàng)索引在調(diào)用HandleItemRemovalL后需要刷新。這種處理重繪的不對(duì)稱(chēng)性看起來(lái)很奇怪又帶有很多潛在問(wèn)題。但是有個(gè)好消息就是在目前的Symbian操作系統(tǒng)文檔中,所有這些都清楚明白的被收錄在你能找到的地方。
作為前面被我們一帶而過(guò)的Symbian的其它方面,在圖形領(lǐng)域,為了使系統(tǒng)的總開(kāi)銷(xiāo)盡可能低,有些應(yīng)用程序的代碼開(kāi)銷(xiāo)也被考慮在內(nèi)。Symbian確確實(shí)實(shí)提供的特性是優(yōu)雅和絕大多數(shù)情況下的一致性,而且現(xiàn)在在文檔方面已經(jīng)做得很好了,他使創(chuàng)建平滑的圖形動(dòng)作變得相對(duì)簡(jiǎn)單。
8.消息,異步服務(wù)和活動(dòng)對(duì)象。
MFC在對(duì)無(wú)論是初級(jí)還是了解驅(qū)動(dòng)他們應(yīng)用程序的消息循環(huán)和調(diào)度機(jī)制的程序員隱藏Windows消息范例方面做得很好。在Symbian操作系統(tǒng)中,驅(qū)動(dòng)應(yīng)用程序的是一套完全不同又十分強(qiáng)大的機(jī)制,被稱(chēng)為活動(dòng)對(duì)象。它與Symbian操作系統(tǒng)的客戶(hù)端-服務(wù)器端結(jié)構(gòu)緊密結(jié)合,提供了各種系統(tǒng)服務(wù),同時(shí)也可以為程序員所使用來(lái)給他們可能需要為應(yīng)用程序創(chuàng)建的任何異步服務(wù)建立干凈、標(biāo)準(zhǔn)的接口。
簡(jiǎn)而言之,CActiveScheduler執(zhí)行了Windows消息循環(huán),在一個(gè)給定線程內(nèi)提供了共用的多任務(wù),CActive(一個(gè)活動(dòng)對(duì)象)充當(dāng)了消息處理者。在Symbian的UI框架中,這要比MFC把消息隱藏得更好:接收到的消息總是由框架調(diào)度給預(yù)先定義的可以被應(yīng)用程序重載的方法——比如OfferKeyEventL(處理鍵盤(pán)輸入)。
定時(shí)器是使用了活動(dòng)對(duì)象框架的簡(jiǎn)單系統(tǒng)服務(wù)的一個(gè)實(shí)例。不同于Windows中調(diào)用StartTimer來(lái)觸發(fā)WM_TIMER消息的做法,Symbian操作系統(tǒng)中是由一個(gè)CTimer對(duì)象與系統(tǒng)時(shí)鐘服務(wù)進(jìn)行交互,RTimer與其RunL函數(shù)根據(jù)由API規(guī)定的時(shí)間間隔被調(diào)用。程序員從CTimer繼承到一個(gè)對(duì)象,并重載RunL方法。
其它的異步系統(tǒng)服務(wù),如Nokia 7650上的照相機(jī)功能也是通過(guò)類(lèi)似方法提供的。CCameraManager定義了一個(gè)活動(dòng)對(duì)象提出拍照操作的請(qǐng)求并隨即獲得通知。實(shí)際的異步服務(wù)是由RCameraServ提供的,但是典型的程序員根本用不著處理任何客戶(hù)端-服務(wù)器端或者顯式的進(jìn)程間通信問(wèn)題。
使用活動(dòng)對(duì)象時(shí),頭腦中時(shí)刻牢記活動(dòng)對(duì)象何時(shí)按預(yù)定計(jì)劃執(zhí)行這個(gè)很簡(jiǎn)單的準(zhǔn)則非常重要。這就是說(shuō),到底什么時(shí)候CActiveScheduler會(huì)調(diào)用一個(gè)CActive對(duì)象的RunL方法呢?
·CActive對(duì)象必需是“活動(dòng)”的。這是通過(guò)調(diào)用Cactive::SetActive實(shí)現(xiàn)的,通常在活動(dòng)對(duì)象自己對(duì)應(yīng)用程序某個(gè)請(qǐng)求做出的響應(yīng)中完成。
·CActive對(duì)象的狀態(tài),由成員變量iStatus所示,一定不能為KRequestPending。這個(gè)值表示該對(duì)象正在等待服務(wù)完成。通常在提出一個(gè)服務(wù)請(qǐng)求時(shí)變量iStatus才會(huì)被服務(wù)提供者設(shè)為KRequestPending。iStatus隨后在服務(wù)結(jié)束時(shí)被服務(wù)者提供者更新。
·CActiveScheduler需要接收到一個(gè)信號(hào)——該信號(hào)由異步服務(wù)在結(jié)束時(shí)發(fā)出。
當(dāng)異步進(jìn)程對(duì)應(yīng)用程序發(fā)出結(jié)束信號(hào),然而沒(méi)有準(zhǔn)備好的CActive對(duì)象時(shí),就會(huì)產(chǎn)生一個(gè)“迷失”信號(hào)。這在以下幾種情況下可能發(fā)生:
·你忘記了使用CActive::SetActive使活動(dòng)對(duì)象處于“活動(dòng)”狀態(tài)。
·服務(wù)提供者在發(fā)出操作完成信號(hào)時(shí)忘記將iStatus置為非KRequestPending的值。這只會(huì)在你寫(xiě)了錯(cuò)誤的服務(wù)提供者時(shí)出現(xiàn)——典型的應(yīng)用程序會(huì)使用系統(tǒng)定義的服務(wù)。
·服務(wù)提供者給同一個(gè)操作發(fā)出了不只一個(gè)終止信號(hào)。
第三種情況在服務(wù)提供者設(shè)計(jì)不夠仔細(xì)的情況下可能發(fā)生。例如,有一個(gè)服務(wù)已經(jīng)結(jié)束并且對(duì)客戶(hù)端通知了這一事實(shí),但是客戶(hù)端恰恰在收到這個(gè)通知之前發(fā)出了取消服務(wù)的請(qǐng)求。服務(wù)提供者必需忽略這一當(dāng)前不合理的請(qǐng)求。要是服務(wù)提供者沒(méi)有忽略這一請(qǐng)求,而是對(duì)它做出響應(yīng)并給出一個(gè)附加信號(hào)說(shuō)已經(jīng)結(jié)束了,會(huì)是怎么樣呢?在這種情況中,附加的信號(hào)終就會(huì)成為“迷失”信號(hào),并會(huì)引起程序出錯(cuò)。這對(duì)于糾錯(cuò)來(lái)說(shuō)很困難,因?yàn)?ldquo;迷失”信號(hào)是被CActiveScheduler檢測(cè)到的,沒(méi)有顯示是什么CActive對(duì)象的責(zé)任。
CActive對(duì)象必需確保在各種情況下與服務(wù)相關(guān)聯(lián)的終止信號(hào)都恰當(dāng)。在解除程序中應(yīng)該典型的具有一個(gè)CActive::Cancel調(diào)用。如果Cancel沒(méi)有被調(diào)用,而對(duì)象在有請(qǐng)求仍被掛起時(shí)就被刪除,錯(cuò)誤就會(huì)發(fā)生。而且,每個(gè)CActive對(duì)象必需以確保任何由該對(duì)象請(qǐng)求的服務(wù)都被取消的方式執(zhí)行CActive::DoCancel,否則CActive::Cancel就會(huì)一直等待服務(wù)提供者的終止信號(hào)。這兩種錯(cuò)誤都很難被檢測(cè)到,因?yàn)樗鼈冎辉诖嬖诿黠@的請(qǐng)求時(shí)才會(huì)被發(fā)現(xiàn)。
結(jié)束語(yǔ)
我們希望本文對(duì)Windows程序員開(kāi)始Symbian操作系統(tǒng)下的工作能提供值得的期待和有用幫助。我們期望看到隨著這片新市場(chǎng)的成長(zhǎng)未來(lái)的Symbian操作系統(tǒng)及相關(guān)的編程范例是如何發(fā)展的。