Symbian:
Symbian程序一般分为两类:Server程序、UI程序
- Server程序:Server程序一般无UI,并且一般不在应用程序列表中显示启动图标,运行时在后台运行。
Server程序主要包括派生于CServer2的Server实现类及派生于CSession2的Client服务类。Server程序启动时,会创建CServer2派生类的实例,并在系统中以特定的名称注册(通过调用CServer2::Start(const TDesC& name))。Client通过Server名称与Server创建连接,并获得与Server连接的Session Handle,之后的请求都通过Session Handle完成。Server收到Client的连接请求时,会为Client建立对应的服务Session,之后在该Session中处理所有的Client请求。
典型的Server程序启动代码:
GLDEF_C TInt E32Main()
{
CTrapCleanup* cleanup = CTrapCleanup::New();
if (cleanup == NULL)
{
return KErrGeneral;
}
CActiveScheduler *pA = new CActiveScheduler;
CActiveScheduler::Install(pA);
CXXXServer *pS = CXXXServer::NewL();
TInt err = pS->Start(KXXXServerName);
CActiveScheduler::Start();
delete pS;
delete pA;
delete cleanup;
return KErrNone;
}
典型的CServer2派生类代码:
class CXXXServer : public CServer2
{
public:
CXXXServer (CActive::TPriority aActiveObjectPriority);
CSession2* NewSessionL(const TVersion& aVersion,const RMessage2& aMessage) const;
static CXXXServer * NewL();
static CWTPServer* NewLC();
~CXXXServer ();
……
};
CSession2* CXXXServer ::NewSessionL(const TVersion& /*aVersion*/, const RMessage2& /*aMessage*/) const
{
CXXXServerSession* session = new (ELeave) CXXXServerSession(this);
return session;
}
典型的CSession2派生类代码:
class CXXXServerSession: public CSession2
{
public:
CXXXServerSession(CXXXServer* aServer);
void ServiceL(const RMessage2& aMessage);
CXXXServer* iServer;
……
};
void CXXXServerSession::ServiceL(const RMessage2& aMessage)
{
TInt ret = KErrNone;
switch (aMessage.Function())
{
case :
……
break;
……
break;
default:
break;
}
aMessage.Complete(ret);
}
典型的Client连接Server及请求服务代码:
class RXXXServSession : public RSessionBase
{
public:
RXXXServSession ();
TInt Connect();
TVersion Version() const;
void Close();
TBool Stop();
};
TInt RXXXServSession ::Connect()
{
TInt ret = CreateSession(KXXXServerName,Version(),kDefaultMessageSlots);
if(ret == KErrNotFound)
{
RProcess process;
ret = process.Create(KXXXServerPath, KNullDesC);
if(ret != KErrNone)
{
return ret;
}
CleanupClosePushL(process);
process.Resume();
TInt retry = 10;
do
{
User::After(1000);
ret = CreateSession(KXXXServerName,Version(),kDefaultMessageSlots);
}while((ret == KErrNotFound) && (retry--));
CleanupStack::PopAndDestroy(1);
return ret;
}
else
{
return ret;
}
}
TBool RXXXServSession::Stop()
{
return (SendReceive(EXXX_SERVICE_STOP) == KErrNone);
}
UI程序:UI程序一般会在应用程序列表中显示启动图标,用户可选择启动图标启动程序。Symbian UI程序的详细结构,请参考 跟着Code走,详解Symbian UI程序框架(1)——UI程序结构 ,此处只是简述之。
Symbian UI程序的结构大体来看也是符合MVC结构的,但是我觉得它并不是严格的符合MVC结构(当然这也取决于对于MVC结构的理解,也许我对MVC结构的理解也不是那么对)。其默认的程序框架,只有Controller/View,并没有Modal部分。如果写程序的人,自己把Modal部分的代码独立出来,那么可能就比较符合MVC结构一些。
UI程序典型的框架必须代码:
LOCAL_C CApaApplication* NewApplication()
{
return new CXXXApplication;
}
GLDEF_C TInt E32Main()
{
return EikStart::RunApplication( NewApplication );
}
以上代码中,EikStart::RunApplication是库中的代码,该函数会完成UI程序框架的初始化(初始化工作包括创建CEikonEnv实例,CEikonEnv实例整个程序唯一,使用了单例模式,可以通过CEikonEnv::Static()访问该对象。CEikonEnv对象可以看做是整个UI程序的Engine,它派生自CActive,是一个活动对象。CEikonEnv会建立与系统各个Server的连接(包括窗口Server等),从这些系统Server获得事件,并通过AppUi类分发到对应的UI控件),并调用NewApplication创建应用程序实例类,之后会再依次调用以下两个函数创建Document类及AppUi类。普通程序并不会在Application类及Document类中写太多代码,除非程序是文档处理程序。手机程序极少会是文档处理程序,所以MVC中的Controller一般情况下,只有AppUi类的代码会被用到。(从这个角度来说,Symbian UI程序的MVC结构设计,并不是太符合手机程序开发的需要)
CApaDocument* CXXXApplication::CreateDocumentL()
{
return CXXXDocument::NewL(*this);
}
CEikAppUi* CXXXDocument::CreateAppUiL()
{
return new (ELeave) CXXXAppUi;
}
AppUi类是整个UI程序的核心,CEikonEnv对象调用它完成UI页面的切换,UI操作事件/重绘事件的分发。AppUi类派生自CAknViewAppUi,基类中完成了页面切换,事件分发的基本代码。一般来说,AppUi类只需要UI页面初始化的代码。
AppUi类的典型代码:
class CXXXAppUi : public CAknViewAppUi
{
public:
// constructor and destructor
CXXXAppUi ();
virtual ~CXXXAppUi ();
void ConstructL();
public:
// from CCoeAppUi
virtual TKeyResponse HandleKeyEventL(
const TKeyEvent& aKeyEvent,
TEventCode aType );
// from CEikAppUi
void HandleCommandL( TInt aCommand );
void HandleResourceChangeL( TInt aType );
// from CAknAppUi
void HandleViewDeactivation( const TVwsViewId& aViewIdToBeDeactivated, const TVwsViewId &aNewlyActivatedViewId );
private:
CXXXView* iXXXView;
CYYYView* iYYYView;
};
void CXXXAppUi::ConstructL()
{
BaseConstructL( EAknEnableSkin );
InitializeContainersL();
}
void CXXXAppUi::InitializeContainersL()
{
iXXXView = CXXXView::NewL();
AddViewL( iXXXView );
SetDefaultViewL( *iXXXView );
iYYYView = CYYYView::NewL();
AddViewL( iYYYView );
……
}
View类实例是程序可显示的页面,但是View类本身并不是可显示的控件或控件派生类,它只是符合系统View管理接口的实体而已。View类必须包括一个可现实的控件实例,以及其他如菜单实例。
View类典型代码:
class CXXXView : public CAknView
{
public:
// constructors and destructor
CXXXView ();
static CXXXView * NewL();
static CXXXView * NewLC();
void ConstructL();
virtual ~CXXXView ();
// from base class CAknView
TUid Id() const;
void HandleCommandL( TInt aCommand );
protected:
// from base class CAknView
void DoActivateL(
const TVwsViewId& aPrevViewId,
TUid aCustomMessageId,
const TDesC8& aCustomMessage );
void DoDeactivate();
void HandleStatusPaneSizeChange();
private:
CXXXContainer* iXXXContainer;
};
void CXXXView::DoActivateL(
const TVwsViewId& /*aPrevViewId*/,
TUid /*aCustomMessageId*/,
const TDesC8& /*aCustomMessage*/ )
{
if ( iXXXContainer == NULL )
{
iXXXContainer = CreateContainerL();
iXXXContainer ->SetMopParent( this );
AppUi()->AddToStackL( *this, iXXXContainer );
}
}
void CXXXView::DoDeactivate()
{
if ( iXXXContainer != NULL )
{
AppUi()->RemoveFromViewStack( *this, iXXXContainer );
delete iXXXContainer ;
iXXXContainer = NULL;
}
}
以上View类代码中iXXXContainer成员是真正可显示的控件(一般来说,它本身是控件,占据整个程序窗口区域。它同时是个控件容器,真正的有效UI控件会以它为父窗口,占据程序窗口的部分区域)。
控件Container典型代码:
class CXXXContainer : public CCoeControl
{
……
public:
// from base class CCoeControl
TInt CountComponentControls() const;
CCoeControl* ComponentControl( TInt aIndex ) const;
TKeyResponse OfferKeyEventL(
const TKeyEvent& aKeyEvent,
TEventCode aType );
void HandleResourceChange( TInt aType );
protected:
// from base class CCoeControl
void SizeChanged();
private:
// from base class CCoeControl
void Draw( const TRect& aRect ) const;
private:
void InitializeControlsL();
void LayoutControls();
CCoeControl* iFocusControl;
MEikCommandObserver* iCommandObserver;
private:
CEikImage* iImage1;
CEikLabel* iLabel1;
};
void CXXXContainer ::LayoutControls()
{
iImage1->SetExtent( TPoint( 87, 89 ), TSize( 117, 117 ) );
iLabel1->SetExtent( TPoint( 186, 265 ), TSize( 85, 34 ) );
}
TKeyResponse CXXXContainer ::OfferKeyEventL(
const TKeyEvent& aKeyEvent,
TEventCode aType )
{
if ( iFocusControl != NULL
&& iFocusControl->OfferKeyEventL( aKeyEvent, aType ) == EKeyWasConsumed )
{
return EKeyWasConsumed;
}
return CCoeControl::OfferKeyEventL( aKeyEvent, aType );
}
void CTestContainer::Draw( const TRect& aRect ) const
{
CWindowGc& gc = SystemGc();
gc.Clear( aRect );
}
从以上代码可以看出,控件容器类是派生自CCoeControl的,本身是控件。另外,还需要注意的是,UI事件(例如按键事件和绘制事件)是直接由控件处理的。AppUi类在分发事件时,不会经过View,而是直接找到对应的控件。AppUi类分发事件的详细过程,请参考 跟着Code走,详解Symbian UI程序框架(2)——程序结构进阶,窗口管理及事件分发
===================================================================
Android:
对于Android下的应用,首先需要明确的是,每个Android应用其实都不能被称作真正意义上的程序,它们只是受Android系统管理的一些Component而已。系统会在与用户的交互过程中,调用并执行对应组件的代码。Android应用在运行时的状态,取决于你对各个Component的属性配置及系统运行时的状态。
Android下的Component分为Activity、Service、BroadcastReceiver及ContentProvider。Activity显示一个UI页面,Service定义一个后台运行的服务,BroadcastReceiver用以监听系统中的通知,ContentProvider用以向其他组件提供数据。你可以在工程配置文件AndroidManifest.xml中配置Component的属性,当然你也可以通过运行时的代码来改变属性(但是有些属性运行时改变,未必会生效)。例如,你可以指定Component运行时的进程名,可以指定多个Component是在同一个进程中执行,还是在各自不同的进程中执行……Android开发者网站中对各种Component的说明已经非常详细了,因为这里这简要列出几个要点。
Activity:从Activity类派生,一般在资源文件中定义包含UI元素的layout,在onCreate中调用setContentView指定layout,UI就显示出来了。如果UI中有button等可能与用户交互的UI元素,可通过findViewById获得元素,并调用setOnClickListener之类的函数设置点击处理函数。如果需要显示菜单,可以在onCreateContextMenu中创建menu,并在onContextItemSelected中根据menu ID进行处理。如果需要弹出Dialog,则首先定义一些Dialog ID,并在需要谈Dialog的地方通过调用showDialog(ID)弹出,然后在onCreateDialog(id)中创建Dialog(你可以在这里关联Dialog中UI元素对应的响应代码)。
Service:从Service类派生,一般在onCreate及onStart中完成初始化。初始化工作包括准备好与外部通信及Service本身相关功能(例如,如果你有长线任务需要在单独线程中处理,可能需要创建线程)。为处理外部请求,可响应onBind,onBind会返回一个符合IBinder接口的对象,Client就可以通过这个对象发送Message给Service了。Service需要实现MessageHandler来处理Client的请求。
Service典型代码:
public class XXXService extends Service {
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_WRS_CHANGE:
……
break;
……
default:
super.handleMessage(msg);
}
}
}
final Messenger mMessenger = new Messenger(new IncomingHandler());
public void onCreate() {
super.onCreate();
……
}
public void onStart() {
……
}
public void onDestroy() {
super.Destroy();
……
}
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
Client连接Service的典型代码:
public class MsgToService {
private void send(Message msg) {
try {
int i = 3;
while( mService == null){
i--;
Thread.sleep(300);
if(i==1)
break;
}
if(mService != null) {
mService.send(msg);
}
else {
}
} catch (RemoteException e) {
} catch (InterruptedException e) {
}
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
try {
} catch (Exception e) {
}
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
public void doBindService() {
boolean ret = mContext.bindService(new Intent(mContext, XXXService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = ret;
}
public void doUnbindService() {
if (mIsBound) {
if (mService != null) {
try {
mContext.unbindService(mConnection);
mIsBound = false;
} catch (Exception e) {
}
}
}
}
}
BroadcaseReceiver:派生自BroadcastReceiver,需要在AndroidManifest.xml中指定该Component对应的Intent-filter,即它监听系统的哪些通知,然后实现onReceive函数进行响应即可。
ContentProvider:派生自ContentProvider,实现insert/delete/query/update等函数处理对应的请求。ContentProvider内部可以采用文件或者数据库保存数据,insert/delete/query/update的参数包含每次请求对应的数据。
Client调用ContentProvider的典型代码:
public final static String Log_TYPE = "Type";
public static final Uri CONTENT_URI = Uri.parse("content://com.xxx.db/log");
ContentResolver cr = c.getContentResolver();
ContentValues values = new ContentValues();
values.put(Log_TYPE, type);
cr.insert(CONTENT_URI, values);
============================================================================
iOS:
Delegate模式在iOS中得到了疯狂的使用,对于程序开发人员来说,编写iOS程序就是使用XCode生成程序基本框架后,根据需要填满Delegate留下的空即可。
main函数典型代码:
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
iOS下没有Garbage Collection机制,取而代之的是AutoreleasePool。当调用对象的autorelease函数后,该对象会被记录到最邻近的AutoreleasePool,AutoreleasePool的drain被调用或者析构时,所有记录下来的对象会被真正释放。AutoreleasePool降低了内存管理的复杂度,可以做到谁分配谁释放(函数可返回在函数内部分配的对象,返回之前调用autorelease,调用者不负责内存释放)。
UIApplicationMain是iOS程序框架的启动函数。第三个参数传递派生如UIApplication的类名,一般很少有程序会派生UIApplication类,所以多数情况下直接传入nil,系统将使用默认的UIApplication类。第四个参数传递Application Delegate的类名,Application Delegate类实现了UIApplicationDelegate协议,程序框架在合适的时候(例如,程序启动、程序切换到后台,程序切换到前台,程序终止等)调用Application Delegate的函数。如果你在程序的主xib资源文件中设置了Application Delegate的连接,UIApplicationMain的第四个参数也可以传nil。
iOS程序的主要代码入口都在Application Delegate类中,并且大多数情况下只会实现程序启动和程序终止两个接口。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
……
}
- (void)applicationWillTerminate:(UIApplication *)application {
/*
Called when the application is about to terminate.
See also applicationDidEnterBackground:.
*/
……
}
程序基本框架有了之后,接下来就是UI显示及用户交互了。工程的主xib文件中有一个window对象,代表应用程序窗口,向其中加入其它UI元素,即完成UI元素的显示。UI元素由一系列派生于UIView的元对象成,UI元素创建时会指定其在屏幕上占据的区域,UIView中可通过调用addSubView加入子View,子元素也可以通过调用removeFromSuperView从父View中删除。
UIView的显示,Apple也是采用MVC结构,每个View一般对应一个Controller。但是框架本身并未体现出View的Modal,编写代码时,需要你自己把数据管理部分的代码分割开来,体现是MVC中的Modal。
典型的View显示代码:
@interface XXXView : UIViewController {
}
@implementation XXXView
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
@end
用户事件的响应:你可以在UI资源文件xib文件中,连接UI元素(例如UIButton)的事件响应函数,也可以在代码中创建UI元素时,或者之后调用setTarget及setAction设置响应函数。
============================================================================
Windows Mobile:
Windows Mobile的程序结构跟Windows的一致,你可以采用MFC或者Win32编程。如果采用MFC编程,在对应的事件处理接口函数中填入代码即可。如果采用Win32编程,则需要自己建立消息循环的基础代码,并分别完成消息响应函数。相关的文章非常多,直接Google或者百度之。
============================================================================
Android/iOS/WM MFC编程是最方便的,基本上程序框架都比较完备,你只需要填空即可。Symbian/WM Win32编程麻烦一点,需要你完成的代码多一点。