跟着Code走,详解Symbian活动对象

来源:互联网 发布:php 分割二维数组 编辑:程序博客网 时间:2024/04/26 20:22

from:http://blog.csdn.net/beyondexisting/article/details/5862371

谢谢!

【瞎侃活动对象】

活动对象本身并不复杂,它由两部分构成:活动对象和活动对象调度器。活动对象主要用来进行异步函数调用,活动对象负责调用异步函数,并处理异步函数完成事件,活动对象调度器负责在异步函数完成时,调用正确的活动对象的完成事件处理函数(RunL)。

Symbian程序并不要求一定使用活动对象,你可以完全像写其他平台程序一样写,完全不是用活动对象。但是活动对象为异步函数调用提供了方便。活动对象的执行结构及使用方法都不麻烦,但是主要的问题在于基于活动对象的程序,实际是一种基于状态的程序。对于初学接触活动对象,对于习惯了代码串行执行的编程者来说,当程序中一大堆活动对象不停的进行着状态切换时,你极可能迷失在其中。

一旦你开始使用活动对象,即调用了CActiveScheduler::Start()之后,那么这个线程的执行过程就受活动对象调度器的控制了。你只有在活动对象的RunL函数中能获得执行代码的机会,其他时间都由活动对象调度器来控制。在调用CActiveScheduler::Start()启动活动对象调度器之前,你可以执行一些初始化的代码。这些初始化的代码中,你至少要加入一个活动对象到调度器中,并且这个活动对象处于活动状态。因为活动对象调度器一旦开始运行,你就只有在活动对象的RunL函数中才有机会执行代码。不知你怎样看,这种主动交出代码控制权限的做法?

 

【活动对象实现】

标题是跟着Code走,那么我们还是主要从Code来谈。每个线程都有一个活动对象调度器指针,可能保存在内核线程对象的成员变量中,也可能保存在用户态的线程私有数据中(取决于编译时的宏定义)。这个活动对象调度器,初始是为空的,所以如果你的程序需要使用到活动对象,你首先要创建活动对象调度器,并调用CActiveScheduler::Install。活动对象相关的用户态实现代码在Kernel Package的文件kernel/eka/euser/cbase/ub_act.cpp中。

CActiveScheduler::Install会调用SetActiveScheduler(aManager);,把活动对象调度器指针保存到线程数据中。一个线程在同一时刻只能有一个活动对象调度器,如果你需要使用多个活动对象调度器,你需要在必要的时候设置新的活动对象调度器指针,和恢复旧的活动对象调度器指针。

CActiveScheduler::Add向活动对象调度器中加入活动对象,CActiveScheduler中有一个TPriQue  iActiveQ;的成员变量,用以保存加入的活动对象,请注意这是一个Priority Queue。以下是CActiveScheduler::Add的源码,主要的一句是pS->iActiveQ.Add,当时在这句之前有三个判断。第一个判断当前线程的线程调度器必须非空,你在试用活动对象之前,必须创建活动对象调度器;第二句加入的活动对象必须非空;第三句如果当前活动对象之前已经被加入过,就不能再次加入了。

EXPORT_C void CActiveScheduler::Add(CActive *aRequest)  
    {  
    CActiveScheduler *pS=GetActiveScheduler();  
    __ASSERT_ALWAYS(pS!=NULL,Panic(EReqManagerDoesNotExist));  
    __ASSERT_ALWAYS(aRequest,Panic(EReqNull));  
    __ASSERT_ALWAYS(!aRequest->IsAdded(),Panic(EReqAlreadyAdded));  
    pS->iActiveQ.Add(*aRequest);  
    }

CActiveScheduler::Start()是活动对象调度器的关键代码,它先后调用CActiveScheduler::Run和CActiveScheduler::DoRunL。CActiveScheduler::Run的代码以展开TRAP宏的方式实现了异常处理,在调用CActiveScheduler::DoRunL之前调用了TTrap.Trap,调用正常结束之后调用TTrap.UnTrap。如果调用过程中发生了Leave,会进行调用栈回退,最终回退到调用Trap的位置,并且返回非0,然后当前活动对象的RunError函数就会被调用。(关于Symbian下的异常处理过程,请参考  跟着Code走,详解Symbian清理栈 )

