Wiley.Developing.Software.for.Symbian.OS.2nd.Edition.Dec.2007 (779887), страница 52
Текст из файла (страница 52)
However, the base destructor requires that no asynchronous event should still be pending – if there is, it generates anE32USER-CBase 40 panic. Therefore the destructor in your derivedclass should call the Cancel() method (as well as any other cleaningup it needs to do).If you want to remove an active object from the active scheduler, butnot destroy it, call CActive::Deque(). This will call the active object’sCancel() method to cancel any outstanding requests, and then removethe active object from the active scheduler.8.8 Active Object ExampleTo illustrate active objects, I’ll present an example based on theSimpleEx program presented in section 2.3.
The original SimpleExdisplays ‘Simple Example’ in the middle of the screen. When you selecta menu item called ‘Start’, it displays an alert dialog indicating that Startwas selected.The example will be expanded here to include an active object. TheStart menu item is changed so that, when selected, it starts a countdown of10 seconds – displaying the progress of the countdown on the screen – atthe end of which it pops up the alert dialog. A Stop item is also added tothe menu and, when Stop is selected, it will stop a countdown, if one is inprogress. (Since active objects are used, the GUI stays responsive duringthe countdown.) The next time Start is selected, it starts the countdownfrom the beginning.8.8.1CCountdown Active ObjectLet’s begin by showing the active object that was added to SimpleEx toimplement the countdown.
The code below shows the declaration of ouractive object class, called CCountdown.class CCountdown : public CActive{public:static CCountdown* NewL(CSimpleExAppView& aAppView);~CCountdown();void StartCountdown();void Stop();protected:CCountdown(CSimpleExApp& aAppView);void ConstructL();void RunL();ACTIVE OBJECT EXAMPLE263void DoCancel();private:TInt iCount;RTimer iTimer;TInt iInterval;CSimpleExAppView& iAppView;};The implementation of the CCountdown active object is shown asfollows:CCountdown* CCountdown::NewL(CSimpleExAppView& aAppView){CCountdown* self = new(ELeave) CCountdown(aAppView);CleanupStack::PushL(self);self->ConstructL();CleanupStack::Pop(self);return self;}CCountdown::CCountdown() // Construct high-priority active object: CActive(CActive::EPriorityUserInput), iAppView(aAppView){iInterval = 1000000; // 1 second interval// Add to active schedulerCActiveScheduler::Add(this);}void CCountdown::ConstructL(){User::LeaveIfError(iTimer.CreateLocal());}void CCountdown::StartCountdown(){// This method is invoked when user selects the start menu item to start// the countdown.if (iCount == 0){iCount=10;iTimer.After(iStatus,iInterval);SetActive();}}CCountdown::~CCountdown(){// Make sure we’re cancelledCancel();iTimer.Close();}void CCountdown::RunL(){_LIT(KFormatString,"-%d-");TBuf<50> buff;buff.Format(KFormatString,iCount);iAppView.UpdateScreenText(buff);if (iCount){iTimer.After(iStatus,iInterval);SetActive();264ASYNCHRONOUS FUNCTIONS AND ACTIVE OBJECTS--iCount;} else{// Replace text with "Simple Example" (set in KSimpleExText).iAppView.UpdateScreenText(KSimpleExText);_LIT(KMessage,"Start Selected!"));CEikonEnv::Static()->AlertWin(KMessage);}}void CCountdown::Stop(){iCount=0;iAppView.UpdateScreenText(KSimpleExText);Cancel();}void CCountdown::DoCancel(){iTimer.Cancel();}Let’s step through the methods and discuss them.
CCountdown::NewL(CSimpleExAppView& aAppView) is the static NewL() methodthat constructs the active object and calls the secondary constructor forit. A reference to CSimpleExAppView is passed to the active object sothat it can draw the countdown text to the application screen area, viathe screen update method UpdateScreenText() that was added tothe application view class.The CCountdown constructor does nothing except set the activeobject’s priority to EPriorityStandard (which is suitable formost active objects you will write). The secondary constructor, ConstructL(), sets up a countdown interval of one second, and initializesthe timer via the RTimer class. ConstructL() then adds the activeobject to the active scheduler. Note that, since this is a GUI application,the active scheduler is already installed.CCountdown::StartCountdown() starts the actual countdownprocess, and is called when the user selects the Start menu item.
StartCountdown() calls RTimer::After() with the one-second interval,passing it the active object’s TRequestStatus iStatus member variable. It then calls SetActive() so that the active scheduler knowsthat the object is waiting for an asynchronous event. Note that if(iCount==0) is there to prevent the countdown from being started whenit is already in progress. This is done since, as I discuss in section 8.9.3,you cannot have two asynchronous functions in progress at a time. Whilethis example will ignore a start command if the countdown is in progress,another option would be to cancel the asynchronous function in progressfirst (by calling Cancel()), resetting iCount to 10, and then start thecountdown again by reissuing the timer asynchronous function call.When the one-second timer has expired, the timer function sets theiStatus variable to KErrNone and signals the calling thread’s requestACTIVE OBJECT EXAMPLE265semaphore.
This causes the active scheduler to invoke the CCountdown’s RunL() method. CCountdown::RunL() calls a new method,implemented in the application view class, called UpdateScreenText() to display the current count on the screen (see section 8.8.2).If zero has not yet been reached, the count is decremented by one andRTimer::After() is reissued along with SetActive(). This willcause RunL() to be re-entered after one second. If the count has reachedzero, the timer function is not reissued, and the Alert Box that reads ‘StartSelected’ is displayed.Note that RunL() does not check the value of iStatus. This isbecause the RTimer::After() function is very simple and no errorconditions exist.
However, for most other asynchronous functions, you’llwant to check iStatus for the status of the asynchronous call. Furthermore, note that I did not override the active object RunError() functionbecause it is not possible for the RunL() in this example to leave.The destructor for CCountdown simply calls Cancel(), which inturn calls DoCancel() if an outstanding request exists, which couldhappen if you exit the application while the count is in progress.When modifying SimpleEx for this example, I put the active objectclass declaration directly in SimpleEx.h and its source in SimpleExui.cpp, but you can create separate files for these.8.8.2Modifications to SimpleExNow, I’ll show the modifications to the existing SimpleEx GUI classes.To simplify, I present the S60 program only. However, active objectsare platform-independent, so the same modifications can be made to theUIQ version.In the SimpleEx header file, I modified the declarations of CSimpleExAppUi and CSimpleExAppView.In CSimpleExAppUi, I added a pointer to the active object as follows:class CSimpleExAppUi : public CAknAppUi{public:void ConstructL();~CSimpleExAppUi();private:void HandleCommandL(TInt aCommand);public:private:CSimpleExAppView* iAppView;CCountdown *iCountdown;};In CSimpleExAppView, I added the method UpdateScreenText(), which is used by the active object to write its countdown266ASYNCHRONOUS FUNCTIONS AND ACTIVE OBJECTSto the screen.
This method takes a descriptor string as an argument, andwrites that string to the center of the screen.Here is the modified application view class declaration:// The Application View Classclass CSimpleExAppView : public CCoeControl{public:static CSimpleExAppView* NewL(const TRect& aRect);static CSimpleExAppView* CSimpleExAppView::NewLC(const TRect& aRect);void ConstructL(const TRect& aRect);void UpdateScreenText(const TDesC16& aText);private:TBuf<100> iScreenText;void Draw(const TRect&) const;};The rest of the SimpleEx classes remain the same.Here is the modified resource file, which adds the Stop item to themenu:RESOURCE TBUF r_default_document_name{buf="";}RESOURCE EIK_APP_INFO{menubar = r_SimpleEx_menubar;cba = R_AVKON_SOFTKEYS_OPTIONS_EXIT;}RESOURCE MENU_BAR r_SimpleEx_menubar{titles ={MENU_TITLE{menu_pane = r_SimpleEx_menu;}};}RESOURCE MENU_PANE r_SimpleEx_menu{items ={MENU_ITEM{command = ESimpleExCommand;txt = "Start";},MENU_ITEM{command = ESimpleExStop;txt = "Stop";}};}ACTIVE OBJECT EXAMPLE267The ESimpleExStop command is added to the command enum inSimpleEx.hrh as follows:#ifndef __SimpleEx_HRH__#define __SimpleEx_HRH__// SimpleEx enumerate command codesenum{ESimpleExCommand = 1, // start value must not be 0ESimpleExStop};#endif // __SimpleEx_HRH__Two existing SimpleEx source files are changed – simpleEx ui.cpp and simpleEx view.cpp.The following shows the modified CSimpleExAppView in simpleEx view.cpp:#include "eikenv.h"#include <coemain.h>#include "SimpleEx.h"CSimpleExAppView* CSimpleExAppView::NewL(const TRect& aRect){CSimpleExAppView* self = CSimpleExAppView::NewLC(aRect);CleanupStack::Pop(self);return self;}CSimpleExAppView* CSimpleExAppView::NewLC(const TRect& aRect){CSimpleExAppView* self = new (ELeave) CSimpleExAppView;CleanupStack::PushL(self);self->ConstructL(aRect);return self;}void CSimpleExAppView::ConstructL(const TRect& aRect){CreateWindowL();SetRect(aRect);ActivateL();}void CSimpleExAppView::UpdateScreenText(const TDesC16& msg){iScreenText.Copy(msg);DrawNow();}void CSimpleExAppView::Draw(const TRect& ) const{CWindowGc& gc = SystemGc();gc.Clear();const CFont* font = iEikonEnv->TitleFont();gc.UseFont(font);TRect drawRect = Rect();TInt baselineOffset=(drawRect.Height() - font->HeightInPixels())/2;268ASYNCHRONOUS FUNCTIONS AND ACTIVE OBJECTSgc.DrawText(iScreenText,drawRect,baselineOffset,CGraphicsContext::ECenter, 0);gc.DiscardFont();}The changes to CSimpleExAppView were to add the UpdateScreenText() method as well as modify Draw() to support it.UpdateScreenText() simply writes the text passed to it to theiScreenText descriptor and forces a screen draw.
Draw() will callgc.DrawText() to write whatever is in this descriptor to the center ofthe screen.The following shows the modified CSimpleExAppUi (in simpleEx ui.cpp):void CSimpleExAppUi::ConstructL(){BaseConstructL(CAknEnableSkin);iAppView = CSimpleExAppView::NewL(ClientRect());iCountdown = CCountdown::NewL(*iAppView);iAppView->UpdateScreenText(KSimpleExText);}CSimpleExAppUi::~CSimpleExAppUi(){delete iAppView;delete iCountdown;}void CSimpleExAppUi::HandleCommandL(TInt aCommand){switch(aCommand){case EEikCmdExit:case EAknSoftkeyExit:Exit();break;case ESimpleExCommand:iCountdown->StartCountdown();break;case ESimpleExStop:iCountdown->Stop();break;}}The main change here was to invoke the active object’s methods tostart and stop the countdown in response to the Start and Stop menu itemcommands.