活动对象( active object )

85
Copyright © 2001-2007 Symbian Software Ltd. Symbian OS 基基 基基基基Active Object

Upload: pekelo

Post on 15-Mar-2016

204 views

Category:

Documents


0 download

DESCRIPTION

 活动对象( Active Object ). 活动对象. 活动对象被用于事件驱动的多任务处理 它们是 Symbian OS 的一个基本部分 本讲要讲解为什么活动对象非常重要 解释它们是如何怎样设计以进行响应式、高效率的处理事件. 活动对象. Symbian OS 中的事件驱动多任务处理 理解同步请求和异步请求的区别之处,并且能够对它们的典型进行区分 认识活动对象的典型应用 —— 处理异步任务并且不阻塞线程时被请求 理解用多线程和多活动对象实现多任务的区别,以及为什么后者在 Symbian OS 编码中要优先使用. Symbian OS 中的事件驱动多任务处理. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1:  活动对象( Active Object )

Copyright © 2001-2007 Symbian Software Ltd.

Symbian OS基础

  活动对象( Active Object)

Page 2:  活动对象( Active Object )

2

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象

活动对象被用于事件驱动的多任务处理• 它们是  Symbian OS的一个基本部分

本讲要讲解为什么活动对象非常重要 • 解释它们是如何怎样设计以进行响应式、高效率的处理事件

Page 3:  活动对象( Active Object )

3

Symbian OS基础课程目标

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象Symbian OS中的事件驱动多任务处理‣ 理解同步请求和异步请求的区别之处,并且能够对它们的典型进行区分‣ 认识活动对象的典型应用——处理异步任务并且不阻塞线程时被请求‣ 理解用多线程和多活动对象实现多任务的区别,以及为什么后者在 Symbian OS 编码中要优先使用

Page 4:  活动对象( Active Object )

4

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

Symbian OS 中的事件驱动多任务处理同步和异步请求• 当程序的函数代码中要调用一个服务请求时,这个服务不是同步执行就是异步执行同步函数 • 执行一个服务直到完成,然后返回其调用者,通常返回的是一个成功或者失败指示异步函数 • 提交一个请求作为函数调用的一部分,并且马上返回到其调用者继续执行• 在一段时间之后请求再完成

Page 5:  活动对象( Active Object )

5

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

Symbian OS 中的多任务处理

调用一个异步请求以后• 调用者可以自由处理其他问题或者简单的等待,后者就是常说的“阻塞”• 一旦服务完成,调用者可以接收到一个信号,显示请求是否成功这个信号就是一个事件• 这段代码被称为事件驱动定时器等待是一个典型的异步调用的例子• 另一个例子是 Read()方法,它在 Symbian OS 的 RSocket类中,这个方法是从远程主机收取数据

Page 6:  活动对象( Active Object )

6

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

Symbian OS 中的线程 

线程被内核以抢占的方式调度• 内核执行已经就绪的最高优先级的线程• 每个线程都可能由于等待一个事件的发生而暂停执行,而在适当的条件下恢复执行

内核控制线程的调度• 允许线程通过时间片分割的方式共享系统资源,如果有更高级的线程处于就绪态,则抢先执行这个高优先级的线程

• 这种总是运行已就绪的最高优先级线程的方式是占先式多任务处理的基础

Page 7:  活动对象( Active Object )

7

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

Symbian OS中的线程

当当前线程挂起时会发生上下文切换• 上下文切换导致了运行时内核调度器的负载• 如果原来的和替换的线程在不同的进程执行,会因为要交换进程内存以及写入缓存而带来更大开销

• 比线程上下文切换要慢 100倍

Page 8:  活动对象( Active Object )

8

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

事件驱动多任务处理

异步事件可发生在如下情况: • 从外部资源——例如用户输入或者硬件外设接收到数据。 • 通过软件——例如由定时器或者完成异步请求时引起。

Page 9:  活动对象( Active Object )

9

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

事件驱动多任务处理事件由事件处理器管理• 事件处理器等待事件,然后处理它• 一个高层的事件处理器的应用是网页浏览器应用等待用户输入和响应,提交请求以接收Web页,并把接收到的页面显示出来网页浏览器使用系统服务器,用以接收来自他客户端的请求。系统服务器接收到请求,并且继续等待另一个请求。在服务请求中,系统服务器依次将请求地交给其他的服务器,它们将稍后产生完成事件。

• 这里描述的每个软件组建都是事件驱动的。它需要对用户输入或者来自系统的请求作出响应• 这样的话很快就会变得很复杂 !

Page 10:  活动对象( Active Object )

10

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

Symbian OS 中事件处理考虑

在响应一个事件的时候,事件处理器可能要请求另一个服务,而这将引发另一个事件(依次类推)• 操作系统必须有一个高效的事件处理模型,从而在事件发生后通过最合适的顺序尽快处理他们

• 对用户驱动的事件进行快速处理及给出响应以提供良好的用户体验是尤为重要的

Page 11:  活动对象( Active Object )

11

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