CActiveScheduler::DoRunL是最主要的实现代码,代码如下,是一个双层的嵌套循环。外部循环调用WaitForAnyRequest,保证在调用CActiveScheduler::Stop之前,代码的执行流程都在活动对象调度器的控制之下。异步函数操作完成时的Complete,会设置线程的请求完成信号量,WaitForAnyRequest就会返回。内层的循环在活动对象列表中查找对应的活动对象,这里使用了Priority Queue的迭代方法,所以优先级高的活动对象会被优先找到。

这里需要注意的是查找条件,可能有很多活动对象同时在进行异步调用,活动对象调度器收到线程的请求完成信号后,调用哪个活动对象的RunL呢?while的继续条件是活动对象非Active或者活动对象的状态是KRequestPending,那也就是说只有当活动对象是Active并且状态非KRequestPending时,才找到正确的活动对象。活动对象调用异步函数时,要求调用SetActive,活动对象的状态是什么时候变化的呢?这个是关键,我们后面一段继续说,这里先把调度器的执行流程说完。找到正确的活动对象后,首先会改变活动对象的状态至非EActive和非ERequestPending状态(避免一次异步函数调用,活动对象的RunL多次被调用),然后调用活动对象的RunL。这里并没有对RunL进行TRAP,所以如果代码中有Leave,会由CActiveScheduler::Run中的Trap处理,如果发生错误,当前活动对象的RunError会在CActiveScheduler::Run中得到调用。

void CActiveScheduler::DoRunL(TLoopOwner* const volatile& aLoop, CActive* volatile & aCurrentObj, TCleanupBundle* /*aCleanupBundlePtr*/)  
    {  
    TDblQueIter  q(iActiveQ);  
    do  
        {  
        WaitForAnyRequest();  
        q.SetToFirst();  
        CActive* pR;  
        do  
            {  
            pR=q++;  
            __ASSERT_ALWAYS(pR!=NULL,Panic(EReqStrayEvent));  
            //if the line below panics it's either because you made a request but you haven't  
            //SetActive the object (pR->iStatus.iFlags&TRequestStatus::EActive==0) or you didn't set the iStatus  
            //to KRequestPending (pR->iStatus.iFlags&TRequestStatus::ERequestPending==0)  
            __ASSERT_DEBUG(!(pR->iStatus.iFlags&TRequestStatus::EActive)==!(pR->iStatus.iFlags&TRequestStatus::ERequestPending),Panic(EReqStrayEvent));  
            } while (!pR->IsActive() || pR->iStatus==KRequestPending);  

        pR->iStatus.iFlags&=~(TRequestStatus::EActive | TRequestStatus::ERequestPending); //pR->iActive=EFalse;  
        aCurrentObj = pR;  
        pR->RunL();

        } while (aLoop != KLoopInactive);  
    return;        // exit level  
    }

现在回到前面提到的关键问题,活动对象的请求状态的变化过程是怎样的? 活动对象调用异步函数后,会调用CActive::SetActive(),这个函数中会设置iStatus.iFlags&TRequestStatus::EActive,内核在处理异步请求完成时,会设置iStatus.iFlags&TRequestStatus::ERequestPending(别忘了,异步请求的最后一个参数是TRequestStatus* aStatus)。这样即使活动对象调度器中有多个EActive的对象(即多个活动对象都调用了异步函数,并SetActive了),内核处理完一个异步请求,活动调度器收到请求完成信号,也只会找到唯一对应的活动对象。

 

【适用于活动对象架构的设计模式:状态模式】

使用活动对象进行异步调用,跟使用回调函数比较类似。初始化一些操作之后,就等着框架代码调用你的活动对象的RunL,然后你在RunL中再触发包括异步请求的另外一个操作。这种情况下,你写程序的思路与一般的顺序执行代码会有些不同,更多的是基于状态的改变来设计程序。状态模式在Symbian活动对象框架下会得到很好的使用,其实如果你去看看Symbian内核的源码,你就会了解到系统代码也大量的使用了状态模式。关于状态模式的使用,这里就不废话了,此类专业文章太多太多。


原创粉丝点击