S60应用程序的运行依赖于大量的OS组件,例如屏幕绘图和应用程序数据持久性等,可以直接使用OS的窗口服务器或者文件服务器即可。

  

  应用程序核心框架类

  应用程序框架由一套核心类组成,这些类是所有应用程序框架的基础。这些类封装了应用程序和所需OS服务器之间的相互作用。

  第一层:CBase和CActive两个基类,其中CActive也是派生于CBase,而CActive又被第二层的CConEnv派生

  第二层:包含两个基本组件:AppArc和CONE。AppArc代表“应用程序架构”,这些类提供了基本的应用程序结构、将系统信息提交到应用程序的机制,以及使用文件服务器持久化数据。其中的类在命名时都带有前缀“*Apa”。CONE是控制环境的缩写,在这个组件中的类提供用于处理用户输入并创建用户界面的机制--这些类主要用于和窗口服务器进行交互,其中的类在命名时都带有前缀“*Coe”。这一层中的许多类都是抽象类,仅仅定义了一个API的接口。

  第三层:Uikon组件。这是具有丰富功能、非抽象框架类的一般性设备无关实现,并且提供了一个在所有symbian OS上公用的UI库层。一些具体的UI控件(比如列表框和滚动条等)都可以在该层创建,这些控件有时也被称为Eikon控件。UIkon组件中的类在命名时都带有前缀“*Eik”。添加了一个公共的symbian OS实现,和其他Symbian OS UI平台共享。

  第四层:由Avkon类组成,这些类提供了核心的S60 UI功能,例如菜单支持。Avkon控件的类在命名时都带有前缀“*Akn”。在框架上添加S60特有的实现。

  第五层:针对应用程序的层,设计自己的应用程序,添加自定义应用程序实现。

  以上提到的这些类简单来说就是:视图(View)、文档(Document)、应用程序(Application)、应用程序UI(Application UI)。那么一个S60 UI应用程序是如何用这些类来实现其有机程序呢,这个就涉及到一个应用程序框架的初始化问题。

 

 

