




已阅读5页,还剩10页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Symbian编程总结-基础篇-活动对象正解(1)-理解活动对象 ZZ Symbian OS中的活动对象的使用无疑是最基础的、最频繁的、最重要的。什么是活动对象呢?大家学习一个新的事物时,总是会将这个新的事物与自己认知的事物相比较,从而达到快速学习的目的。我开始学习Symbian的时候,我查看很多Symbian书籍、网上很多Symbian教程都将活动对象与多线程联系到一起,我也总是会把活动对象想象成一个线程。然而,经过了更深入的接触,我发现并不像我想象的那样。现在,我在此向你保证:活动对象和多线程没有任何关系!不要拿平时做多线程的思想去理解活动对象!comments:废话,如果是的话,为什么叫活动对象这个新词呢,还不如就叫thread来的方便,所以啊,显然不是。大家都知道,process之间的切换是有overhead的,为了提高performance,人们就搞了thread来,thread一下子提高了不少性能,但是呢,在symbian os只主要的用在mobile device的,RAM小,CPU是ARM7/9只有300M左右,所以resource就显得更相当的宝贵和重要了,一个thread在内核中大概需要4KByte的空间开销,在用户空间有8KB用于程序栈的空间开销,而AO只需要几百个Byte而已,甚至更小,所以很帅气吧活动对象可以按照以下步骤这么理解:1, Symbian OS中提供了很多异步函数,这些异步函数大多部分都是基于“服务器-客户端”架构的。这里与win32 api中的函数有很大的不同。如:win32中,CSocket:Receive(是recv而不是WSARecv)为同步函数,线程阻塞在Receive处,直到套接字接收到了网络流才返回。Symbian OS也有类似的函数,RSocket:Recv,但是此函数是一个异步函数,线程不会阻塞在Recv处而会继续执行。 comments:其实,Symbian-OS是一个严重的C/S架构的系统,很多的service都是通过server来完成的,资源都是server来给client统一集中管理的。2, 如何区分Symbian中的函数哪些是同步的哪些是异步的?很简单:看函数内是否包含类型为TRequestStatus的形参,如果有则函数为异步函数。如RSocket:Recv的函数原型为: IMPORT_C void Recv(TDes8 &aDesc, TUint flags, TRequestStatus &aStatus);3,参数aStatus为一个状态位,初始值为ERequestPending(值为1),它意味着用户请求的操作是否执行完毕。如:我们调用异步函数RSocket:Recv请求接收网络流,Recv函数会直接返回。当“接收”网络流的过程完毕后,aStatus会变为EActive,所以我们只要监视aStatus是否不为ERequestPending就可以知道“接收”是否完成了。 4, 我们可以使用以下伪代码完成以上所述操作:TRequestStatus status(KRequestPending);RSocket:Receive(aDesc, flags, status);for (;)if (status != KRequestPending) break;/ 此处我们已经通过RSocket:Recv完成了类似CSocket:Receive的同步的工作5, Symbian OS不建议我们使用以上方法,它建议我们使用异步方法,而不要使用我们这种方法去强制同步,活动对象就是帮我们做这件事情。活动对象体系帮我们监视aStatus的值,只要aStatus != ERequestPending,他就会以事件的方式通知我们,告诉我们“Socket已经接收完毕,你可以去取数据了!”,活动对象就是干这事的。 comments:其实真正的不是这样的,是异步service provider在request完成的时候会把请求者的iStatus的状态改为KErrNone,然后AS会检测到这个事件,从来调用相应的AO来做后续的事情。6, 总结一下:系统中有一个“活动调度器”,我们建立一个“活动对象ao1”,将该对象与某个系统中的异步函数绑定,然后将该活动对象注册到“活动调度器”中,“活动调度器”会等待异步函数返回的“完成”消息。收到完成消息后,调度器遍历所注册的活动对象,如果发现status != KRequestPending则找到该status对应的“活动对象ao1”,调用其中的RunL方法,以事件的方式告知我们异步函数已经执行完成。 在下一节我会介绍如何使用活动对象。Symbian编程总结-基础篇-活动对象正解(2)-使用活动对象 zz 在上一节里我们已经大致了解了活动对象的基本概念,要使用活动对象机制,需要用到活动对象、活动调度器、异步函数。我们想使用异步函数,要按照应用程序-活动对象-活动调度器-异步函数的流程来使用。接下来我们开始进入实战,使用活动对象。一、创建活动调度器 我们知道,活动调度器是应用程序(个人觉得应该是AO才对)和异步函数之间的桥梁,应用程序使用活动对象通过活动调度器去截获异步函数的返回“完成”消息,并以事件的方式通知应用程序。使用Carbide C+ 1.3,通过模板向导生成的控制台程序自动为我们生成了创建活动调度器的代码:CActiveScheduler* scheduler = new (ELeave) CActiveScheduler();CleanupStack:PushL(scheduler); / add thisCActiveScheduler: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 representingbackground 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 objectshandling user input.*/EPriorityUserInput=10,/*A priority higher than EPriorityUserInput.*/EPriorityHigh=20,;当调用CActiveScheduler:Add方法注册活动对象时,活动调度器会按照活动对象的优先级进行排序,插入或添加到活动对象集合中。在此会起到如下作用:当多个异步函数消息同时返回时(多个iStatus同时不为KRequestPending),活动对象调度器轮询集合的时候总是会先找到优先级高的活动对象并调用其RunL方法。(前后矛盾的说,重复的说)但是在通常情况下,我们会在构造函数传入EPriorityStandard。(按照你的需求来设置)comments:作者为了在这里表述,使用了一些不是太准确的说法,理解作者的良苦用心,作者是想多说些,说的多点,清楚点,但是有时简明准确一点更好!因为,如果你说错了,即使后面纠正了,也会有个先入为主的概念在里面,体现思路不清楚的问题。三、活动调度器Start方法的伪代码通过以上两点分析,我们完全可以模拟出CActiveScheduler:Start方法:void CActiveScheduler:Start()for (;)/ 挂起线程直到异步函数消息返回/ 注:活动调度器和应用程序不在一个线程,所以应用程序不会阻塞User:WaitForAnyRequest(); / 如果异步函数和主程序在不同的线程则RThread:WaitForAnyRequest(); (搞错了吧,RThread有这个方法?应该是User那个,只有在调用RequestComplete()的时候才会区分是一个线程还是不同的线程,从而调用不同类的方法。)/ 当消息返回的时候线程会苏醒/ 以优先级降序的方式检测调度器集合中每个活动对象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定时器,每隔一秒在屏幕上显示累加的数字,效果如下:五、小结在这一节中,我们基本了解了活动调度器、活动对象的工作机制及工作流程,在下一节里,我们将深入活动对象的内部,了解其工作原理,进一步加深对活动对象的理解。Symbian编程总结-基础篇-活动对象正解(3)-活动对象的工作原理 zz 在上一节里我们已经知道如何创建和使用活动对象,大家对活动对象的创建、使用都有了一定的了解。在这一节里我将深入活动对象机制,分为“活动对象的工作流程”、“信号迷失错误”两个部分,为大家剖析活动对象的工作原理。一、活动对象工作流程首先我们用时序图来说明一下应用程序、活动对象、活动调度器及异步函数服务器之间创建及调用的流程:Comments:这个图有两个地方有问题。一:5,6处,结合下面作者写的详细说明,个人觉得作者,写了个无关重要的5,而把表面同样的,但是重要的东西给遗忘了,或者简单的说就是把5放错的地方,应该是先写6,然后把5,放在异步函数服务器那端,因为iStatus作为函数参数传递到异步函数服务器时,这个server会首先把iStatus的值变为KRequestPending(值为OX8000001),然后放到自己的request队列里面去,告诉自己这里还有个请求要做,如果当前有其他的请求需要完成的话。二:10,11处顺序反了,应该先是11,当异步函数服务器完成了request后,如果APP和异步函数服务器在同一个进程则调用User:RequestComplete()方法,如果在不同的进程里面则调用:RThread:RequestComplete()方法,此方法会把iStatus的值变为相应的完成值(如果出错可能会是,KErrNone或是KErrNotFound等等),并以告知请求信号量的方式,在请求线程中产生一个完成事件,然后活动对象调度器得到通知后会根据AO的iActive和iStatus值来调用相应的RunL()方法。下面我们针对每一个步骤结合代码(点击此处下载代码)进行说明:1、创建并安装活动调度器: CActiveScheduler* scheduler = new (ELeave) CActiveScheduler(); CleanupStack:PushL(scheduler); CActiveScheduler:Install(scheduler);如果创建了一个基于GUI应用程序框架的应用程序,框架已经为我们创建并且安装了活动调度器,我们可以直接使用CActiveScheduler的一系列方法。2、创建活动对象iMyAO = CMyActiveObject:NewL(*console);此处创建的CMyActiveObject类是继承自CActive类的活动对象。3、将活动对象添加到活动调度器中void CMyActiveObject:ConstructL() . CActiveScheduler:Add( this); / Add to scheduler 可以看到,活动对象在通过“二阶段构造”创建时就已经将自己的指针添加到了活动调度器中。4、StartLStartL为应用程序请求活动对象调用异步函数的方法,此处用户可以根据自身需求对此方法重新命名:void CMyActiveObject:StartL(TTimeIntervalMicroSeconds32 aDelay) Cancel(); / 取消异步函数请求 iStatus = KRequestPending; /个人觉得其实这个没有必要 iTimer.After(iStatus, aDelay); / 在此处调用异步函数 SetActive(); / 将成员变量iActive = ETrue 因为不能保证用户在等待异步函数调用完毕返回的时候不重新调用StartL方法,所以在StartL方法的入口点首先调用Cancel()方法取消异步请求,否则可能会发生臭名远扬的“信号迷失”错误。5、iStatus = KRequestPending在以上代码StartL方法中,异步函数调用之前,首先要将iStatus设置为KRequestPending以便活动调度器遍历时匹配。6、请求异步函数并发送iStatus在StartL方法中,iTimer.After(iStatus, aDelay);这行代码传递了成员变量的引用并调用了异步函数iTimer.After。7、SetActive调用基类CActive的SetActive方法,方法内部将iActive设置成ETrue,以便活动调度器遍历时匹配。8、启动活动调度器CActiveScheduler:Start();10、查找对应的活动对象我们在上一节已经通过分析并还原了CActiveScheduler:Start()方法的伪代码,此代码块在另外一个线程遍历所有向调度器注册的活动对象,查看对象的iStatus不为KRequestPending且iActive为ETrue。因 为异步函数服务器完成了请求的工作以后,会改变iStatus的实参,使其不等于KRequestPending,再加上活动对象在调用完异步函数返回后 马上改变了iActive值为ETrue,所以活动调度器只要判断iStatus != KRequestPending & iActive = ETrue则可以知道哪一个活动对象所请求的异步服务已经完成。9、WaitForAnyRequest()等待异步函数返回通知,11、RequestComplete()并将iStatus的值改变当异步服务已经完成了所请求的工作,它会使用User:RequestComplete()发送一个通知,活动调度器会通过 User:WaitForAnyRequest()或RThread:WaitForAnyRequest()(我特地google了一下,除了作者提到这个API外,其他结果都没有啊)收到这个通知,随后遍历向其注册的条件 匹配的活动对象(第8点下划线部分说明)。12、调用RunL方法如果找到了异步函数所对应的活动对象,则调用活动对象的RunL方法,RunL方法在TRAP宏里运行。如果RunL方法抛出异常,活动调度器会自动调用活动对象的RunError方法(先调RunL()后调RunError())。因此大多数情况下不需要在RunL内编写异常捕获的代码。活动对象调用完RunL方法后,将活动对象的iActive值设置为EFalse,以便在下次遍历集合时跳过已经处理过的活动对象。如果没有找到异步函数所对应的活动对象,活动调度器将抛出一个“信号迷失”异常。13、CActiveScheduler:Stop()停止活动调度器,停止监控异步函数返回的信号。二、活动对象专属错误-“信号迷失”从上面的分析我们知道,活动调度器会拦截异步函数返回的消息,随后遍历活动对象集合找到相应的活动对象调用,如果没有找到则抛出“信号迷失”异常。有以下两种方法会导致“找不到活动对象”的发生:1. 活动对象根本没有注册到活动调度器中:没有调用CActiveScheduler:Add方法注册活动对象 2. 不满足活动调度器查找活动对象的标准iStatus != KRequestPending & iActive = ETrue:在调用异步函数后,没有调用SetActive()方法将iActive的值设为ETrue;或者将iStatus同时传给两个异步函数,导 致第二个异步函数返回时,当前活动对象的iActive为EFalse,调度器找不到相应的活动对象。 Comments:其实是这样的,一个AO提供的发出异步请求的方法是提供给用户使用的,所以,你不可以告诉用户说,我的这个请求在没有回来之前,你是不可以再调用的。其实根本的原因是一个AO只能同时处理一个异步请求,如果你想同时有多个请求你就得要有多个AO才行。对于同一个异步请求调用多次,如果是确定上次已经完成了,再接着调用,那是没有任何问题的,这就像很多情况下都是这么做的一样:在RunL里面再发异步请求。但是如果不是这样的呢?就要取决于需求是什么了,总的来说有三个options,一是,出错,并且取消当前的请求后立即返回,二是忽略该请求,就是再次调用的话,如果AO处于Active状态就不管了,三是cancel掉前一次请求,再重新发一次请求。这三个都是可以实现的,如果不必像作者写的那个在很多的函数开始先调用Cancel()方法。三、小结在这一节里,我们学习了活动对象的内部工作原理,让我们对活动对象处理机制有了更深入的了解。在下一节里,我们将学习如何同步调用现有的异步函数。Symbian编程总结-基础篇-活动对象正解(4)-异步函数的同步调用 zz 在上一节里 我们深入了解了活动对象、活动调度器及异步函数服务器的工作原理及运行机制,想必大家对活动对象的机制及体系结构的运用已经了如指掌。但是大家有没有觉得异步函数使用起来比较麻烦呢?难道非要使用活动对象,非得以异步的方式调用异步函数吗?这一节将为大家解决这个问题:异步函数的同步使用。 一、使用CActiveSchedulerWait类 在以前的文章Symbian编程总结-界面篇-打开jpeg/gif/png图像里我们已经看到了CActiveSchedulerWait类的使用方法,在此我再详细介绍一下。很多初学者在开始时会将CActiveScheduler和CActiveSchedulerWait类弄混,CActiveScheduler是活动对象的调度器,而CActiveSchedulerWait可以简单的理解为一个当前线程的阻塞器: 调用CActiveSchedulerWait:Start()方法时,线程阻塞; 调用CActiveSchedulerWait:AsyncStop()方法时,请求停止对线程的阻塞 因此,我们在不修改原来活动对象代码的情况下,只要简单的在异步函数调用方法后加上CActiveSchedulerWait:Start(),在 活动对象的RunL方法的开头加入 CActiveSchedulerWait:AnsycStop()就可以了。针对上一节教程介绍的控制台应用程序,我们对以下几个方法(下划线为修改部分)进行修改:点击此处下载源代码CActiveSchedulerWait* iWait; void CMyActiveObject:ConstructL() User:LeaveIfError(iTimer.CreateLocal() ); / Initialize timer CActiveScheduler:Add( this); / Add to scheduler iWait = new (ELeave)CActiveSchedulerWait; CMyActiveObject:CMyActiveObject() Cancel(); / Cancel any request, if outstanding iTimer.Close(); / Destroy the RTimer object / Delete instance variables if any if (iWait-IsStarted() iWait-AsyncStop(); delete iWait; iWait = NULL; void CMyActiveObject:StartL(TTimeIntervalMicroSeconds32 aDelay) Cancel(); / Cancel any request, just to be sure iTimer.After(iStatus, aDelay); / Set for later SetActive(); / Tell scheduler a request is active iWait-Start(); / 第1点 void CMyActiveObject:RunL() iWait-AsyncStop(); / 第2点 TBuf outputStr; outputStr.AppendNum(iCount); iConsole.Write(outputStr); iConsole.Write(_L(n); iCount+; 使用CActiveSchedulerWait的几点注意事项: 1. CActiveSchedulerWait必须结合活动对象使用,而且使用方法只有以上代码介绍的那一种; 2. Start方法和AsyncStop方法必须成对出现; 3. 程序退出时要检查CActiveSchedulerWait是否在IsStarted()状态,如果是则调用 AsyncStop方法。否则程序不能正常退出; 4. CActiveScheduler类内部有自己的静态指针,提供的静态方法都调用了内部的静态指针。而CActiveSchedulerWait 类没有内部静态指针,方法也不是静态的,我们必须自己管理CActiveSchedulerWait类的全局指针,在这点上程序要经过良好的设计。 二、使用User:WaitForRequest方法 如果不想使用活动对象,也不想使用难于管理的CactiveSchedulerWait,你可以使用User:WaitForRequest方法。以下是User:WaitForRequest方法的原型:IMPORT_C static void WaitForRequest(TRequestStatus& aStatus); 此方法将等待异步函数服务器返回的信号量,然后匹配aStatus参数。如果接收到的信号与参数aStatus一一匹配,则跳过阻塞进入下一行代码,否则继续阻塞线程直到aStatus对应的信号通知返回。User:WaitForRequest还有一个重载的方法,它可以监视两个信号的通知:IMPORT_C static void WaitForRequest(TRequestStatus& aStatus1,TRequestStatus& aStatus2); 有了User:WaitForRequest,异步函数使用起来就变得非常方便,我们不需要创建活动对象,也不需要创建成员变量 TRequestStatus,只需要声明局部的TRequestStatus、局部的异步函数类,在异步函数调用之后,加入 User:WaitForRequest(status),就能够使线程阻塞在User:WaitForRequest处直到status对应的异步 函数处理完成。LOCAL_C void DoTestL() RTimer timer; CleanupClosePushL(timer); User:LeaveIfError(timer.CreateLocal(); TRequestStatus status(KRequestPending); / 调用异步方法并将stat
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 海水养殖土壤改良关联(若涉及相关领域)创新创业项目商业计划书
- 家政中介社交媒体营销创新创业项目商业计划书
- 植物防水材料创新创业项目商业计划书
- 榛子果仁果冻生产线创新创业项目商业计划书
- 2025老板表示:“从未听闻兼职员工需签订合同”模板范本
- 2025双务合同范本范文
- 2025新版商铺联营合同范本
- 2025年工会知识竞赛题库和答案
- 初中数学变量函数教学设计
- 2024年秋八年级历史上册 第六单元 中华民族的抗日战争 第19课 七七事变与全民族抗战说课稿 新人教版
- (必会)中级《审计理论与实务》近年考试真题题库(300题)
- 食品安全与日常饮食智慧树知到期末考试答案章节答案2024年中国农业大学
- 烘焙与甜点制作
- T-CRHA 028-2023 成人住院患者静脉血栓栓塞症风险评估技术
- 线路光缆施工方案
- 弹塑性力学讲稿课件
- 心怀国防梦争做好少年中小学生国防教育日主题班会课件
- 《运动的快慢》速度、平均速度与瞬时速度课件
- 地基事故案例分析
- 2023淘宝村研究报告
- 国家开放大学《财政与金融(农)》形考任务1-4参考答案
评论
0/150
提交评论