Symbian OS 中事件处理考虑要避免在事件之间持续的检测• 这样不断的检测会导致大量的功耗,这在电池供能的设备上必须避免操作系统在低能耗状态进行等待• 在等待下一个事件的时候,软件应允许操作系统转入空闲模式

事件处理应使内存资源使用最小化• 处理器资源也要有效的使用活动对象满足这些要求,并提供了一种轻量级的事件驱动多任务处理机制

Page 12:  活动对象( Active Object )

12

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象和活动调度器活动对象和活动调度器( Active Scheduler)• 一起被称为“活动对象框架”• 用于简化异步程序设计,使编写代码更容易:提交异步请求 管理请求的完成事件对结果的处理

• 通常,一个 Symbian OS应用程序或服务包含单个主事件处理线程和与其相关的活动调度器• 在线程中运行一组活动对象• 活动对象有的事件处理方法以供活动调度器调用每个活动对象封装了一个任务 • 它向其服务器提供者请求异步服务,并由活动调度器调用它处理请求完成事件

Page 13:  活动对象( Active Object )

13

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象和活动调度器

活动对象框架被用于调度• 同一个线程中多个异步任务的处理所有活动对象都在同一个线程中运行,因此在活动对象中切换比线程间上下文切换的代价要低• 这就使得活动对象通常是 Symbian OS 中最合适的事件驱动多任务处理机制活动对象在运行中是彼此独立的• 就像在进程中线程的运行时彼此独立的一样• 然而,由于在在同一线程中,活动对象更容易共享内存

Page 14:  活动对象( Active Object )

14

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象和活动调度器

活动对象框架 • 是协作式或者非抢先的多任务处理• 在线程中其他活动对象能够开始执行操作之前,每个活动对象的方法都会运行直到完成当活动对象在处理事件时• 它不能被线程中其他运行的活动对象所抢占• 注意线程本身的调度是抢先式的(见前面的幻灯片)

Page 15:  活动对象( Active Object )

15

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象和活动调度器

一个Win32 应用 (例如在Windows下运行的程序 ) 使用了一个简单的消息循环和消息分发的模式• 在 Symbian OS 中,活动调度器代替了Windows中的消息循环,活动对象的事件处理函数相当于消息处理函数

• 由活动调度器执行的事件完成处理与事件引发的特定动作是解耦的——它们由单个活动对象执行

• 例如: email 发送完成事件——其处理动作就移开了”发送”对话框

Page 16:  活动对象( Active Object )

16

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

实时性考虑说明有些事件要求在有保证的时间内响应• 这被称为“实时”事件处理 • 例如一个实时任务可能被要求保持为一个声音驱动的缓冲区提供声音数据,相应延迟就会导致声音解码的延迟,其结果就是声音的中断

• 另一个典型的实时要求可能更严格,例如底层的电话通信不同任务对实时响应有不同的需求• 这些可以用任务优先级表示 • 高优先级任务总必须能抢占低优先级任务,从而保证符合他们的实时性要求 • 任务要求的响应越短,它应赋予的优先级越高

Page 17:  活动对象( Active Object )

17

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象不适合实时任务

当一个活动对象在处理事件时,它无法被同一线程的另一个活动对象的事件处理程序所抢占• 因此,他们不适合实时任务

在  Symbian OS 中 , 实时任务应当用高优先级线程和进程来实现,并根据相对的实时性要求选择适当的优先级

Page 18:  活动对象( Active Object )

18

Symbian OS基础课程目标

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象CActive 类‣ 了解活动对象优先级别的重要性‣ 知道活动对象事件处理方法 (RunL()) 是非抢占的‣ 知道活动对象的继承特性,以及需要实现和覆盖的函数‣ 了解如何构建、使用和销毁活动对象

Page 19:  活动对象( Active Object )

19

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

  活动对象

引言• 每个活动对象请求一个异步服务,并且在请求过后一段时间处理完成事件• 它也提供一种撤销未完成请求的方法,还有异常情况下的错误处理• 一个活动对象类必须派生自 CActive  类,该类定义在 e32base.h中

Page 20:  活动对象( Active Object )

20

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象类的构造CActive 类是一个抽象类,拥有两个纯虚函数• RunL()  和  DoCancel() – 所有具体的活动对象类必须从 CActive类中继承 ,定义和实现这些方法

• 它还拥有一个 TRequestStatus  成员变量用以接收异步请求的完成结果在构造时• 从 CActive派生的类必须调用基类中的保护型构造函数• 同时传递一个参数来设置活动对象的优先级 • 和线程类似,所有的活动对象都有一个优先级值来决定它们被如何调度

Page 21:  活动对象( Active Object )

21

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

为什么有活动对象优先级 ?

当活动对象关联的异步服务完成时会产生一个事件,活动调度器能侦测到这个事件 .

1. 活动调度器决定每个事件关联的是哪个活动对象2. 然后调用恰当的活动对象去处理事件当活动对象在处理事件时• 直到事件处理函数返回到活动调度器,它是无法被抢占的• 因此很可能很多事件在控制返回到活动调度器之前已经完成……

Page 22:  活动对象( Active Object )

22

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

为什么有活动对象优先级 ?

