Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 33
Текст из файла (страница 33)
The client program must ensure that thisis the case, either by refusing to issue another request when oneis already outstanding or by canceling the previous request prior toissuing a new one. We must do this because an active object has noway of handling multiple concurrent requests.• Then, we request the timer to generate an event after aDelaymicroseconds.
The first parameter to iTimer.After() is aTRequestStatus& which refers to the iStatus member that weinherit from CActive. As we explain below, TRequestStatusplays a key role in event handling.• Finally, we indicate that a request is outstanding by calling SetActive().This is the standard pattern for active object request functions: assertthat no request is already active (or, in rare cases, cancel it prior to issuinga new request); issue a request, passing the active object’s iStatusto some function that generates an event; then call SetActive() toindicate that the request has been issued.You can deduce from this that since each active object has only asingle iStatus member, it can be responsible for only one outstandingrequest at a time. You can also deduce that all request functions takea TRequestStatus& parameter.
A TRequestStatus object is animportant partner and we look at it in more detail in Section 6.5.Our UI program calls this function from HandleCommandL():case EActiveHelloCmdSetHello:{iDelayedHello->Cancel();// just in caseiDelayedHello->SetHello(3000000); // 3-second delaybreak;}A MORE IN-DEPTH LOOK AT ACTIVE OBJECTS163In other words, it cancels any request so that the assertion inSetHello() is guaranteed to succeed and then requests a messageto appear after three seconds.When the timer event occurs, it is handled by the active objectframework and results in the active object’s RunL() being called:void CDelayedHello::RunL(){iEnv->InfoMsg(R_ACTIVEHELLO_TEXT_HELLO);}Clearly, this code is very simple; it’s a one-line function that producesan information message with the usual greeting text.The degree of sophistication in an active object’s RunL() functioncan vary enormously from one active object to another.
The application framework’s CCoeEnv::RunL() function initiates an extremelysophisticated chain of processing. In contrast, the function above is asimple one-liner. In all cases, recall that no other asynchronous requestcan be dispatched by the active scheduler until the RunL() functionruns to completion.
The RunL() function should therefore be kept asshort as possible.Error handling for RunL()CActive() provides the RunError() virtual function, which the activescheduler calls if RunL() leaves. You implement this to handle the leave.This means that it is generally unnecessary to TRAP errors from insideRunL() methods. Instead, you should implement the RunError()virtual function and let the active scheduler take care of trapping yourRunL() call.The protocol here is that your function must return KErrNone, whichtells the active scheduler that the leave, or error, has been handled. Adefault implementation is provided by CActive which just returns theleave code:EXPORT_C TInt CActive::RunError(TInt aError){return aError;}Only a return value of KErrNone informs the active scheduler thatthe leave has been handled.
If the leave has not been handled thenthe active scheduler calls the central error handler, CActiveScheduler::Error().164ACTIVE OBJECTSIn UI threads, the application framework installs its own CActiveScheduler-derived class. This subsequently displays the leave as anerror dialog. For example, in S60 it appears as a system error.Let’s take a look at our example RunError() implementation:TInt CDelayedHello::RunError(TInt aError){return KErrNone;}Clearly, in this example, RunL() cannot leave, and all we do isreturn KErrNone to illustrate the point.
However, in more sophisticatedapplications, it is entirely possible that the function could leave andRunError() would then need to do some real work.If an active object’s RunL() calls a leaving function, then that activeobject should provide an override of the RunError() function.Canceling a requestIf your active object can issue requests, it must also be able to cancelthem. CActive provides a Cancel() function that checks whether arequest is active and, if so, calls DoCancel() to cancel it.You should not call DoCancel() directly from elsewhere in yourcode. Instead, you should call Cancel() and let the frameworkdecide if a call to DoCancel() is required.As the implementer of the active object, you have to implementDoCancel():void CDelayedHello::DoCancel(){iTimer.Cancel();}There is no need to check whether a timer request is outstanding,because CActive has already checked this by the time DoCancel()is called.
By canceling the request, we ensure that no call to RunL()occurs. If you use the UI’s cancel function, then the text does notappear.It is also worth mentioning again that it is best practice to always callCancel() in your active object’s destructor. DoCancel() should notleave or allocate resources.A MORE IN-DEPTH LOOK AT ACTIVE OBJECTS165There is an obligation for the asynchronous service provider to provideboth the request function and corresponding cancel function.
Whenrequesting an asynchronous service, you call your service provider’srequest function. Correspondingly, when implementing your DoCancel() function, you must call the service provider’s cancellationfunction.The Start flashing menu itemThe Start flashing menu item causes the text in the center of the displayto start flashing and Stop flashing stops it. The flashing is implementedby an active object that creates regular timer events and then handlesthem.
Its event handling changes the visibility of the text and redraws theview.The behavior that we see when the Start flashing menu item is selectedis handled by the CFlashingHello class. This is slightly more complexthan CDelayedHello because the RunL() function not only handlesthe completion of a timer event, but it must also re-issue the timer requestto ensure that a subsequent timer event occurs and RunL() is calledagain. There is also added complexity because it has drawing code.ConstructionThis is the same as the Set Hello example, except that now we need tosupply a pointer to the application view object, because we do somedrawing.The second-phase constructor, the ConstructL() function, savesthe pointer to the application view object and creates the kernel-sidetimer object in exactly the same way as we did in the Set Hello example.Requesting and handling eventsThe Start(), RunL() and DoCancel() functions of CFlashingHello show how you can maintain an outstanding request.
Here’sStart():void CFlashingHello::Start(TTimeIntervalMicroSeconds32 aHalfPeriod){_LIT(KFlashingHelloPeriodPanic, "CFlashingHello");__ASSERT_ALWAYS(!IsActive(), User::Panic(KFlashingHelloPeriodPanic, 1));// Remember half-periodiHalfPeriod = aHalfPeriod;// Hide the text, to begin withShowText(EFalse);// Issue request166ACTIVE OBJECTSiTimer.After(iStatus, iHalfPeriod);SetActive();}This is called from HandleCommandL() when you select the Startflashing menu item; it begins by asserting that a request is not alreadyactive and ends by issuing a request – just as before.
Since a whole seriesof requests are issued, Start() doesn’t merely pass the half-periodparameter to the iTimer.After(), but stores it as a member variablefor later use.Start() also starts off the visible aspect of the flashing process, byhiding the text (which is visible until Start() is called).When the timer completes, RunL() is called:void CFlashingHello::RunL(){// Change visibility of app view textconst TBool textIsShowing = iAppView.TextIsShowing();ShowText(!textIsShowing);// Re-issue requestiTimer.After(iStatus, iHalfPeriod);SetActive();}RunL() changes the visibility of the text to implement the flashingeffect. Then, it simply renews the request to the timer with the sameiHalfPeriod parameter as before.
As always, the renewed request isfollowed by SetActive().In this way, events are generated and handled and then generatedagain in a never ending cycle. The only way to stop the flashing is toissue a call to Cancel() which, as usual, checks whether a request isoutstanding and, if so, calls the DoCancel() implementation:void CFlashingHello::DoCancel(){// Ensure text is showingShowText(ETrue);// Cancel timeriTimer.Cancel();}We make sure the text is showing, and then cancel the timer. ShowText() is a simple utility function that sets the visibility of the text; itsimply changes iShowText in the application view and redraws theapplication view.Because DoCancel() contains drawing code, it must be executedwhen there is an environment in which drawing is possible.