Symbian编程总结-基础篇-活动对象正解(2)-使用活动对象

本文章由杨芹勍原创,如需转摘请注明出处。谢谢!

上一节里我们已经大致了解了活动对象的基本概念,要使用活动对象机制,需要用到活动对象、活动调度器、异步函数。我们想使用异步函数,要按照应用程序->活动对象->活动调度器->异步函数的流程来使用。接下来我们开始进入实战,使用活动对象。

一、创建活动调度器

我们知道,活动调度器是应用程序和异步函数之间的桥梁,应用程序使用活动对象通过活动调度器去截获异步函数的返回“完成”消息,并以事件的方式通知应用程序。

使用Carbide C++ 1.3,通过模板向导生成的控制台程序自动为我们生成了创建活动调度器的代码:

CActiveScheduler* scheduler = new (ELeave) CActiveScheduler(); CActiveScheduler::Install(scheduler);

CActiveScheduler::Install()方法调用以后,内部代码就会将scheduler指针赋值给CActiveScheduler类内部的静态指针,后面的代码就可以方便的使用CActiveScheduler类的静态方法,如:

IMPORT_C static void Add(CActive* aActive); IMPORT_C static void Start(); IMPORT_C static void Stop();
  • Add()方法:将活动对象加入活动调度器中注册,以备使用
  • Start()方法:启动活动调度器,活动调度器将开始循环等待异步函数返回的通知消息
  • Stop()方法:停止活动调度器

二、创建活动对象

1、我们创建的活动对象必须派生自CActive类,CActive类已经为我们准备好了iStatus成员变量:

public: TRequestStatus iStatus; private: TBool iActive;

另外一个成员变量iActive起着标识作用,证明该活动对象已经请求了异步函数,如:

RTimer::After(iStatus, 1000000); SetActive();

SetActive()方法为基类CActive的方法,其实就是将iActive = ETrue;,用来标识活动对象已经调用了异步函数。所以,我们只要调用了异步函数,在调用异步函数的代码后面应该紧挨着调用SetActive()方法的代码。

2、有两个虚方法必须继承:

virtual void DoCancel() =0; virtual void RunL() =0;
  • RunL方法:活动调度器接收到异步函数返回的“完成”消息后,遍历在其注册的所有活动对象,如果活动对象的iActive = ETrue且iStatus != KRequestPending则调用活动对象的RunL方法,并将iActive设置成EFalse,以防下次轮询时仍然调用此活动对象。
    在这里“RunL”这个名字会让很多人产生歧义,我刚开始接触的时候总以为和J2me中的Runnable接口的run方法差不多。其实在这里把“RunL”改为“NotifyRequestCompleteL”更贴切些。

    再次声明一下,调用异步函数时,参数TRequestStatus& status都是以引用的方式传递的,如:
    IMPORT_C void After(TRequestStatus &aStatus, TTimeIntervalMicroSeconds32 anInterval);

    所以异步函数内部可以改变status的实参,也就是改变活动对象的类成员iStatus。
  • DoCancel()方法:基类CActive中有取消异步函数的方法Cancel(),调用Cancel()后,活动对象会通过DoCancel()方法通知应用程序做取消方法的后期工作,如删除对象及回收指针等。注意:在应用程序中如果想终止活动对象,要使用Cancel()方法调用而不是DoCancel()方法。

3、活动对象的带优先级的构造函数:

基类CActive的构造函数原型如下:

protected: IMPORT_C CActive(TInt aPriority);

此处将传入一个优先级枚举值,枚举值内容如下:

/** Defines standard priorities for active objects. */ enum TPriority { /** A low priority, useful for active objects representing background processing. */ EPriorityIdle=-100, /** A priority higher than EPriorityIdle but lower than EPriorityStandard. */ EPriorityLow=-20, /** Most active objects will have this priority. */ EPriorityStandard=0, /** A priority higher than EPriorityStandard; useful for active objects handling user input. */ EPriorityUserInput=10, /** A priority higher than EPriorityUserInput. */ EPriorityHigh=20, };

当调用CActiveScheduler::Add方法注册活动对象时,活动调度器会按照活动对象的优先级进行排序,插入或添加到活动对象集合中。在此会起到如下作用:当多个异步函数消息同时返回时(多个iStatus同时不为KRequestPending),活动对象调度器轮训集合的时候总是会先找到优先级高的活动对象并调用其RunL方法。

但是在通常情况下,我们会在构造函数传入EPriorityStandard。

三、活动调度器Start方法的伪代码

通过以上两点分析,我们完全可以模拟出CActiveScheduler::Start方法:

void CActiveScheduler::Start() { for (;;) { // 挂起线程直到异步函数消息返回 // 注:活动调度器和应用程序不在一个线程,所以应用程序不会阻塞 User::WaitForAnyRequest(); // 如果异步函数和主程序在不同的线程则RThread::WaitForAnyRequest(); // 当消息返回的时候线程会苏醒 // 以优先级降序的方式检测调度器集合中每个活动对象 for (;;) { // 调用第一个已完成且iActive == ETrue的活动对象事件处理函数 if (activeObject->IsActive() && activeObject->iStatus != KRequestPending) { // 找到一个已准备好处理事件的活动对象 // 重置iActive状态以表明其不再是活动状态了 activeObject->iActive = EFalse; // 在TRAP中调用活动对象的事件处理函数 TRAPD(err, activeObject->RunL()) ; if (err != KErrNone) { // 如果异常则调用活动对象的RunError方法 err = activeObject->RunError(); if (err != KErrNone) { Error(err); } } break; } } } }

 

四、使用活动对象的例子

点击此处下载源代码

此例子将启动一个控制台程序,并使用异步服务类RTimer定时器,每隔一秒在屏幕上显示累加的数字,效果如下:

image

 

五、小结

在这一节中,我们基本了解了活动调度器、活动对象的工作机制及工作流程,在下一节里,我们将深入活动对象的内部,了解其工作原理,进一步加深对活动对象的理解。

六、参考文献

  1. Symbian OS Explained Effective C++ Programming for Smartphones
posted @ 2008-11-23 00:02  musée  阅读(3501)  评论(13编辑  收藏  举报