要解决哪个活动对象是下一个要运行的• 活动调度器通过活动对象的优先级进行排序,而不是根据完成的顺序• 否则,一个具有低优先级的事件,如果他在一个更重要的事件之前完成,那么它就会将高优先级事件的处理推迟一段时间,而这段时间的长度是不确定的

优先级只是用来决定事件处理者运行的顺序的• 如果一个高优先级活动对象收到一个事件处理请求,而此时一个低优先级的活动对象正处理事件,这个低优先级的事件处理器是不能被抢占的

Page 23:  活动对象( Active Object )

23

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

优先级

定义了一系列的优先级• 在 CActive  类中的 TPriority  枚举类• 通常使用优先级  CActive::EPriorityStandard (=0) ,除非有很好的理由不去这么做

Page 24:  活动对象( Active Object )

24

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象类的构造

作为构造的一额外部分,活动对象代码应当调用活动调度器的静态函数CActiveScheduler::Add()• 这可以将活动对象加入到那个线程中的事件处理活动对象列表中• 列表由活动调度器来维护• 列表用活动对象的优先级排序,最高优先级的活动对象在前面

Page 25:  活动对象( Active Object )

25

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象类的构造

活动对象典型的拥有的一个对象的句柄• 向它发出异步完成的请求,例如 RTimer  类型的定时器对象• 这个对象通常被认为是异步服务提供者,它可能需要作为活动对象构造的一部分被初始化• 如果初始化失败,它必须在第二阶段构造中执行

Page 26:  活动对象( Active Object )

26

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

提交异步服务请求

活动对象类 • 会向调用者提供公有的请求发起方法以初始化请求• 这些方法通过建立好的模式向活动对象对应的异步服务者提交请求• 过段时间请求完成,产生一个事件如下所示 ...

Page 27:  活动对象( Active Object )

27

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

提交异步服务请求

1. 检查以前的未完成请求• 请求方法应该检查在试图提交另一个请求之前没有请求已被提交了 . • 每一个活动对象只能有一个已未完成的请求 . 依赖于实现,代码可能有如下几种情况 :

• 如果请求已经发出,就发生致命系统 (这种情况只会因为程序设计错误而发生 )• 如果尝试提交多个请求是合法饿,拒绝提交另一个请求,• 取消先前的请求并提交一个新的请求

Page 28:  活动对象( Active Object )

28

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

提交异步服务请求

2. 请求的结果• 活动对象应该向服务提供者发送请求,传入其  TRequestStatus&  类型的 iStatus成员变量作为参数

• 服务提供者在开始异步请求之前要把这个值赋给  KRequestPending

Page 29:  活动对象( Active Object )

29

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

提交异步服务请求

3. 调用 SetActive() 方法标识对象为“等待”状态• 如果请求被成功的提交了,请求方法将调用 CActive  基类中的 SetActive()方法• 从而向活动调度器指明请求是已经被提交并且是未完成的• 只有在请求已经提交了以后,才能调用这个函数

Page 30:  活动对象( Active Object )

30

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

事件处理每个活动对象类 • 必须实现  CActive  基类的纯虚成员函数  RunL()• 这是当来自异步服务提供者的完成事件发生时调用的事件处理函数• 活动调度器选择了一个活动对象处理该事件时,就调用这个函数RunL() 方法 • 这函数的名字有一定的误导性,因为异步函数已经在运行了 • 或许  HandleEventL()  或者  HandleCompletionL()这样的名字会更清楚的描述其功能

Page 31:  活动对象( Active Object )

31

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

事件处理RunL() 的典型行为 • 通过监测活动对象的 TRequestStatus 对象中的 iStatus  即一个 32位整形值,来判定异步请求是否成功完成

• RunL()  通常或者发送另一个请求或者告知系统中其他对象事件完成• RunL()  的代码复杂程度可能变化很大一旦 RunL() 开始执行• 它无法被其他活动对象的事件处理器所抢占• 正是由于这个原因代码应该尽快完成,这样其他事件才能够无延误的处理

Page 32:  活动对象( Active Object )

32

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

    此图展示了当前活动对象向一个异步服务提供者提交一个请求,一段时间后请求完成了,产生一个事件,而事件被 RunL()处理时基本的动作执行序列

     活动对象              异步服务提供者1. 提交请求并传递 iStatus

3. 对自身调用 SetActive()

2. 设置  iStatus=KRequestPending  并开始服务

4. 服务完成,服务提供者通过 RequestComplete()  来通知活动调度器,并传递一个完成结果

5. 活动调度器对活动对象调用  RunL()  来处理该事件6. RunL()  重新提交另一个请求,并且不可被抢占

   活动规划器

进程或线程边界

进行服务处理

事件处理

Page 33:  活动对象( Active Object )

33

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

撤销未完成的异步请求一个活动对象 • 必须能够撤销任何未完成的、自己所产生的非同步请求• 例如,活动对象所在的应用程序线程将要终止,他必须能够阻止请求CActive 基类 • 实现了一个  Cancel()  方法,该方法调用纯虚方法 DoCancel()然后等待请求尽早完成• 所有的 DoCancel()  实现都应该对异步服务提供者调用恰当的撤销函数

Page 34:  活动对象( Active Object )