应用程序框架初始化

  必须创建下面的每个方法,才能提供最小的S60应用程序:

  a、框架入口:所有S60 UI都实现一个全局函数E32DLL()(非UI类的全局入口函数为E32Main),当应用程序启动时,框架将首先调用该函数,该函数也称为DLL入口点,应用程序必须存在该函数。每个S60 UI 应用程序都是一个多态DLL。

  b、新建应用程序实例:让框架调用NewApplication(),该函数是由DLL导出的唯一函数,负责创建应用程序类的一个实例,并返回它的指针,以后框架使用该指针完成应用程序的创建。这里在堆上创建应用程序实例使用的是“new”方法而不是Symbian OS常见的“new (ELeave)”方法,这是因为TRAP harness(陷阱捕捉)这时还没有创立。如果系统不能为新的实例分配内存,那返回的指针就是NULL,应用程序框架能够检查到并处理这个问题。

  c、返回UID:由框架调用AppDllUid()返回应用程序的UID。该函数必须返回在.mmp文件中指定的UID值,该值可用于确定应用程序的实例是否正在运行。如果一个应用程序正在运行,而要启动这个程序的另一个实例,这时该运行的程序就会被切换到前台,而不是重新生成一个。

  d、新建文档实例:框架通过框架调用应用程序类实例的CreateDocumentL()函数获取指向新创建的Document类实例的指针。通过文档实例指针,框架就可以调用文档实例的某些功能,同时文档实例也通过调用自身的NewL()和NewLC()双重静态构造函数(为什么是双重构造,这跟Symbian的异常处理机制有关,而之所以设计成静态函数我现在也还不是很清楚)来创建自己。

  e、是否装入文件:框架重新调用AppDllUid()来观察是否要从文件系统中装入一个文件。(似乎这里跟某些永久存储信息有关,目前项目还没用到这个功能,我也不知道这个该怎么用)

 f、新建AppUi和View实例:框架通过调用文档对象的CreateAppUiL()方法生成了一个AppUi实例并获取指向它的指针。此处AppUi实例创建自身时使用的是C++的默认构造函数并返回实例的指针,之后框架通过这个指针调用AppUi实例的ConstructL()函数来完成构造。而在这个ConstructL()函数中首先调用的是AppUi基类的BaseContructL()函数,处理了读入与该程序相关的资源的事宜,其次是新建需要加载的View类的实例(通过双重构造),如果不止一个View的情况下需要调用AddViewL()函数将新建的View实例加载进来,最后如果是多个View时通过SetDefaultViewL()函数设置缺省的默认视图。如读入一个与该程序相关的资源文件AppUi调用了AppView类的NewL函数来生成其实例,这里也是用的双重构造

  g、 View的重绘:通过以上的创建过程,框架已经拥有了View的无限调度权利,此时如果有一般的system事件框架就可以通过窗口服务器调用View的Draw()函数,至于这些System事件一般指程序启动、应用程序获得焦点或者选项菜单消失等等。在这里需要注意的是编程开发者并不能直接调用Draw,它要求在调用前系统的graphics context处于激活状态,如果编程开发者希望进行屏幕的重绘,则只能调用DrawNow()方法。还有就是Draw函数是不能异常退出的,这是因为该函数能被框架直接调用,而框架是不知道如何处理可能发生的异常的。因此Draw自己需要捕捉和处理可能发生的错误。

  h、 处理命令:完成以上步骤后,用户可以通过比如菜单选项之类的交互UI使框架调用HandleCommandL()函数,并传递一个参数aCommand,它指明了用户所选择的命令,HandleCommandL()会据此来判断不同的命令以分别调度执行。在此处还有一个必须注意的地方是如果要程序的View实例也回应用户的按键,则必须将View实例通过AppUi的AddToStackL()函数加入到AppUi实例的控件堆栈(control stack)中。

 

  对于上面的调用方式实现说明,个人觉得有必要再将AppUi的几个重要事件处理方法罗列一下:

  HandleWsEventL()用于处理窗口服务器传递的事件,它的作用使框架调用下面这些具体事件处理方法;

  HandKeyEvent()用于处理用户按键事件(此函数在控件堆栈为空时调用,否则框架将会调用OfferKeyEventL()函数);

  HandleForegroundEventL()当应用程序切换到前台或从前台切换到后台时调用该函数,默认的实现可以处理键盘焦点的改变;

  HandleSystemEventL()用于处理由窗口服务器生成的事件;

  HandleApplicationSpecificEventL()用于处理自己定义的自定义事件。默认的实现可以处理颜色方案改变的通知;

  HandleCommandL()用于处理用户选择的菜单项;

  HandleSwitchOnEventL()用于处理像设备切换之类的特定切换事件;

  HandleMessageReadyL()用于处理窗口服务器产生的消息事件。

  在以上各函数处理事件中,除了第一个HandleWsEventL()函数外,其它函数默认都是纯虚函数,需要重载才能用。

  通过以上对应用程序框架初始化步骤后,一个完整的框架就创建完毕了

 

 

 

 

posted @ 2010-05-12 20:48 landylee 阅读(110) 评论(0) 编辑

一、基本类:

 1 应用程序视图(View)类:GUI根控件,该类实现主窗口,并充当其他应用程序控件的容器。

 2 应用程序用户界面(UI)类:该类实例化应用程序View类,处理发自应用程序的命令;

 3 应用程序文档类(Application document)类: 该类处理应用程序中非GUI数据-应用程序数据。它还实例化应用程序的UI类;

 4 应用类(Application)类:主要的应用程序类,通过实例化应用程序和启动文档类,来启动应用程序。它也设置应用程序的UID(各个应用程序的唯一标识符)。


二、交互流程

   源文件中各个文件:以“helloworld”为例

1)   helloworld.cpp,该文件包含了应用程序的入口点。所有的DLL都需要E32DLL(应用程序实际就是DLL),虽然它不做任何事情,但必须返回一个成功状态(KErrNone)。Symbian OS应用程序框架调用NewApplication()方法,创建并返回一个应用程序对象指针,该指针在helloworld_app.cpp文件中定义。

2)  helloworld_app.cpp  该文件包含了应用程序类的实现。应用程序启动时,GUI框架调用这些方法。应用程序框架定义并返回应用程序的UID,同时创建并返回应用程序文档对象。

3)  helloworld_doc.cpp 文件, 该文件处理应用程序中的数据。由框架调用CreateUiL()方法,创建并向应用程序UI类传递指针;

4) helloworld_UI.cpp文件,它包含了程序的UI类。GUI应用程序的UI类是进行操作的地方,因为应用程序在这里处理用户事件。所有的用户事件(键盘输入等)来自于UI类的HandleCommandL()方法。

5) helloworld_view.cpp文件,View实例化后,UI框架调用View类的ConstructL()方法。正是这个方法创建了主应用程序窗口,并激活后进行显示。为了在屏幕上绘制控件,框架针对每个控件调用Draw()方法,应用程序的视图是一个控件。

  Model没有映射到Symbian OS的特定类,它的作用在于应用程序的数据和算法。该Model归Document所有,可以调用Document提供的方法来访问它。


三、核心类介绍

 1) 应用(Application):是应用程序的主入口点,它将应用程序相关的信息(如图标、标题等)返回给系统框架。Application自身不包含程序的数据和算法,这部分的类继承自CAknApplication类。

 2)文档(Document):提供了存储数据的环境,该部分的类继承自CAknDocument,文档同时也实例化了一个AppUi类。

 3)应用程序UI(Appllication UI):本身不是一个可绘制的控件,更准确的说,它负责接受信息,是一个框架触发的通告消息的接受器,如对用户按键事件或重要的系统事件进行接收。AppUi会处理此事件,或者传递给View处理。这部分按键事件或重要的系统事件进行接收。AppUi会处理此事件,或者传递给View处理。这部分的类继承自CAknAppUi。

 4)视图(view):这是用户在屏幕上实际看到的视图,在简单的应用情形下,它可以用于显示数据,或者在较为复杂的应用情形下,它能够用于收集用户数据。例如,文字处理应用程序中的编辑器是文本字符键入的地方。此编辑器就是一个Avkon类提供的标准控件。

posted @ 2010-05-12 20:42 landylee 阅读(182) 评论(0) 编辑
1,冒泡法:

public class BubbleSortImpl1 {
public static void BubbleSort(int A[]) {
   int n = A.length;
   for(int i=0;i<n;i++){
    for(int j=0;j<n-i-1;j++){
     if(A[j]>A[j+1])
     {
      int temp=A[j];
      A[j]=A[j+1];
      A[j+1]=temp;//直接调用Swap会出错。why?
     
     }
    }
   }
  
}

public static void swap(int a, int b) {
   int temp = a;
   a = b;
   b = temp;
}

/**
* @param args
*/
public static void main(String[] args) {
   // TODO Auto-generated method stub
   int A[] = new int[] { 2, 5, 3, 9, 7, 1, 30 };

   BubbleSort(A);
  
   for (int i = 0; i < A.length; i++) {
    System.out.println(A[i]);
   }

}

}

2,堆排序

public class HeapSort {
static void HeapAdjust(int H[],int s,int n){//使H[s...m]称为一个大顶堆
   int rc=H[s];
   int j;
   for(j=2*s;j<=n;j=j*2){
    if(j<n-1&&H[j]<H[j+1])
     ++j;//j为父节点的最大孩子
    if(rc>=H[j])
      break;//rc应该掺入在j的父位置上
    H[s]=H[j];//j上移
    s=j;
   }
   H[s]=rc;
}
static void Heap_Sort(int H[]){
   int n=H.length;
   for(int i=n/2;i>0;i--){
    HeapAdjust(H,i,n);
   }//
   for(int k=n-1;k>1;k--){
    int temp=H[1];
    H[1]=H[k];
    H[k]=temp;//将堆顶记录和 当前未经排序子序列中最后一个记录交换。
    HeapAdjust(H,1,k-1);
   }
}
/**
* @param args
*/
public static void main(String[] args) {
   // TODO Auto-generated method stub
        int A[]={0,3,5,9,2,7};
        Heap_Sort(A);
        for(int i=0;i<A.length;i++)
        System.out.print(A[i]);
       
}

}

3,插入排序

public class InsertSortImpl {

/**
* @param args
*/
public static void InsertSort(int A[]) {
   int n = A.length;
   for (int i = 0; i < n-1; i++) {
    int temp = A[i+1];
    Insert(A, temp, i );
   }
}

public static void Insert(int A[], int e, int k) {// 对A[1...k]排序
    while(k>=0&&A[k]>e){
    A[k+1]=A[k];
    k--;
   }
   A[k+1]=e;
}

public static void main(String[] args) {
   // TODO Auto-generated method stub
   int A[] = new int[] { 2, 5, 3, 9, 7, 1, 30,6 };

   InsertSort(A);

   for (int i = 0; i < A.length; i++) {
    System.out.println(A[i]);
   }
}

}

