考虑到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框架组件,我们必须对它的功能有一个详细的了解。
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
{
CApaApplication* 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
<script language="JavaScript">
function initUD(){
UD.body=$("_$$_inner_layer");
UD.layer=$("_$$_outer_layer");
UD.window=$("blog-163-com-body");
UD.bar=$("theme_selector_pointer");
}
</script><script language="JavaScript">
// <![CDATA[
var g_urlPrefix="";
function g_initCommon(){
var _arr = ["rmdtpxt","rmdtplgn"];
for (var i=_arr.length-1;i>=0;i--){
_o = $(_arr[i]);
if(i==0)_o && (_o.onclick=function(){exitAfterLogged();return false;});
else if(i==1)_o && (_o.onclick=function(){showLoginDlg('fuyinshitu.blog.163.com');return false;});
}
topFromUrsLogin();
}
function g_initUtil(){
TopSearch.init();
}
// ]]>
</script><style type="text/css">
<!--/*--><![CDATA[/*><!--*/
#messageZone {top:0px; right:20px; background:#ffffae; width:100px; position:fixed!important;
position:absolute;font-family: Arial,Helvetica,sans-serif; padding:4px;font-size:12px;}
/*]]>*/-->
</style>
<script src="http://st.blog.163.com/js/utils/InfoAlertPad.js" type="text/javascript"></script><script language="javascript">
function initAll(){
try{
initUD();
g_initCommon();
initFlash(1);
showPageTopBar({serverName:'fuyinshitu.blog.163.com',serverHostName:'blog.163.com'});
initPage();
window.setTimeout("g_initUtil();",3000);
}catch(e){}
}
initAll();
</script><script language="JavaScript">
function onVisitorInfoLoad(_p){
var _s = $('visitorInfoJs').readyState;
if (_p && _s!='loaded'&&_s!='complete') return;
gLoadPermaLink(1, 'prev', 'fks_084074086087088074086087095095083082089067084082094070', 20, -100,
'Symbian应用程序的启动过程', 'blog/static/74800485200862310245913', '74800485200862310245913', 'http://q.163.com',
0, 1, 'off'
);
topFromUrsLogin();
}
var ver = new Date().getTime();
document.write('<script type="text/javascript" onreadystatechange="onVisitorInfoLoad(true);" onload="onVisitorInfoLoad();" id="visitorInfoJs" src="http://blog.163.com/js/static/visitorInfo.js?host=fuyinshitu&mode=prev&channel=blog&blogId=fks_084074086087088074086087095095083082089067084082094070&pubTime=1216779845913&v=' + ver + '"></'+'script>');
</script><script id="visitorInfoJs" onreadystatechange="onVisitorInfoLoad(true);" src="http://blog.163.com/js/static/visitorInfo.js?host=fuyinshitu&mode=prev&channel=blog&blogId=fks_084074086087088074086087095095083082089067084082094070&pubTime=1216779845913&v=1229489686421" type="text/javascript" onload="onVisitorInfoLoad();"></script>
相关推荐
所以本文将以一个应用程序启动、运行和结束这样一个流程将UI的整体框架串接起来,相信这样可以更容易理解。当然读者最好已经熟悉 Symbian应用程序框架,要知道什么是The UI Control Framework (CONE)以及Application...
(1)核心应用程序类. 所有的S60 UI应用程序都具有一些基本功能: ◆提供一个用户界面,用于显示信息并允许用户进行交互 ◆响应各种用户启动的事件,比如用户选择一个菜单项 ◆响应系统启动的不同事件,比如导致屏幕...
一个应用程序的组件可以在另一个应用程序中用作其他用途。你甚至可以将Android内置的组件替换为自己改进后的版本。这将在移动领域掀起新一轮的创造风潮。 众多开箱即用的内置服务。基于位置的服务使用GPS或手机...
第一篇 基础篇第1章 Android概述Android概述,讲述了Android的前生后世、架构和特点、Android Market、应用程序组件和Android与Java ME的区别及联系 第2章 Android开发基础Android开发基础,讲述了Android开发...
通过手机上网下载以及使用短信定购的方式,直接在手机的个人文档或者应用程序中找 到(根据手机各不相同),无需在安装,直接可以在类似"我的文件夹"之类的选项找到。 使用电脑下载就需要将文件传送到手机上。 ...
3.应用管理:手机接入后能识别手机中已安装的应用程序,并提供安装、卸载和升级的功能。 4.手机连接到搜狗手机助手应用中心,提供免费应用推荐和应用下载。 5.联系人:手机接入后能读取手机中的联系人信息。 ...
797.6 动态缓冲区 807.7 小结 82第8章 使用活动对象的事件驱动多任务 848.1 多任务基础 848.2 事件驱动多任务 858.3 使用活动对象 878.4 示例代码 908.5 没有活动调度器的线程 938.6 应用程序代码...
OMAP(开放式多媒体应用平台)是TI公司推出的一款面向多操作系统(包括PalmOS5.0,PocketPC2002和通信领域的Symbian)的高性能低功耗处理器。它集成了包括一个数字协处理器在内的多媒体单元,并且加入GSM/GPRS接口...
1.2 安装 安装前保存所有打开的文档并退出所有运行的应用程序。您可以在设备上直接安装 ESET Mobile Security 或使用计算机来安装它。 成功安装后,通过遵循 产品激活部分中的步骤激活 ESET Mobile ...
Android 是一个专门针对移动设备的软件集,它包括一个操作系统,中间件和一些重要的应用程序。 Beta 版 的 Android SDK 提供了在 Android 平台上使用 JaVa 语言进行 Android 应用开发必须的工具和 API 接口。 特性 ...
9. 启动应用自动开启wifi 10. 应用不提示自动更新 11. 一直处于加载状态 12. 待审核的帖子依旧显示问题 13. 加密相册访问限制问题 14. 设置权限后未生效的问题 15. 个人头像上传无法替换问题 16. 部分手机...
Activity Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务,是一个负责与用户交互的组件 SSH 为 Struts+Spring+Hibernate的一个集成框架,是目前较流行的一种Web应用程序开源框架。...
3.2启动程序和程序列表 3.3对列表操作的函数 3.4随机选择和随机数 3.5图像保存和处理 3.6绑定快捷键 3.7网页源码查看器 3.8联网提交数据 3.9对文件的操作 4.0制作一个截图软件 4.1局部变量和全局变量 4.2资源管理器...
应用程序自动随着手机旋转 ·短信、Gmail、日暦,浏览器的用户接口大幅改进,如Gmail可以批量删除邮件 ·相机启动速度加快,拍摄图片可以直接上传到Picasa ·来电照片显示 3) Android 1.6 代表机型HTC Hero G3 在...
的记录的标签时,应用程序会自动启动。 C++ 类封装了 NFC 功能,并在运行时向用户界面的 QML 文件注册。 这允许使用信号和插槽在 UI 和 NFC 引擎之间进行直接和轻松的通信:在 QML 中创建一个 NdefManager 元素并对...
应用程序检查设备的 NFC 可用性。 目前支持以下消息类型进行写入: 带有 URI、标题*、动作* 和图像* 的智能海报(* 可选) URI 记录,使用所有定义的 URI 缩写自动编码。 文字记录; 可以在源代码中调整默认的英语...
句,通过串行口输出到GPS应用软件。用鼠标或键盘控制Virace而模拟实际GPS接 收器的运动,或者重播已有的GPS航迹文件,从而在室内测试GPS导航软件等。 Virace具有手动实时控制及航迹重播两种方式。为了控制灵活方便...