34

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

撤销选定的异步请求DoCancel() 也可以包括其他处理 • 但它不应该异常退出或分配资源,而且也不应该执行长时间的操作• 限制该方法只执行撤销工作以及与与撤销关联的需要清除工作,而非实现复杂的功能 ,是一个很好的准则

• 这是因为析构函数应该调用 Cancel() ,而可能已经清除了 DoCancel() 函数所需要的资源

不需要检查 ...• 在调用  Cancel()之前,请求是否已经被被选中的• 这样做也是安全的,即便它当前不是活动的,例如正在等待事件

Page 35:  活动对象( Active Object )

35

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

错误处理从 Symbian OS v6.0 之后 • 活动对象提供了  RunError()  虚方法,当活动对象的 RunL()方法中发生异常退出的时候,活动调度器会调用这个方法

• 该方法接受一个异常退出码作为参数,返回一个错误码用以表明异常退出是否已经被处理了

• 缺省的实现是不处理异常退出,而只是简单的返回传入函数的异常退出码如果活动对象能够处理 RunL()中发生的所有异常退出 • 它应该通过覆写缺省的实现 CActive::RunError()来处理错误然后返回  KErrNone如果在 RunL()中不会发生异常退出,则不必覆盖

Page 36:  活动对象( Active Object )

36

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

错误处理

如果  RunError()返回非  KErrNone 的值,表明异常退出尚未解决• 那么活动调度器会调用它自己的 Error()函数来进行处理活动调度器 • 没有任何可用于执行错误处理的关于活动对象的上下文信息 • 因此,通常更倾向于在相应活动对象的  RunError()  方法中进行错误恢复

Page 37:  活动对象( Active Object )

37

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

析构活动对象

CActive派生类的析构函数应该总是调用 Cancel() • 作为清除代码的一部分来终止选定的请求• 理想的情况是在活动对象持有的其他资源被销毁之前完成该调用,因为这些资源可能会被服务提供者或者 DoCancel()  方法所使用

析构代码 • 应该释放对象持有的所有资源,包括所有异步服务提供者的句柄

Page 38:  活动对象( Active Object )

38

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

析构活动对象基类 CActive的析构函数是虚函数• 对对象进行检查,确保活动对象当前不是活动的• 如果有任何请求被选定,即  Cancel()  没有被调用,那么将会发生致命错误该致命错误捕获所有导致它的程序设计错误 • 如一个请求在处理它的活动对象被销毁后完成了• 这会导致一个“迷失信号” (“ stray signal” ),这时活动调度器无法找到一个活动对象来处理事件

再验证确认活动对象没有已提交的请求被选定后• CActive  析构函数会将活动对象从活动调度器中移除

Page 39:  活动对象( Active Object )

39

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象类的示例下面的例子 • 描述了如何用活动对象包装异步服务 • 这个例子中采用的是 RTimer  服务提供的一个定时器Symbian OS 已经提供了一个抽象活动对象类  CTimer 该类包装了 RTimer ,并且可以继承该类• 但是,这里还是使用本例,因为它用简单明了的方式展示了如何写一个活动对象类下面的幻灯片 • 展示了涉及到的类以及它们和活动调度器的关系

Page 40:  活动对象( Active Object )

40

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象类的示例CActive

iActiveiStatusRunL()DoCancel()RunError()

CExampleTimer NewL()After()RunL()DoCancel()RunError()

CActiveScheduler

Add()...

RTimer

n 1

iTimer

CExampleTimer  及其与  RTimer, CActive  和  CActiveScheduler之间的关系

Page 41:  活动对象( Active Object )

41

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

