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 杨芹勍 阅读(1866) 评论(11)  编辑 收藏 网摘 所属分类: Symbian
Body:93.75,BeforeCate:46.875,125

  回复  引用  查看    
#1楼2008-11-23 01:28 | 火星人.NET      
现在博客园是啥东西都有了,牛叉!!!
  回复  引用  查看    
#2楼[楼主]2008-11-23 09:48 | 杨芹勍      
@火星人.NET
恩。搞手机开发的前景是比较乐观滴

  回复  引用  查看    
#3楼2008-11-23 11:04 | 路缘      
支持一下,喜欢看到一些.net之外的东西
  回复  引用  查看    
#4楼2008-11-24 08:56 | JustDI      
难得在博客园里也看到做symbian的
  回复  引用  查看    
#5楼[楼主]2008-11-24 09:01 | 杨芹勍      
@JustDI
以前也做.net,发现越来越不好玩了:)

  回复  引用    
#6楼2009-01-21 14:55 | 天马行空5028[未注册用户]
正在学symbian中,找到了不少资料,这里是一个重点,每篇都一定要看过去,呵呵!博主不会有意见吧!
顺便问个博主是湖北人的吗?

  回复  引用  查看    
#7楼[楼主]2009-02-04 10:40 | 杨芹勍      
@天马行空5028
呵呵,我只是在武汉念的大学而已

  回复  引用    
#8楼2009-03-21 00:31 | Softworm[未注册用户]
您好,看到伪代码里面有这样一个注释:
注:活动调度器和应用程序不在一个线程,所以应用程序不会阻塞

我是用baidu搜索"CActiveScheduler 阻塞主线程"找到您这篇文章的。

我之前看了好多文章也觉得奇怪,为什么用户程序自己调用User::WaitForRequest()就会阻塞,而CActiveScheduler调用就不会阻塞UI呢。

好不容易看到您这儿有这么依据注释,

能不能再帮忙给我更详细一点介绍?多谢!

  回复  引用    
#9楼2009-03-21 01:29 | Softworm[未注册用户]
找了半天,发现AS并不是和应用在不同的线程。

http://www.forum.nokia.com/info/sw.nokia.com/id/759268ae-c2b5-4fed-8e8d-f6f417fc3658/Symbian_OS_Active_Objects_And_The_Active_Scheduler_With_Example_v1_0b_en.zip.html

从这个文档,看出:
The Symbian OS UI application is a process that has a main thread. The application framework installs an active scheduler for the main thread. There are also many active objects in the active scheduler that process application events (keyboard presses and screen update requests) and make callbacks to methods that the application programmer may override. The programmer can use the default active scheduler provided by the application framework.

说的很明确,就是
AS的确是和拥有他的线程一起的,他自己并不是一个单独线程,他也的确会阻塞整个线程,但因为UI等事件(包括用户程序里面的回调处理)都是属于某个AO(用户代码脱离不了框架控制,而框架就是AO化的),因此并不会造成什么用户体验等问题。

以上内容和您分享。:)

我也算解开心头一个疑惑了。

  回复  引用    
#10楼2009-04-03 15:09 | ydyd_net[未注册用户]
看了Softworm的,明白了
谢谢大家的分享

  回复  引用    
#11楼2009-04-10 00:10 | Softworm[未注册用户]
AS的确是和拥有他的线程一起的,他自己并不是一个单独线程,他也的确会阻塞整个线程,但因为UI等事件(包括用户程序里面的回调处理)都是属于某个AO(用户代码脱离不了框架控制,而框架就是AO化的),因此并不会造成什么用户体验等问题。

这句话,自己刚才看了几遍才回过神来。

再描述一下:
就是说UI等事件处理代码所在的AO就是属于这个AS管辖下的,所以AS阻塞在那儿等待AO发出的异步事件搞定,这本来就是原貌。怎么可能会阻塞AO呢?
两者不是同一层面的问题。

乱说一气。呵呵。

发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1339205 3Uwkk03hj18=



相关文章:


相关搜索:
Symbian

相关链接: