Symbian OS — 应用程序架构(ZZ)

来源:互联网 发布:种植头发的效果 知乎 编辑:程序博客网 时间:2024/05/16 14:33

 

 

 

转载自:http://blog.sina.com.cn/s/blog_448367c90100a36c.html

 

应用程序架构(Application Framework)

 

1、S60应用程序架构

    S60平台在底层Uikon应用程序框架上添加了一个用户界面层(Avkon)。Avkon提供了一套特别为S60设计的UI组件和应用程序框架。

 

1.1、S60应用程序结构

 

1.1.1、模型(Model)—视图(View)—控制器(Controller)模式(MVC)

    MVC模式在S60 UI应用程序中是一个通用的设计模式。应用程序被分离成不同的逻辑部分;它们包装了应用程序的不同方面。每个部分都用特殊的任务。MVC模式分离了应用程序设计,使模型(Model)的代码得到重用。

    模型(Model):

  • 封装了应用程序的状态和功能。
  • 通知视图(View)进行切换。
  • 响应来自视图(View)的状态查询。

    视图(View):

  • 呈现模型(View)。
  • 接收来自模型(Model)的视图更新通知。
  • 将用户的输入发送给控制器(Controller)。

    控制器(Controller):

  • 定义了应用程序的行为。
  • 将用户操作与模型(Model)更新相映射。
  • 响应视图(View)切换请求。

1.1.2、S60应用程序结构和MVC

    S60应用程序通常分离成两大部分,引擎(Engine)和UI。应用程序引擎,也就是应用程序模型,用来处理逻辑运算和数据结构表示。应用程序UI,用来在屏幕上显示应用程序的数据和全部的行为。在基于S60应用程序框架下,实现引擎和UI分离模式有三种方式:传统的Symbian OS应用程序构架、对话框构架、视图切换构架。不同的构架只反映UI的实现,应用程序类(CAknApplication继承类)和文档类(CAknDocument)并没有区别。

应用程序UI的组成:

    CAknApplication继承类:

  •     应用程序框架的启动对象。
  •     定义了应用程序的性质。
  •     创建文档(CAknDocument)类。

    CAknDocument继承类:

  •     创建AppUi(Controller)。
  •     提供了应用程序数据的持久化功能。

    CAknAppUi或CAknViewAppUi继承类(Controller):

  •     基类的选择依赖于应用程序架构。
  •     处理应用程序事件。
  •     控制应用程序模型(Model)。
  •     负责切换视图(View)。

    CCoeControl继承类(View):

  •     显示模型(Model)状态。
  •     接收用户输入。
  •     向控制器通知相关事件。
  •     根据模型(Model)变化更新显示。

应用程序引擎(Engine):

  •     封装了应用程序数据和状态。
  •     封装了非UI依赖的功能,能够在其他UI平台重用。
  •     通常以类库的形式实现。
  •    AppUi直接操作。

1.2、传统的Symbian OS 应用程序架构

 

1.3、对话框(Dialog)架构

 

1.4、视图切换架构

    视图切换架构是一种机制,它允许应用程序注册视图,并且在任意时刻只有一个视图被激活。视图切换架构并没有规定视图的具体内容,而是为视图在屏幕上显示提供了支持。

    与传统的Symbian OS应用程序架构不同的是,AppUi类(Controller)继承自CAknViewAppUi,并且引入了一个新类CAknView作为AppUi(Controller)和容器(Control)之间的媒介。在视图切换架构中,CAknView派生类称为Avkon视图(View),它拥有一个容器。AppUi创建每个Avkon视图并且在服务器端进行注册。切换视图时使用视图UID进行切换。

 

1.4.1、创建视图

    CAknView派生类实例的创建通常在AppUi对象的ConstructL()方法中进行。该方法中将所有的视图进行注册,并设置一个默认视图:

    void CMyViewArchAppUi::ConstructL()

    {

        BaseConstructL();

        CMyViewArchAppView1* view1 = new(ELeave) CMyViewArchAppView1;

        CleanupStack::PushL(view1);

        view1->ConstructL();

        AddViewL(view1); // Transfer ownership to CAknAppUi.

        CleanupStack::Pop();  // pop view1.

        CMyViewArchAppView2* view2 = new(ELeave) CMyViewArchAppView2;

        CleanupStack::PushL(view2);

        view2->ConstructL();

        AddViewL(view2); // Transfer ownership to CAknAppUi.

        CleanupStack::Pop();  // pop view2.

        SetDefaultViewL(*view1);

        // More code

    }

    另外,视图本身并不具有绘制控件的能力,所以每个视图需要包含派生自CCoeControlMCoeControlObserver的控件容器:

    class CMyViewArchAppView1Container : public CCoeControl, MCoeControlObserver

 

1.4.2、Avkon视图类

    每个Avkon视图类就像一个小型的AppUi。它必须提供一个Id()函数,从而系统可以标识这个视图,并且必须实现DoActivateL()和DoDeactivate()函数来完成视图激活和注销时的具体操作,此外还应该实现HandleForegroundEventL()、HandleCommandL()和HandleStatusPaneSizeChange()函数用于处理各种事件。

    DoActivateL()

    当视图被激活时,将调用该函数。该函数负责实例化并显示视图的控件。在视图被注销前可能多次调用该函数,所以实现该函数必须考虑重复实例化控件。

    DoDeactivate()

    当视图被注销时,将调用该函数,负责销毁视图中的控件。只有当应用程序退出时,或者激活同一个程序的另一个视图时,激活的视图才被注销。该函数不能异常退出。

    HandleForegroundEventL()

    该函数只有当视图处于激活状态下才会被调用,也就是在DoActivateL()调用之后和DoDeactivate()调用之前这段时间。当视图到达前台时,视图将接收到HandleForegroundEventL(ETrue)。当视图从前台被移除时,视图将接受到HandleForegroundEventL(EFalse)。只用视图在前台的状态实际改变时才会调用该函数。因为拥有视图的应用程序可能在前台和后台间来回切换多次,所以该函数会被调用多次。函数的实现可能用来设置焦点或控制屏幕更新。

    HandleCommandL()

    当视图的菜单发出消息事件时时,该函数将调用来处理菜单事件。

    HandleStatusPaneSizeChange()

    由于状态面板的改变导致客户区域大小的变化将调用该函数。

    视图在活动期间接收事件的典型顺序:

    1. DoActivateL() 
    2. HandleForegroundEventL(ETrue) 
    3. HandleForegroundEventL(EFalse) 
    4. DoDeactivate()

    成对出现HandleForegroundEventL()可能在视图活动期间多次调用。DoActivateL()可能在DoDeactivate()调用之前多次调用。

 

1.4.3、视图资源(AVKON_VIEW)

    典型情况下,视图都需要拥有菜单项或CBA。通过将视图资源(AVKON_VIEW)的ID传递给视图的BaseConstructL()方法,可以很容易为每个视图提供自己唯一的菜单项。

    RESOURCE AVKON_VIEW

    {
    hotkeys= ;
    menubar= ;
    cba= ;
    }

 

1.4.4、视图切换

    本地视图切换

    本地视图切换就是在同一应用程序内进行视图切换。切换时只需要指定需要切换视图的UID:

    ActivateLocalViewL( TUid::Uid(1) );

    外部视图切换

 

2、事件处理

 

2.1、命令(Commands)事件

    命令事件是由Avkon框架产生,用来响应用户选择菜单项和系统特殊事件。AppUi或Avkon视图将会通过HandleCommandL()方法处理命令事件。

 

2.1.1、AppUi对命令事件的处理

    在传统的应用程序架构中,AppUi的HandleCommandL()方法将处理命令事件。

    void CContainerAppUi::HandleCommandL(TInt aCommand) 
   
    switch (aCommand) 
       
        case EEikCmdExit: 
                {
                Exit(); 
                break; 
               
        case ECmdXXX: 
               
                // implementation of cut operation 
                break; 
               
            ... 
        default: 
        break;
        }
    }

    用户自定义的命令ID(如ECmdXXX)在.hrh文件中定义,Avkon系统命令ID(如EAknSoftKeyBack)定义在avkon.hrh中。EEikCmdExit是系统标准的应用程序退出命令ID。

 