CExampleTimer 类class CExampleTimer : public CActive {public: ~CExampleTimer(); static CExampleTimer* NewL(); void After(TTimeIntervalMicroSeconds32& aInterval);protected: CExampleTimer(); void ConstructL(); // 两阶段构造protected: virtual void RunL(); // 从 CActive继承 virtual void DoCancel(); virtual TInt RunError(TInt aError);private: RTimer iTimer; TTimeIntervalMicroSeconds32 iInterval; };

Page 42:  活动对象( Active Object )

42

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

CExampleTimer 构造函数CExampleTimer::CExampleTimer() : CActive(EPriorityStandard) { CActiveScheduler::Add(this); }

void CExampleTimer::ConstructL() { User::LeaveIfError(iTimer.CreateLocal()); }

CExampleTimer* CExampleTimer::NewL() {...}

CExampleTimer::~CExampleTimer() { Cancel(); iTimer.Close(); // 关闭句柄 }

 创建异步服务提供者

第二阶段构造,出于整洁,略去代码

Page 43:  活动对象( Active Object )

43

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

CExampleTimer::After()

void CExampleTimer::After(TTimeIntervalMicroSeconds32& aInterval){ if (IsActive()) { _LIT(KExampleTimerPanic, "CExampleTimer"); User::Panic(KExampleTimerPanic, KErrInUse)); } iInterval = aInterval; iTimer.After(iStatus, aInterval); SetActive(); }

   同时只允许提交一个定时器请求,调用者在提交另一个请求之前必须调用 Cancel()

设置  RTimer将该对象标志为活动的

Page 44:  活动对象( Active Object )

44

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

RunL()  和  DoCancel()void CExampleTimer::RunL() { User::LeaveIfError(iStatus.Int()); _LIT(KTimerExpired, "Timer Expired\n"); RDebug::Print(KTimerExpired); iTimer.After(iStatus, iInterval); SetActive(); }

void CExampleTimer::DoCancel() { iTimer.Cancel(); }

事件处理方法如果发生错误就退出并且在  RunError()中处理错误

否则 , 标记完成时间提交计时请求

撤销定时器

Page 45:  活动对象( Active Object )

45

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

错误处理和事件处理

如果没有发生错误 • 事件处理者 RunL()  使用 RDebug::Print()  记录定时器完成情况作为调试输出• RunL()  用存储的间隔值重新提交计时器请求一旦定时器请求已经开始,它将持续运行并且不断被提交• 直到活动对象调用 Cancel()方法将其停止

Page 46:  活动对象( Active Object )

46

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

错误处理和事件处理事件处理者 RunL()负责检查活动对象的 iStatus 结果• 如果  iStatus  的值不是 KErrNone  就异常退出,这样 RunError()  方法可以处理发生的问题

在这种情况下 –错误处理十分简单:• 从请求返回的错误被记录作为调试输出• 这可能在 RunL() 方法中已经完成• 但是这被分离到了 RunError()  方法中,用来演示如何使用活动对象框架将错误处理从时间处理的主逻辑中分离出来

Page 47:  活动对象( Active Object )

47

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

CExampleTimer::RunError

TInt CExampleTimer::RunError(TInt aError) { _LIT(KErrorLog, "Timer error %d"); RDebug::Print(KErrorLog, aError); return (KErrNone); }

该函数在  RunL()发生异常退出时被调用 (aError 包含一个异常退出码 )

记录错误已被处理

Page 48:  活动对象( Active Object )

48

Symbian OS基础课程目标

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象活动调度器‣ 了解活动调度器的角色和属性‣ 知道  CActiveScheduler::Start()  只能在至少有一个活动对象请求发出的时候之后才能被调用‣ 明白线程处理事件失败的典型的原因是可能是活动调度器没有启动或者过早的停止了‣ 理解  CActiveScheduler  可以称作继承 ,以及创建一个派生活动调度器的原因

Page 49:  活动对象( Active Object )

49

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

建立和安装活动调度器

绝大多数线程都有活动调度器• 它通常是由框架隐式创建的 (例如  GUI 框架中的 CONE)

• 服务器代码必须在使用活动对象之前显示创建并启动一个活动调度器 • 基于控制台的测试代码如果依赖了一些使用活动对象的组件,必须在其主线程中创建一个活动调度器

Page 50:  活动对象( Active Object )

50

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

建立和安装活动调度器

建立和安装活动调度器的代码:CActiveScheduler* scheduler = new(ELeave) CActiveScheduler;

CleanupStack::PushL(scheduler);

CActiveScheduler::Install(scheduler);

Page 51:  活动对象( Active Object )

51

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

启动活动调度器一旦活动调度器被建立并且安装完毕后,它的事件处理的循环等待功能将通过调用静态 CActiveScheduler::Start() 启动• 但是调用 Start()  进入事件处理循环后,只有在调用 CActiveScheduler::Stop() 的时候才会返回

在活动调度器启动之前,必须至少有一个活动对象发出的异步请求• 这样,线程的请求信号量被标识并调用 User::WaitForAnyRequest()来完成• 如果没有请求发出,线程只是简单的进入循环等待状态并且不定时的休眠

Page 52:  活动对象( Active Object )

52

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动调度器等待循环当异步请求完成时• 当服务提供者和服务请求者在同一个线程中的时候,异步服务提供者调用

User::RequestComplete()方法• 当它们不在同一个线程中的时候,调用  RThread::RequestComplete()  方法它传递给 RequestComplete()• 与请求关联的 TRequestStatus

• 一个完成结果典型的标准错误代码,如  KErrNone  或者  KErrNotFound

RequestComplete() 将 TRequestStatus 值设定为给定的错误代码• 在请求线程中产生一个完成,通过标志线程的请求信号量

Page 53:  活动对象( Active Object )

53

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动调度器等待循环

当请求已经发出,请求线程将在活动调度器的事件处理循环中运行• 当它没有处理其他的完成事,活动调度器通过调用 User::WaitForAnyRequest()挂起线程

• 该函数将等待线程的请求信号量的标记信号

Page 54:  活动对象( Active Object )

54

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动调度器等待循环当收到信号时• 活动调度器决定哪个活动对象来处理该事件• 它检查活动对象的优先级列表,然后决定标记哪个请求即,把他的 iActive  的布尔值设为 ETrue (在请求被提交并且调用 CActive::SetActive() 之后设定 )

• 活动调度器检查活动对象的 TRequestStatus  成员数据,来确定是否设置了一个除KRequestPending之外值

• 指明活动对象与一个已经完成的请求关联,并且应该调用其事件处理代码

Page 55:  活动对象( Active Object )

55

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动调度器等待循环找到合适的活动对象• 活动调度器清除活动对象的 iActive  布尔标记,并且调用 RunL()  进行事件处理RunL() 处理事件• 进行必要的处理 它也可能提交一个请求或者在另一个系统中生成一个对象

• 注意: 在运行的时候可能产生另一个事件的请求,但是 RunL()  不能被抢占RunL() 完成 •   活动调度器恢复控制权• 然后决定是否有其他请求已经完成

Page 56:  活动对象( Active Object )

56

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动调度器等待循环一旦  RunL()完成后• 活动调度器通过调用 User::WaitForAnyRequest()重新进入事件处理等待循环User::WaitForAnyRequest() • 检查线程的请求信号量 没有线程请求完成: 挂起它如果在以前的 RunL() 还在运行时,信号量指出另一个事件已经发生:则立刻返回,重复活动对象查找和错误处理

Page 57:  活动对象( Active Object )

57

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动调度器等待循环EventProcessingLoop(){ User::WaitForAnyRequest(); FOREVER { if (activeObject->IsActive())&& ActiveObject->iStatus!=KRequestPending) { activeObject->iActive = EFalse;

TRAPD(r, activeObject->RunL()); if (KErrNone!=r) { r = activeObject->RunError(); if (KErrNone!=r) Error(r); } break; } } }

挂起线程直到事件发生

从优先级列表中取得下一个活动对象 并且事件的 iStatus!=KRequestPending

找到一个已准备好处理事件的活动对象重置  iActive  状态以表明其不再活动

在 TRAP中调用活动对象的事件处理

事件处理函数异常退出 ,调用 RunError() 不使用 objectRunError()处理错误调用 CActiveScheduler::Error()

  事件处理完毕,跳出查找循环并在 FOREVER 循环结束处恢复执行

1. 当请求信号亮被通知的时候,线程将苏醒2. 按照优先级降序的顺序检查调度器器中每一个活动对象3. 调用第一个活动且已完成的活动对象的事件处理函数

事件处理循环伪代码 

Page 58:  活动对象( Active Object )

58

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

停止活动调度器活动调度器停止 • 通过调用 CActiveScheduler::Stop(), 通常在 RunL()中调用当调用 CActiveScheduler::Stop()方法完成,即返回时• 对 CActiveScheduler::Start()  的调用也返回停止活动调度器 • 终端线程中的事件处理• 它只应被控制线程的主活动对象调用因此在  GUI 应用中不要这么做

Page 59:  活动对象( Active Object )

59

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

扩展活动调度器CActiveScheduler 是一个具体的类 • 它可以用于 “就是” ,也可以被继承• 它定义了两个可扩展的虚函数: Error()  和 WaitForAnyRequest()

默认情况下 WaitForAnyRequest()函数只是简单的调用User::WaitForAnyRequest()• 但它可以被扩展,例如在等待之前或之后执行一些处理 • 如果函数被覆盖,它必须要么就必须调用基类函数或者直接调用

User::WaitForAnyRequest()

Page 60:  活动对象( Active Object )

60

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

扩展活动调度器如果在 RunL() 事件处理函数中发生异常退出• 活动调度器把错误码传递给 RunError()方法 如果  RunError() 方法无法处理异常退出 • 它将返回错误码并且活动调度器将其传递给自己的 Error()  方法默认的 Error() 抛出致命错误• E32USER-CBASE 47 • 但可以被覆盖以处理错误• 例如,通过调用错误解析器来获得错误的文本描述,并将这些内容显示给用户或写入日志文件中

Page 61:  活动对象( Active Object )

61

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

警告

如果活动对象代码 • 依靠特殊的活动调度器的特殊版本• 那么就无法移植这些代码使其能运行在其他由更基础的活动调度器管理的线程中此外 • 为了扩展活动调度器而添加的代码应该简单易懂• 并且要避免执行复杂或者缓慢的处理而妨碍整个线程的事件处理

Page 62:  活动对象( Active Object )

62

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

没有活动调度器的线程有一些线程有意的不使用活动调度器,因此它不能使用活动对象或者由活动对象组成的组件• Java实现 不支持活动调度器

Java 的本地方法不能使用活动对象 .• C 标准库  (STDLIB)线程是没有活动调度器的,因此标准库代码不能使用活动对象。但表准库代码可被活动对象所使用,例如在初始化代码中或者在 RunL()  中

• OPL不支持活动调度器,并且 OPL 的 C++扩展  (OPXs) 绝不能使用活动对象或者那些使用活动对象的组件

OPL 是一个解释性语言,使用入门级的开发工具,可以进行快速应用开发

Page 63:  活动对象( Active Object )

63

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象取消已发出的请求

• 理解活动对象在正常完成异步请求以及调用 Cancel()结束时,代码中的不同路径

Page 64:  活动对象( Active Object )

64

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

CActive::Cancel() • 调用  DoCancel()的基类• DoCancel()  不能发生异常退出或进行资源分配,因为它将被析构函数调用活动对象内部• 绝不可以直接调用 DoCancel()  方法来撤销一个请求• 应该通过调用 CActive::Cancel()来调用 DoCancel()  并且处理取消事件,详见下一页描述……

CActive::Cancel()

Page 65:  活动对象( Active Object )

65

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

撤销一个已发出的请求

当调用  CActive::Cancel()时发生了什么 ?• 首先检查调用它的活动对象是否有标记过的请求• 这通过检查 iActive  标记是否通过调用 CActive::IsActive()函数来设置过

Page 66:  活动对象( Active Object )

66

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

撤销一个已发出的请求如果活动对象确有已发出的请求 • CActive::Cancel()  调用 DoCancel() – CActive类中的纯虚函数 必须在活动对象类中实现 实现  DoCancel() 时• 不需要有代码来检查是否有发出的请求• 因为如果没有发出的请求, DoCancel()  不会被调用• 通过调用其提供的撤销方法,  DoCancel()必须发送给撤销封装的异步服务提供者的请求

Page 67:  活动对象( Active Object )

67

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

撤销一个已发出的请求调用了  DoCancel()以后• CActive::Cancel()  然后调用 User::WaitForRequest()  传递关于他的 iStatus成员变量

CActive::Cancel() 是一个同步函数 • 直到 DoCancel()返回并且原来的异步请求完成时它才会返回。 因此:DoCancel()  不应该执行长操作线程被阻塞直到异步服务提供者发布一个取消通知,即将 iStatus设置为 KErrCancel

• CActive::Cancel()  重置活动对象的 iActive  成员,以显示不再有已发出的请求了

Page 68:  活动对象( Active Object )

68

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

撤销一个被发出的请求撤销事件 • 由活动对象的 Cancel()方法处理,而不是由活动调度器处理• RunL()  不会被调用CActive::Cancel() 方法执行所有普遍的撤销代码一个派生活动对象类• 只用 DoCancel()  来调用异步服务提供者的适当的撤销函数• 并且执行必要的清除操作

DoCancel() 不可以调用 User::WaitForRequest() • 这将使得线程的信号量计数变得混乱

Page 69:  活动对象( Active Object )

69

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

信号迷失的致命错误

当一个活动对象要被销毁的时候,必须确保没有等待完成的需要处理的事件• 这是因为 CActive的析构函数把活动对象从活动规划器列表中删掉了如果任何标记的请求要在过一段时间才能完成的时候进行销毁,将会产生一个活动对象失去关联的事件• 这将导致迷信号的致命错误

Page 70:  活动对象( Active Object )

70

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

CActive::~CActive()

避免信号迷失错误 • 在把活动对象从活动调度器删除的之前,基类 CActive 的析构函数应检查没有标记的请求• 要强调这个问题,将抛出一个 E32USER–CBASE 40  致命错误这个致命错误比迷失信号的致命错误更容易跟踪

• 鉴于这个原因, Cancel()应该在每个继承自活动对象类的对象中被调用

Page 71:  活动对象( Active Object )

71

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象

后台任务• 理解如何使用活动对象来完成长任务(或者说是后台任务)• 理解如何实现自我完成 (self-completion)

Page 72:  活动对象( Active Object )

72

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

后台任务除了封装异步服务提供者• 活动对象也可以用来实现长任务,这种任务需要在低优先级的后台中运行• 任务必须能分割为若干小的片段 例如,为打印准备数据,执行后台重算和压缩数据

• 这些都要在活动对象的事件处理方法中执行它们必须足够短,因为 RunL()一旦运行就无法被抢占

Page 73:  活动对象( Active Object )

73

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

后台任务活动对象应被赋予一个低优先级 • 诸如  CActive::TPriority::EPriorityIdle (=-100)  决定了任务片段只在没有其他事件要处理的时候才运行 

• 也就是通常所说的间歇运行 如果任务由一系列不同的步骤组成• 活动对象必须跟踪运行的一系列状态• 使用状态机实现

Page 74:  活动对象( Active Object )

74

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

后台任务

活动对象 • 活动对象通过产生自己的事件来调用自己的事件处理函数,从而可以驱动任务• 不再调用异步服务提供者,只是活动对象对自己的 iStatus  对象调用

User::RequestComplete()

• 这样活动对象就将调用它的事件处理函数• 这样的话,活动对象就会不断提交请求,直到整个任务完成

Page 75:  活动对象( Active Object )

75

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

后台任务

示例代码中展示的是一个典型的例子 • 在类声明中展示了所有相关方法 • 但只给出了和这里的讨论相关的实现• 为了简洁,这里省略了错误处理• StartTask(), DoTaskStep()和  EndTask()  执行小的、不连续的任务,可以直接被低优先级活动对象中的 RunL()方法所直接调用

Page 76:  活动对象( Active Object )

76

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

后台任务 : CLongRunningCalculation

class CLongRunningCalculation : public CBase {public: static CLongRunningCalculation* NewL(); TBool StartTask();

TBool DoTaskStep(); void EndTask(); ...

};

TBool CLongRunningCalculation::DoTaskStep() {

... ... ... ... }

   在任务执行前初始化执行一个短小的任务步骤

销毁中间数据

执行一个尖酸的任务步骤,如果还有任务要做则返回 ETrue , 如果任务完成了 ,要返回 EFalse