4,快速排序

import java.util.Random;

public class QuickSortImpl {

/**
* @param args
*/
public static void swap(int a, int b) {
   int temp = a;
   a = b;
   b = temp;
}

public static void QuickSort(int A[], int left, int right) {  
   if (left < right) {
    int i = left;
      int j = right;
    int pivot = A[left];
    while(left<right){
     while(A[left]<pivot) {
      ++left;
     }
     while(A[right]>pivot){
      --right;
     }
     if(left<right){
     int temp=A[left];   
     A[left]=A[right];
     A[right]=temp;
     }
    }
    int temp=A[right];
    A[right]=pivot;
    pivot=temp;   
    QuickSort(A, i, right- 1);
    QuickSort(A, right+1, j);
   }
}

public static void main(String[] args) {
   // TODO Auto-generated method stub
   int A[]=new int[]{3,7,2,35,8,9};
   QuickSort(A,0,5);
      for(int i=0;i<5;i++){
       System.out.println(A[i]);
      }
}
}

5,选择排序

public class SortedSortImpl {
public static void SelectSort(int A[]) {// 通过n-i此关键字比较,从
   // n-i+1个记录中选出最小的与第i个记录交换
   int n = A.length;
   for (int i = 0; i < n; i++) {
    int k = i;
    for (int j = i + 1; j < n; j++) {
     if (A[k] > A[j]) {
      k = j;
     }
    }
    int temp = A[i];
    A[i] = A[k];
    A[k] = temp;

   }
}

/**
* @param args
*/
public static void main(String[] args) {
   // TODO Auto-generated method stub
   int A[] = new int[] { 2, 5, 3, 9, 7, 1, 30 };

   SelectSort(A);

   for (int i = 0; i < A.length; i++) {
    System.out.println(A[i]);
   }

}
}


posted @ 2010-05-12 14:21 landylee 阅读(242) 评论(0) 编辑

开发symbian的GUI应用是有模板的,用Carbide C++的工程向导可以自动生成程序的基本框架。不过可能与安装的SDK版本有关,我现在只能生成一个AppUi一个Container的常规模板(以前用2nd FP3时还可以生成多个view多个container的视图模板)。

不管是哪一种模板,它们共同的部分是入口函数、Application和Document。即以下三个文件是所有工程都具有的:

1)有一个与工程同名的cpp文件,它只提供了两个全局函数,是程序执行的入口,负责创造Application。这个文件我们不用管它。

2)有一个以工程名+Application命名的类,派生于CAknApplication,它负责创建文档类并提供应用的UID。一般情况下它也不需要我们操心,但是因为它提供了一个函数OpenIniFileLC,如果我们需要在启动程序时加载ini文件中的配置,可以重载它。

3)有一个以工程名+Document命名的类,派生于CAknDocument,它负责创建Ui类,同样一般情况我们不管它,但是它也提供了一个函数OpenFileL,如果需要加载普通文件,也可以重载它。

下面来看看两种模板的不同点吧。

一. 基于CCoeControl的常规模式

这种模式的特点是一个AppUi类并且对应一个Container类。

它在上述三个文件以外,还有下面两个文件:

4)有一个以工程名+AppUi命名的类,派生于CAknAppUi,它主要的工作是负责用户接口(所谓的UI)并且创建Container类,所以它是我们关注的重点之一。

它最重要的成员函数是HandleCommandL,这个函数来自于CEikAppUi类,负责处理各种命名/事件。

有两个函数DynInitMenuBarL和DynInitMenuPaneL,如果需要动态更改菜单的项目,可以重载它们。

HandleKeyEventL函数则可以在需要自己处理键盘事件时重载一下。

此外,这个AppUi还提供一些比较常用的函数,比如:

Document() 可以获取Document对象指针。

Application() 可以获取Application对象指针。

StatusPane() 可以获取状态栏的指针。

Cba() 可以获取控制栏的指针。

5)有一个以工程名+Container命名的类,派生于CCoeControl,它负责内容的展示,也是我们关注的重点。

如果需要在界面上增加控件类类的东西,都是在这个类中实现,总的来说,它负责所有与界面展示相关的东西。