2.1.2、Avkon视图对命令事件的处理

    在视图切换应用程序架构中,当前视图的HandleCommandL()方法将处理命令事件。

    void CMyAppView1::HandleCommandL(TInt aCommand) 
    
    switch (aCommand) 
        
        case EMyAppCmdSwitchToView2: 

                {
            AppUi()->ActivateLocalViewL(KView2Id); 
            break; 

                }
        case ECmdXXX: 
               
                // Implement cut operation 
                break; 
               
        // ... Other view-specific command handling here 
        case EAknSoftkeyBack: 
            
            ((MEikCommandObserver*)AppUi())->ProcessCommandL(EEikCmdExit); 
            break; 
               
        default:

                AppUi()->HandleCommandL(aCommand); 
                break; 
        
    }  

 

2.2、按键事件与控件栈

    按键事件是用户与应用程序交互时键盘所产生的事件。键被按下时被FEP转换成相应的键事件,最终由应用程序框架传递给当前的应用程序。应用程序框架只将键事件传递给在控件栈中的对象。AppUi是缺省的键事件处理对象,但是CCoeControl派生类的UI控件应该第一时间处理键事件。因此必须将控件放入控件栈中,通常情况下由AppUi的AddToStackL()方法将控件放入控件栈中。

    在传统和对话应用程序构架中,在AppUi的构造阶段(Construction()方法)将控件放入控件栈,控件将处于栈顶。当按键事件产生时,应用程序框架首先要求处于栈顶的控件处理事件,如果控件没有处理事件,那么应用程序控件将要求栈中的第二个控件处理,以此类推,当栈中的所以控件都没有处理是,将由AppUi的HandleKeyEventL()方法处理。

    void CMyAppUi::ConstructL()
    {
        BaseConstructL();
        iCurrentView = CMyAppMainView::NewL(ClientRect());
        AddToStackL(iCurrentView); // to enable key events
    }

    在视图切换应用程序架构中,控件放入控件栈通常在视图对象的DoActivateL()中控件完成构造后进行。

    void CMyView::DoActivateL(const TVwsViewId&, TUid, const TDesC8&)
    {
        if(!iUiControl) // iUiControl is CCoeControl-derived UI ctrl
        {
            iUiControl = CMyUiControl::NewL(this, ClientRect());
            AppUi()->AddToStackL(*this, iUiControl); // to ctrl stack.
        }
    }

    当不希望该控件再接收按键事件时,应该使用AppUi的RemoveFromStack()方法将控件移除控件栈。在视图切换应用程序机构中,通常在视图对象的DoDeactivate()方法中实现从栈中移除控件。

    void CMyView::DoDeactivate()
    {
        if(iUiControl)
        {
            AppUi()->RemoveFromStack(iUiControl);

        }
        delete iUiControl;
        iUiControl = NULL;
    }

    在控件栈中的控件响应按键事件是通过调用他们的OfferKeyEventL()方法来实现的。调用的顺序是以控件在控件栈中的顺序进行,最后放入的控件将先调用它的OfferKeyEventL()方法。OfferKeyEventL()方法必须重写,因为它的默认实现不处理任何事件。当控件处理了事件就应返回EKeyWasConsumed,因为如果返回EKeyWasNotConsumed,应用程序框架将使控件栈中的下一个控件来处理。如果所以的控件都返回EKeyWasNotConsumed,那么最后将在AppUi的HandleKeyEventL()方法中处理。

    TKeyResponse CMyUiControl::OfferKeyEventL(const TKeyEvent& aKeyEvent,

                                                                        TEventCode aType) 
   
        TKeyResponse response = EKeyWasNotConsumed;  
        // pass key press events to the listbox. It will typically  
        // consume Up, Down, and Selection keys. 
        if (aType == EEventKey && iListBox) 
       
            response = iListBox->OfferKeyEventL(aKeyEvent, aType); 
       
        return response; 
    }