活動規(guī)劃器 (Active Scheduler)
由于使用多線程來處理異步請求比較消耗系統(tǒng)資源,所以symbian 使用了活動對象(Active Object)來解決異步請求的問題。
活動規(guī)劃器(active scheduler)用于處理由活動對象提出的異步請求。它檢測活動對象提出的異步請求,并安排活動對象的請求完成事件的執(zhí)行順序。活動規(guī)劃器僅用一個事件處理線程來規(guī)劃各個活動對象提出的事件請求,所以它要比多線程實現(xiàn)異步請求占用更少的資源。
一個活動規(guī)劃器通常對應到一個線程,CActiveScheduler 類的靜態(tài)方法通常都是針對當前線程中對象的操作。
在程序中使用活動對象時,首先應該創(chuàng)建一個活動規(guī)劃器對象,并把它安裝到當前線程,也就是把活動規(guī)劃器和當前線程關聯(lián)。 CActiveScheduler::Install()方法可以把活動規(guī)劃器安裝到當前線程, CActiveScheduler::Current() 方法可以獲取和當前線程關聯(lián)的活動規(guī)劃器。如:
CActiveScheduler::Install(CAtv) CActiveScheduler* sco2 =CActiveScheduler::Current(); |
Add()方法可以把活動對象添加到活動規(guī)劃器中,這樣活動規(guī)劃器就可以處理活動對象所提出的異步請求,當異步請求完成事件發(fā)生時調(diào)用活動對象的RunL()方法。
活動規(guī)劃器的 Add() 方法把指定的活動對象添加到當前的活動規(guī)劃器中。但是在某些時候可能需要刪除在活動規(guī)劃器中的活動對象:如果活動對象被銷毀,那么同時活動對象就在活動規(guī)劃器中被刪除了,另外使用活動規(guī)劃器的Deque()方法也可以把活動對象從活動規(guī)劃器中刪除。
在調(diào)用Add()方法之前必須確認活動規(guī)劃器已經(jīng)安裝到當前線程,否則將發(fā)生E32USER-CBase 44 panic。Add()方法包括一個CActive型的參數(shù), 這是一個指向活動對象的指針,在調(diào)用Add()方法時,這個指針不可以為 NULL,否則函數(shù)發(fā)生 E32USER-CBase 48 panic。另外與要注意的是,在使用Add()方法之前確認活動對象是否已經(jīng)添加到活動規(guī)劃器中,如果這個對象已經(jīng)添加到活動規(guī)劃器中那么將發(fā)生E32USER-CBase 41 panic。
CActiveScheduler::Start()方法啟動一個在當前活動規(guī)劃器控制下的的循環(huán)等待。而CActiveScheduler::Stop()則可以終止活動規(guī)劃器的循環(huán)等待。活動規(guī)劃器實際上是構(gòu)建了一個事件處理的框架,它在循環(huán)中使用User::WaitForAnyRequest()等待活動對象的異步請求,當請求事件完成時,就調(diào)用一該請求事件相關的活動對象的RunL()方法。活動對象調(diào)用RunL()時把它放在TRAP下,這樣即使RunL()函數(shù)發(fā)生Leave活動對象也有可以對它處理,執(zhí)行一些必要的內(nèi)存清理。當RunL()發(fā)生異常退出之后,首先進入活動對象的RunError()方法,這個方法是一個需方法,有一個默認實現(xiàn),并返回一個整型的值,如果返回的整形值不是KEeeNone,那么活動規(guī)劃器將調(diào)用自己的Error()方法。Error()方法也有一個默認的實現(xiàn):發(fā)生一個E32USER-CBase 47 panic。
活動對象(Active Object)
CActive類是活動對象(Active Object)的核心類,所有的活動對象類應該從這個類繼承。在CActive 中,封裝了兩個功能:一個是向異步服務提供者發(fā)出異步請求;另外一個是處理請完成事件。一個應用程序中可以包含若干個活動對象,并且是它們的都在活動規(guī)劃器(active scheduler)的控制下運行。
活動對象應該包括一個和請求函數(shù)相關連的請求對象,請求函數(shù)是專門用于完成異步請求的,由于帶有請求函數(shù)的對象提供了異步服務,所以在SDK的文檔里又把他們叫做異步服務提供者(asynchronous service provider)。這里可以舉一個簡單的例子,RTimer中包括了一些請求函數(shù),如After()、At()等。所以RTimer型的對象是一個異步服務提供者。所以在活動對象中,通常會有一個服務提供者成員,它的作用是提出一個異步請求,并異步完成他們的請求。
活動對象的 RunL() 方法用于處理活動對象的請求完成事件。活動對象類作為CActive類的派生類,它必須實現(xiàn)RunL()方法。如果有業(yè)務邏輯需要,RunL()可以發(fā)出另外一個新的異步請求。當請求結(jié)束事件發(fā)生時,這個函數(shù)被活動規(guī)劃器調(diào)用,也就是說,在活動規(guī)劃器的WaitForAnyRequest()函數(shù)結(jié)束以后RunL()函數(shù)被調(diào)用。WaitForAnyRequest()實際上是對User::WaitForAnyRequest()的調(diào)用。
在調(diào)用活動對象的RunL方法前,活動規(guī)劃器需要確定,哪一個活動對象擁有最高優(yōu)先權,然后標記該對象的請求作為請求完成事件。這個優(yōu)先權是在活動對象構(gòu)造的時候指定的。RunL運行在活動規(guī)劃器所設定的TRAP中,如果RunL在執(zhí)行過程中發(fā)生leave那么,那么活動規(guī)劃器將調(diào)用RunError()方法。一旦調(diào)用了活動規(guī)劃器的 Start方法,那么所有的用戶代碼將運行在活動規(guī)劃器的某個活動對象的RunL()函數(shù)或RunError函數(shù)下。
活動對象 RunError() 方法用于處理請求結(jié)完成事件處理函數(shù)RunL() 的異常退出(Leave)。如果活動對象的RunL()函數(shù)發(fā)生Leave,那么活動規(guī)劃器將調(diào)用活動對象的RunError方法。這樣這個發(fā)生錯誤的活動對象就可以完成一些必要的內(nèi)存清理。如果有派生類實現(xiàn)了這個方法,那么就應該對可能的 leave 做出處理,并返回KErrNone;如果返回了其他的錯誤碼,那么CActiveScheduler類的Error()將被調(diào)用。
RunError() 方法的默認實現(xiàn)返回一個異常退出碼,而一旦發(fā)生leave這個異常退出碼是一個非KErrNone的值,這樣可以認為:一般情況下,活動對象的RunL()方法如果發(fā)生leave,那么CActiveScheduler類的Error()將被調(diào)用。
注意:如果活動對象用于錯誤處理,那么在他的派生類中應該提供一個比較好的CActiveScheduler Error函數(shù),以便處理那些錯誤。
活動對象的SetActive()方法用于向活動規(guī)劃器提交一個異步請求。派生類應該在請求發(fā)出以后調(diào)用這個方法。活動對象也可以通過一定的方法來取消請求,但是這里需要說明的是,取消請求只是讓請求提前完成。關于異步請求取消可以使用Cancel()方法。
請求狀態(tài)(TRequestStatus)
服務提供者為了完成服務,需要有一個如何完成服務的信息。TRequestStatus 類的對象用于存放為服務提供者定制的請求完成狀態(tài)。當線程提出請求時,例如RTimer 的After()請求,請求狀態(tài)對象被作為參數(shù)傳遞到請求函數(shù)中。服務完成以后,服務提供者發(fā)送信號給服務請求對象的請求信號量并在請求對象中保存一個完成碼,通常這個完成碼的值是KErrNone或者是其他系統(tǒng)定義的錯誤碼。TRequestStatus 類是密封類,不再被繼承。
活動規(guī)劃器和活動對象協(xié)同工作
這里的代碼和附件中的例子一致,可以下載例子直接在VC6++中調(diào)試。活動規(guī)劃器和活動對象的的協(xié)同工作過程如下。首先應該創(chuàng)建一個與當前線程相關的活動規(guī)劃器:
CATV* sco = new(ELeave) CATV(); // 推入清理棧 CleanupStack::PushL(sco); // 把 sco 活動規(guī)劃器安裝為當前線程的活動對象 // 安裝以后,sco 活動對象專門處理當前線程的請求事件 CActiveScheduler::Install(sco); |
接著創(chuàng)建一個活動對象,TmCount類是一個從CActive類派生的活動對象類。
TmCount* timeCount = TmCount::NewLC(); |
然后可以從NewLC()方法中看到活動對象構(gòu)造的具體細節(jié):
TmCount* TmCount::NewLC() { console->Printf(_L("NewLC\n")); // 使用 C++默認構(gòu)造函數(shù)構(gòu)造 TmCount* result = new (ELeave) TmCount(); // 把活動對象推入清理粘 CleanupStack::PushL( result ); // 調(diào)用兩階段構(gòu)造函數(shù),構(gòu)造活動對象內(nèi)部成員 result->ConstructL(); // 返回活動對象指針 return result; }; |
在這個默認的構(gòu)造函數(shù)中,還調(diào)用了默認的C++構(gòu)造函數(shù),這個比較關鍵,因為正是在這個構(gòu)造函數(shù)中,把活動對象添加到了活動對象,在這個;函數(shù)中,還可以定義活動對象的優(yōu)先權限:
TmCount::TmCount():CActive(0) // 這里可以設置活動對象的優(yōu)先級 { // 把自己加入活動規(guī)劃器 iTimeCounter=1; CActiveScheduler::Add(this);
};
在ConstructL()中,則獲得了一個記時器句柄 void TmCount::ConstructL() { // 初始化計數(shù)器和定時器 iTimeCounter = 0; User::LeaveIfError(iTimer.CreateLocal()); console->Printf(_L("ConstructL\n")); };
|
這樣,活動對象構(gòu)造完成。接著,程序調(diào)用活動對象的請求函數(shù)。
在活動對象的請求函數(shù)中:
// 活動對象的請求函數(shù) void TmCount::StartL(TInt mT) { // 將請求傳遞給異步服務提供者, // 異步服務提供者,調(diào)用請求函數(shù), // 這里需要注意iStatus 這個參數(shù),它是一個請求狀態(tài)對象。 // 設定定時器狀態(tài)為每隔mTime秒鐘狀態(tài)完成一次 iTimer.After(iStatus, 10000 * 100 * mT); // 在調(diào)用異步請求函數(shù)之后調(diào)用SteActive()方法,提交異步請求 SetActive(); }; |
異步服務提供者處理請求,當請求處理完成以后,它生成一個事件。活動規(guī)劃器不斷的檢查是否有請求完成(是否有請求完成事件發(fā)生),如果有請求完成,那么調(diào)用與該請求相關聯(lián)的活動對象的RunL()方法:
void TmCountTwo::RunL() { // 計數(shù)器+1以后繼續(xù)提交延時請求事件 console->Printf(_L("[2]The Count is ->>%d\n"), iTimeCounter++); // 再次提出異步請求 StartL(1); }; |
在這個方法中又提出了一次異步請求,并使計數(shù)器值增加。如果條件符合則可以不再讓活動對象提出異步請求,這是一個修改后的RunL():
void TmCount::RunL() { // 計數(shù)器+1以后繼續(xù)提交延時請求事件 console->Printf(_L("[1]The Count is ->>%d\n"), iTimeCounter++); // 當記數(shù)器值大于等于10時,不再提出異步請求。 if (iTimeCounter<10) { StartL(1); } else { //取消請求 Cancel(); } //User::Leave(9); }; |
如果RunL()在運行過程中發(fā)生錯誤,則RunError()被調(diào)用,以下是RunEror()方法:
TInt TmCount::RunError(TInt aError) { console->Printf(_L("[1]Error\n")); //console->Getch(); return 7; };
如果RunError()方法返回了一個非KErrNone的值那么活動規(guī)劃器的Error方法將被調(diào)用。 class CATV :public CActiveScheduler { public : IMPORT_C void Error(TInt anError) const;
};
void CATV::Error(TInt anError) const { console->Printf(_L("Error:%d\n"),anError); //CATV::Stop(); };
|