略去

Page 77:  活动对象( Active Object )

77

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

后台任务 : CBackgroundRecalc  活动对象class CBackgroundRecalc : public CActive

{public:

... public:

void PerformRecalculation(TRequestStatus& aStatus);protected:

CBackgroundRecalc(); void ConstructL();void Complete(); virtual void RunL(); virtual void DoCancel();

private: CLongRunningCalculation* iCalc; TBool iMoreToDo; TRequestStatus* iCallerStatus;};

CBackgroundRecalc::CBackgroundRecalc():CActive(EPriorityIdle) {

CActiveScheduler::Add(this);}

 出于简洁,略去  NewL(), 析构函数等

iCalc  是长线任务—— 其他的活动对象都有一个异步活动提供器

iCallerStatus 用来通知调用者任务完成

创建低优先级任务

Page 78:  活动对象( Active Object )

78

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

后台任务 : PerformRecalculation

void CBackgroundRecalc::PerformRecalculation(TRequestStatus& aStatus)

{ iCallerStatus = &aStatus;*iCallerStatus = KRequestPending;

_LIT(KExPanic, "CActiveExample"); __ASSERT_DEBUG(!IsActive(), User::Panic(KExPanic, KErrInUse)); iMoreToDo = iCalc->StartTask();

Complete();}

void CBackgroundRecalc::Complete() {

TRequestStatus* status = &iStatus; User::RequestComplete(status, KErrNone); SetActive();}

CBackgroundRecalc  是一个高效的异步服务提供者

调试机制 iCalc  初始化任务自完成以产生一个事件

Complete()

将  iStatus 置为完成状态从而对自身产生一个事件

Page 79:  活动对象( Active Object )

79

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

  后台任务 : RunL & DoCancelvoid CBackgroundRecalc::RunL() { if (!iMoreToDo) { iCalc->EndTask(); User::RequestComplete(iCallerStatus, iStatus.Int()); } else { iMoreToDo = iCalc->DoTaskStep(); Complete(); } }

void CBackgroundRecalc::DoCancel() { if (iCalc) iCalc->EndTask(); if (iCallerStatus) User::RequestComplete(iCallerStatus, KErrCancel); }

将后台任务分成小片段处理在任务下次执行或停止时重新提交请求

不需再做了——事件已完成允许 iCalc  清除所有中间数据

通知调用者

提交另一请求并将通过自完成来产生事件

DoCancel

给  iCalc  一个执行清除操作的机会

通知调用者取消操作已发生

Page 80:  活动对象( Active Object )

80

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象常见问题

• 知道一些可能导致迷失信号,无回应的事件处理和线程阻塞的原因,

Page 81:  活动对象( Active Object )

81

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

迷失信号错误

在编写活动对象代码时最常遇到的问题就是“迷失信号”致命错误 (E32USER-CBASE 46)• 它会在活动调度器接收到一个完成事件却无法找到处理该事件的活动对象时产生 • 例如:一个当前处于活动状态并具有一个完成了的 iStatus  结果(由非 KRequestPending 的值指示的)活动对象

Page 82:  活动对象( Active Object )

82

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

迷失信号错误

下列原因都会产生迷失信号:• 活动对象被创建的时候没有调用 CActiveScheduler::Add()  方法• 当向异步服务提供者提交的请求被接受后 ,没有调用 SetActive() • 异步服务提供者多次完成活动对象的  TRequestStatus  成员,要么:因为异步服务提供者程序设计的错误,或者因为在同一个活动对象上提交了不止一个请求

Page 83:  活动对象( Active Object )

83

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

无回应的事件处理

比如说,在一个  UI 线程中使用活动对象进行事件处理时• 事件处理方法必须保持简短,从而可以保证  UI 的快速响应 • 在活动调度器中,不应该有活动对象具有阻止其他活动对象进行事件处理的垄断权

  活动对象应该是“协作的”而且不应该:• 有冗长的 RunL() 或 DoCancel()  方法• 反复地再提交快速完成的请求,阻止其他活动对象来处理事件• 赋予其比实际需要更高的优先级

Page 84:  活动对象( Active Object )

84

活动对象

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

线程阻塞线程可以被阻塞,从而阻止一个应用程序的  UI 继续保持回应, 包括下面几条的许多原因都可以造成线程阻塞 :• 调用 了 User::After(),它将阻塞一个线程直到参数指定的一段时间过去为止• 活动调度器的不正确的使用 在启动活动调度器之前 , 至少必须有一个异步请求通过活动对象被提交,这样一来线程的请求信号量就会被告知,同时  User::WaitForAnyRequest()  将完成 如果没有未完成的请求,线程就只是不确定的进入等待循环和休眠状态

• 使用  User::WaitForRequest()  等待一个异步请求,而不是使用活动对象框架

Page 85:  活动对象( Active Object )

85

Symbian OS基础课程清单

Symbian OS基础Copyright © 2001-2007 Symbian Software Ltd.

活动对象✓ Symbian OS 中的时间驱动多任务✓ CActive 类✓ 活动规划器✓ 撤销标记的请求✓ 后台任务✓ 常见的问题