它有一个Draw函数,但是如果我们是通过控件来展示信息,则这个函数里的代码似乎与我们关系不大,除非我们的界面完全是靠画出来的。

另两个函数ComponentControl和CountComponentControls分别获取控件与获取控件个数,在依赖于控件展示的GUI应用中则更为重要。

因为它实现了接口MCoeControlObserver,所以函数HandleControlEventL也需要实现一下。

利用它的成员iCoeEnv可以取到AppUi类的指针,不过需要强制转型一下,如:STATIC_CAST(CiMusicAppUi*,iCoeEnv->AppUi())->...

二. 基于CAknView的MVC模式

这种模式的特点是在AppUi类与Container类之间增加一个AppView的类,即一个AppUi对象,N个AppView和N个Container。

除了公共的三个文件以外,它包括的文件有:

4) 有一个以工程名+AppUi命名的类,派生于CAknViewAppUi类,其实也是间接派生于CAknAppUi类,它的工作职责与常规模式中的AppUi类也基本相同。

唯一的区别在于它不是直接创建Container类,而是创建AppView类,并且是创建多个AppView类。同时它还需要负责将创建的view加入视图栈里AddViewL。

 

    CiMusicViewMusic* view1 = new (ELeave) CiMusicViewMusic;
CleanupStack::PushL( view1 );
view1->ConstructL();
AddViewL( view1 );    // transfer ownership to CAknViewAppUi
CleanupStack::Pop(); // view1

CiMusicViewFavt* view2 = new (ELeave) CiMusicViewFavt;
CleanupStack::PushL( view2 );
view2->ConstructL();
AddViewL( view2 );    // transfer ownership to CAknViewAppUi
CleanupStack::Pop(); // view2

CiMusicViewWeb* view3 = new (ELeave) CiMusicViewWeb;
CleanupStack::PushL( view3 );
view3->ConstructL();
AddViewL( view3 );    // transfer ownership to CAknViewAppUi
CleanupStack::Pop(); // view3

this->ActivateLocalViewL(KViewMusicId);

5)有多个以工程名+View命名的类,派生于CAknView类。它负责分担AppUi部分事件的处理,所以,它也有HandleCommandL函数。

此外,它的DoActivateL和DoDeactivate两个函数在当前视图激活或失活时被调用,需要重载一下。

在激活时,需要创建视图对应的Container类,并且调用Container的SetMopParent为自己,还要在上层的AppUi中将这个Container加入栈中。一般代码如下:

 

    iContainer = new (ELeave) CiMusicContainerFavt;
iContainer->SetMopParent(this);   
iContainer->ConstructL( AppUi()->ApplicationRect() );
iContainer->listType=type;
AppUi()->AddToStackL( *this, iContainer );

注意,它的AppUi()可以得到它的上层的AppUi对象指针。

失活时则正好相反,需要

if ( iContainer )
...{
AppUi()->RemoveFromViewStack( *this, iContainer );
}
delete iContainer;
iContainer = NULL;

6)有多个以工程名+Container命名的类,派生于CCoeControl并实现接口MCoeControlObserver,所以它的行为与常规模式中的Container类似。

这种模式可以有效地组织应用程序,根据应用的多个功能界面切割成数个模块(视图)。由多个AppView来分担AppUi中的事件处理,负责自己这个视图下的具体行为与显示。

各个视图之间的切换也很简单:

STATIC_CAST(CiMusicAppUi*,iCoeEnv->AppUi())->ActivateLocalViewL(KViewMusicId);

[补充]

三 比较两种模式

有些教材还提出过对话框模式,但是我觉得那似乎不太实用,也没注意过它的结构组成。

对比上述两种架构模板,很显然,第二种基于视图的模板应该更有实用性一点,除非程序实在简单。

不过,这里的“视图”很容易让人产生误解。一般我们说MVC的时候,模型-视图-控制器,但是这里的AppView其实对应的控制器,而Container对应的则是视图。

所以在新的SDK里,第一种模板中由向导生成的工程中源于CCoeControl的类名改成了AppView,对应继承于CAknAppUi控制器的类名叫AppUi。因为我没有看到多视图的工程生成的代码,如果这样的话,估计也应该对应的改一下吧。

posted @ 2010-05-12 14:18 landylee 阅读(657) 评论(0) 编辑