您的位置:首页 > 移动开发

Symbian应用程序运行框架

2008-01-07 19:12 351 查看

Symbian应用程序运行框架

1.1.1.   Symbian应用程序的启动过程

考虑到Symbian作为一个商业的开放操作系统,它的UI框架结构和功能必须达到易用、强大和可靠的统一,不是简简单单完成人机交互而已。所以它的结构必须是经过精心设计的。因此,要想详细描述其内在的运行过程,一般方法是通过自顶向下并逐步分解来详细介绍。但这同时也存在一个缺点,就是容易忽视各种模块之间的交互过程。所以本文将以一个应用程序启动、运行和结束这样一个流程将UI的整体框架串接起来,相信这样可以更容易理解。当然读者最好已经熟悉Symbian应用程序框架,要知道什么是The UI Control Framework (CONE)以及Application Architecture (APPARC),这样理解起其内在机制更容易一些。
我们先看一个应用程序的入口函数:
LOCAL_C CApaApplication* NewApplication()
{
return new CXXXApplication;
}
 
GLDEF_C TInt E32Main()
{
return EikStart::RunApplication( NewApplication );
}
可以看出,在E32Main()函数中,调用了EikStart::RunApplication( NewApplication )函数,该函数的参数是指向NewApplication函数的指针。
我们先看EikStart中RunApplication函数的声明
IMPORT_C static TInt RunApplication(TApaApplicationFactory aApplicationFactory)
这里有一个工厂类TApaApplicationFactory,该类可以看作是建立应用程序的工厂。在E32Main()函数中,调用的是EikStart::RunApplication( NewApplication )函数,参数是指向NewApplication函数的指针。那和TApaApplicationFactory有什么关系呢?我们看看apparc.h中TApaApplicationFactory的定义:
class TApaApplicationFactory
{
public:
typedef CApaApplication* (*TFunction)();
public:
IMPORT_C TApaApplicationFactory(TFunction aFunction);
……
原来在调用EikStart::RunApplication过程中,编译器创建一个TApaApplicationFactory对象,以指向NewApplication函数的指针为TApaApplicationFactory构造函数的参数。即编译器调用IMPORT_C TApaApplicationFactory(TFunction aFunction)作为TApaApplicationFactory的构造函数。
好,TApaApplicationFactory对象已经创建了,我们现在深入IMPORT_C static TInt RunApplication(TApaApplicationFactory aApplicationFactory)函数,来看看它是如何启动并运行UI程序的。
TInt err = KErrNoMemory;
CEikonEnv* coe = new CEikonEnv;
这是RunApplication函数最先执行的代码,很简单,它在堆上创建了一个CEikonEnv对象,了解Symbian的都知道。这个对象是Symbian UI框架中CONE的基础。既然它又是第一个被调用的UI框架组件,我们必须对它的功能有一个详细的了解。
首先我们看来看看它的类关系:
 



图 1 CEikonEnv类关系
CEikonEnv继承于CCoeEnv,而CCoeEnv则继承于CActive,从这里可以看出CEikonEnv就是一个以事件驱动为基础的异步调用操作,这在后面还会介绍。
在CEikonEnv* coe = new CEikonEnv中,CEikonEnv的构造函数里执行了这样的代码:
EXPORT_C CCoeEnv():CActive(EActivePriorityWsEvents)
{
……
iCleanup = CTrapCleanup:New()
……
}
可以看到它装载了清除栈,那么从现在开始清除栈就可以使用了。接下来它又执行了下面一段代码:
If(coe != NULL)
{
TRAP(err, coe->ConstructAppFromCommandLineL(
aApplicationFactory, *aCommandLine));
}
顾名思义,该函数负责整个application框架的初始化工作,具体细节在这里不一定介绍,主要强调过程及如何与底层相互衔接。对于初始化这部分工作,CEikonEnv 主要是通过在该函数内调用基类的CCoeEnv::ConstructL,那我们来看看它主要完成了哪些工作:
u     创建Active Scheduler,将自身作为Active Object加入到Active Schelduler
u     创建与Window Server的连接RWsSession
u     创建RWindowGroup,作为应用程序的根窗口
u     创建CWsScreenDevice对象
u     创建CWindowGc对象
下面我们一一介绍每一步骤的功能:
1)      创建Active Scheduler
该函数首先创建了Active Scheduler,将自身作为Active Object加入到Active Schelduler,这样CEikonEnv就可以异步的负责处理从Window Server来的标准事件(如键盘或是触摸屏事件)和重绘事件。但是Active Scheduler在这里还没有被启动,所以暂时还没有事件被处理。
2)      创建Window Server的连接
Window Server是UI处理的核心组件,它采用的是标准SymbianC/S模式,其主要功能有:
a)       处理键盘、触摸屏事件及窗口绘制事件,并将它发到相应的客户端的请求代码。
b)      负责SymbianUI的窗口绘制和管理,采用树形结构。包括窗口的建立,刷新和销毁。
c)       提供客户调用API:RWsSession,并提供其它相应的插件以方便用户扩展,例如Animation、Sprites和Cursor。
CEikonEnv通过定义Window Server的客户类的成员变量RWsSession来与Window Server进行通信。其初始化过程便在这里执行。
3)      初始化RWindowGroup
RwindwoGroup是用来在Window Server内创建窗口组(window group)的,窗口组是一种特定的不能被显示的Window,它仅作为应用程序的根窗口。且键盘和事件的焦点和它联系在一起,这样的话Window Server就知道已经有一个应用程序已经和它产生联系,需要在适当的时候将按键等事件发给应用程序
4)      创建一个与文件服务的RFs连接以便于读取资源文件。例如,RSS文件。
5)      创建图形上下文一个是CWsScreenDevice,另一个是CWindowGc。
Window Server无法负责具体的应用程序屏幕绘制功能,而是应由应用程序间接的控制Window Server来绘制图形。所以这里有两个类,作为CEikonEnv的成员函数提供给应用程序来完成它的图形会制功能,一个是CWsScreenDevice,另一个是CWindowGc。CWsScreenDevice实际上是一个虚拟的屏幕设备,储存着屏幕的大小及各种参数。CWindowGc是用来提供窗口绘图环境,比较常见。具体可以参考Symbian SDK。
完成初始化之后,会执行如下函数:
CEikDocument* const doc = STATC_CAST(CEikDocument*, iProcess->    AddNewDocumentL(aApplicationFactory));
这段代码最终创建了CApaApplication和Document及整个应用程序框架,我们来看看到底如何创建的,首先AddNewDocumentL的参数是TApaApplicationFactory,该对象前面已经讲过是如何创建的,于是在AddNewDocumentL中首先会执行如下函数:
CApaApplication* TApaApplicationFactory::CreateApplicationL() const
{
CApaApplica
a2c9
tion* application = NULL;
……
// create application
Application = (*reinterpret_cast(iData))();
}
其中TFunction的定义前面已经讲过,也就是说在应用程序中的定义的NewApplicaion函数在这里终于被执行到了,CApaApplication子类的对象已经创建。接下来继续调用CApaApplication的CreateDocumentL函数就可以创建CApaDocument子类的对象。在CApaDocument子类的对象被创建好以后,会接着调用CEikAppUi* CEikDocument::CreateAppUiL()函数,这个函数是纯虚函数,是应用程序提供用来建立CEikAppUi对象的。
接下来,被创建的CEikAppUi对象会初始化对View Server的连接并建立相应的视图,这在多视图应用程序中会被用到。

1.1.2.   Symbian应用程序的运行过程

好,我们再回到EikStart::RunApplication,在TRAP(err, coe->ConstructAppFromCommandLineL(aApplicationFactory, *aCommandLine))后会执行这样一段代码。
Coe->Execute();
该函数是这样的:
TRAPD(exitCondition, CActiveScheduler::Start());
这时候Active Scheduler被启动了,CEikonEnv作为CActive的子类,就不断的开始响应Window Server传来的事件。于是整个应用程序就开始真正的工作了。我们来看看具体的运行过程。
如果了解Symbian应用程序结构,就知道CEikonEnv只是应用程序和Symbian UI资源交互的一个桥梁或环境,属于CONE,本身并不具体处理应用程序的逻辑。它只是建立应用程序运行环境并不停的从Window Server去获取该应用程序的事件。它将事件还是交给APPARC来处理,所以我们需要结合APPARC和CONE来说明。
首先,注意到CEikonEnv继承于CActive,自然我们就需要知道RunL函数是怎么工作的,
EXPORT_C void CCoeEnv::RunL()
{
Switch (iStatus.Int());
{
Case KErrNone:
break;
……
TWsEvent event;
iWsSession.GetEvent(event);
const TUint handle = event.Handle();
if (handle)
{
CCoeControl* const window = IsHandleValid(handle)?REINT    ERPRET_CAST(CCoeControl*, handle):NULL;
iLastEvent= event;
iAppUi->MonitorWsEvent(event);
iAppUi->HandleWsEventL(event, window);
}
}
RunL从Window Server取出TWsEvent事件,并对其调用iAppUi->HandleWsEventL(event, window), iAppUi的类定义是CEikAppUi,即APPARC中一个非常重要的UI类,负责所有与UI相关的工作。它的HandleWsEventL就会去处理来自于Window Server的事件。
那么HandleWsEventL中又是如何处理的,我们以Keydown事件为例:
EXPORT_C void CCoeAppUi::HandleWsEventL(const TWsEvent&aEvent,CCoeControl* aDestination)
{
Tint type = aEvent.Type();
switch(type)
{
……
Case EEventKeydown:
If(iStack->OfferKeyL(*aEvent.Key(), (TEventCode)type)==EKeyWasNotConsumed)
HandleKeyEventL(*aEvent.Key(), (TEventCode)type);
……
}
……
}
首先我们来看iStack,iStack的类定义是CCoeControlStack,该堆栈存储了所有属于此应用程序的CCoeControl,当CEikonEnv拿到Window Server与该应用程序相关的事件时,会调用CEikAppUi基类CCoeAppUi的HandleWsEventL函数。对于Keydown事件,如果iStack中的CCoeControl没有消耗掉该Keydown事件,就会调用CEikAppUi基类的CCoeAppUi 的虚函数HandleKeyEventL(*aEvent.Key(), (TEventCode)type),这个虚函数经常被实际应用程序重写。通过这个实例,我们就可以大概了解Symbian应用程序的运行过程。
最后我们还需要知道如何订阅Window Server的事件,否则CEikonEnv这个Active Object无法进行异步调用。它是在CActiveScheduler里进行Window Server事件的订阅,CONE采用的Active Scheduler不是标准的CActiveScheduler,是继承于CActiveScheduler的扩展CCoeScheduler,它重写了WaitForAnyRequest函数:
EXPORT_C void CCoeScheduler::WaitForAnyRequest();
{
iCoeEnv->ReadEvent();
User::WaitForAnyRequest();
}
可以看到,CCoeScheduler的不同之处在于在等待其它线程唤醒时,一定要执行iCoeEnv->ReadEvent()这个函数,它的内部实现是一个异步函数,订阅了来自于Window Server的事件。这样有事件从Window Server过来就会执行CEikonEnv的RunL函数,执行完当CActiveScheduler进行WaitForAnyRequest等待时,就会再次向Window Server订阅事件,So on and so forth。
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息