Mobil alkalmazások fejlesztése ELTE PSZT 2010. március
Előkészületek 1. Perl 5.6.x (Set the pathvariable!) 5.8 /5.10 nem jó! http://downloads.activestate.com/activeperl/windows/5.6/activeperl- 5.6.1.638-MSWin32-x86.msi 2. SDK(s)(S60 3rd Ed. MR + újabb) http://www.forum.nokia.com/resources_and_information/tools/platforms/s 60_Platform_SDKs/ 3. Carbide.c++ 2.0 Developer Edition (vagy újabb) http://www.forum.nokia.com/main/resources/tools_and_sdks/carbide/index. html Eszközök telepítése a felsorolás sorrendjében történjen Az eszközöket ugyanarra a meghajtóra kell telepíteni, ahol az SDK van. (javasolt: C:\. Ne használjon hálózati meghajtót! Fájl nevekben ne használjon helyközt.)
Segédanyagok www.symbian.com/books www.symbian.com/developer www.symbian.developer/public/index.html http://forum.nokia.com/main.html SDK online dokumentáció: C:\S60\devices\S60_3rd_FP2_SDK_v1.1\docs\eclipse.exe
Online dokumentáció c:\s60\devices\s60_3rd_fp2_sdk_v1.1\docs\eclipse.exe
Symbian operációs rendszer SIBO Psion Computers (1980) EPOC - irodai kisszámítógép (1980-1998) Symbian OS (alapítva 1998 : Nokia, Motorola, Psion, Ericsson, 2002: Sony-Ericssson, Siemens) Mérföldkő: 2000-ben megjelent 6.0-ás verzió Symbian Symbian OS S60 C++
Symbian OS felépítése Felhasználói interfész szinkronizáció UI alkalmazás keretrendszer UI eszközkészlet MIDP CLDC Alkalmazás szintű szolgáltatások PIM Böngészés Üzenetkezelés Adatszinkron JVM Java Operációs rendszer szolgáltatásai Általános szolgáltatások Kommunikációs szolgáltatások Grafikai szolgáltatások PC-s kapcsolat szolgáltatásai Alapszolgáltatások Alacsonyszintű szolgáltatások Fájlszerver Kernel és hardware integráció Kernel szolgáltatások Eszközmeghajtók
Kliens-szerver keretrendszer fájlszerver ablakkezelő szerver kommunikácó kezelés adatbázis-kezelés határidőnapló A CServer osztályból származnak. 2009.09.19. Mobil alkalmazások fejlesztése, Symbian OS/C++ 7
Fájl kezelés Hívó kliens File szerver kliens oldali része mytest.exe efsrsv.dll szolgáltatás kérés (API hívások) pl.: RFs::Connect() Process boundary File server efile.exe Kliens-szerver kommunikáció (Kernel által felügyelt) //File I/O in Symbian OS RFs fsession; User::LeaveIfError(fSession.Connect()); RFile file; ret = file.open(fsession, _L( file.txt ), EFileWrite); if (ret) ret = file.create(fsession, _L( file.txt ), EFileWrite); if (!ret) file.write(_l8( Write some data )); file.close(); fsession.close();
Futtaható program modularizálása Forráskód.exe Forráskód.exe Futtató környezet.exe Kép Kép Erőforrás szöveg Erőforrás szöveg Kód Fájl szerver Ablak szerver... indexek Kód Fájl szerver Ablak szerver...
A fordítás és szerkesztés folyamata.bmp <.h>.h.rsg.hrh.rh.rls Bmp konverter.cpp.rss.bmp.mbm.rss C++ fordító Erőforrás fordító Bmp konverter Aif fordító.obj.lib.rsc.pkg.mbm.aif linker.exe;.dll Sis készítő.sis
Biztonságos alkalmazás fejlesztésének támogatása Saját elnevezési konvenciók: osztályok (T,C,M,R), függvények (L,LC) Kódolási minták a memóriaszivárgás kiszűrésére: kétfázisú konstrukció, CleanUpStack Saját kivételkezelés: TRAP és User::Leave String helyett deszkriptorok Többszálúság kezelése: ActiveObject...
Osztálytípusok T: egyszerű típus; nincs destruktora; csak T-ből, vagy M-ből öröklődhet; értékként átadható; stack-en tárolható C: csak a heapen foglalható; van destruktora; pontosan egy C és tetszőleges M osztályból származhat; nem adható át értékként M: interfész; csak virtuális metódusok; nincsenek tagváltozók; nem példányosítható R: erőforrás, melyet meg kell nyitni és be kell zárni Az osztályelnevezéseknek fontos üzenete van, ezért mindig jól át kell gondolni, hol és hogyan használjuk az osztály példányait, és ennek megfelelően kel elnevezni őket. A névkonvenció segíti a fejlesztőt abban, hogyan használhatja majd ezeket az osztályokat anélkül, hogy mélyebben beleásná magát az adott osztály implementációjába. C osztályként definiálhatunk pl. olyan osztályt is, amely túl nagy, és nem ajánlatos stack-ben tárolni.
T osztályok - példa TInt n; enum TColors KRed, KBlue ; class TMyNumber public: void SetNumber (TInt anumber); TInt Number() const; private: TInt inumber; ;
R osztályok - példa TInt doexamplel() RFs fsession; User::LeaveIfError(fSession.Connect()); RFile file; TInt ret = file.open(fsession,_l("c:\\resource\\apps\\second.txt"),efilewrite); if(ret) ret = file.create(fsession,_l("c:\\resource\\apps\\second.txt"),efilewrite); if(!ret) file.write(_l8("write some new data")); file.close(); fsession.close(); return ret;
Kivételkezelés try utasítások; // A kivétel figyelése alatt álló "veszélyes" utasítások catch (kivétel deklaráció) //A kivétel kezelője utasítások; ; int foo1(); //Minden kivétel továbbítódik int foo2() throw (char*) //csak a char* típusú kivételek továbbítódnak int foo3() throw() // egyetlen kivétel sem továbbítódik C++-os kivételkezelés Symbianos kivételkezelés void CMyClass::Foo() TRAPD (error, HandleKeyL())) if(error!= KErrNone) //Ha a HandleKeyL() leavel, akkor ideadódik a vezérlés //és kezelhetjük a hibát
Kivételkezelés - leavelés A Symbianban a C++-os kivételkezelés helyett (mellett) a kivételes szituációk kezelésére a leavelést használják. Leavelésnél a kivételes szituáció helyéről átkerül a vezérlés oda, ahol a kivételes helyzetet kezelni tudjuk. (TRAP) Mikor leave-elhet egy függvény? Ha közvetlenül meghívja a User::Leave() függvényt Heapen foglal helyet new (ELeave) metódussal Más leave-elő függvényt hív Majd látni fogjuk, hogy azokban a kódrészekben, ahol leavelő függvényhívások (is) vannak fokozottabb gondossággal kell eljárni, ezért a programozónak feltétlenül tudnia kell egy függvényről, hogy esetleg leavelhet. Symbianban azt, hogy egy függvény leavelhet a függvény nevében, a neve végén megadott L betűvel jelezzük.
Kivételkezelés C++-os kivételkezelés A kivétel keletkezésekor egy objektumot dobunk Egy try blokkban vagy az abból hívott függvényekben keletkező kivételt a catch blokkban elkaphatjuk és lekezelhetjük. A kivétel keletkezésekor a vezérlés visszatér a hívó blokkba, majd az azt hívóba fel és fel, amíg kivételt le nem kezeljük. Közben a verem memória (stack) visszafejtésre kerül, az abból kikerülő objektumok destruktorai meghívódnak. Symbianos kivételkezelés A kivétel keletkezésekor egy egész szám (hiba kód) generálódik. Egy TRAP vagy TRAPD makróval meghívott függvényben vagy abból hívott függvényekben keletkező kivételt a makróban lekezelhetjük. A kivétel keletkezésekor a vezérlés visszatér a hívó blokkba, majd az azt hívóba fel és fel, amíg kivételt le nem kezeljük. Közben a verem memória kiürül, de az abból kikerülő objektumok destruktorai nem hívódnak meg. 2009.09.19. Mobil alkalmazások fejlesztése, Symbian OS/C++ 18
Memóriakezelés void CMyClass::Foo() TInt myinteger; TBufC<16> buffer;... //függvény törzs Helyfoglalás a stack-en //A lefoglalt hely kilépéskor törlődik void CBasicAppUi::Foo() CMyClass* myptr = new CMyClass; if(myptr) myptr->foo(); //Adatok és függvények biztonságos elérése delete myptr; Helyfoglalás és biztonságos elérés a heapen A kódolást nagyon nehézkessé tenné, ha minden helyfoglalást becsomagolnánk egy ilyen ellenőrzésbe.
Objektum létrehozása a heapen Symbianban LOCAL_C void MainL() CMyClass* myptr = new CMyClass; if(myptr) myptr->foo(); //Can safely access data & functions delete myptr; LOCAL_C void MainL() CMyClass* myptr = new (ELeave) CMyClass; myptr->foo(); Ha nincs hely, akkor leavel.... delete myptr; Erre a pontra csak akkor kerül a vezérlés, ha biztosan megtörtént a helyfoglalás.
CleanupStack LOCAL_C void MainL() CMyClass* myptr = new (ELeave) CMyClass; myptr->foo();... delete myptr; Nem leavelő függvényt hívunk. case ECleanupCmdUse3: CX* x=new(eleave) CX; x->usel(); delete x; Leavelő függvényt hívunk. Az x automatikus változó a heap-re mutat. Ha UseL() leave-el, akkor a delete nem hajtódik végre, a CX által lefoglalt terület árván marad. Megoldás: CleanupStack case ECleanupCmdUse3: CX* x=new(eleave) CX; CleanupStack::PushL(x); x->usel(); CleanupStack::PopAndDestroy(x); A CX helyfoglalása után a rá mutató pointert elhelyezzük a Cleannup Stack-en. Ha UseL() nem leave-el, akkor MI szedjük le a címet a stackről. Ha UseL() leave-el, akkor a Laeve kezelő eljárás.
CleanupStack alkalmazása case ECleanupCmdUse3: CX* x1=new(eleave) CX; CleanupStack::PushL(x1); CX* x2=new(eleave) CX; CleanupStack::PushL(x2); x1-<usel(); CleanupStack::PopAndDestroy(2); Egyszerre több elemet is leemelhetünk a stackről. Tilos!! CleanupStack::PushL(iMember); Pointert tartalmazó adattagot ne tegyünk a CleanupStack-re. Az általa mutatott terület felszabadítása az adattagot tartalmazó osztály feladata, nem a Leave-elő mechanizmusé. A PushL() nem leave-el (előrefoglalás miatt)
Leavelés és CleanupStack case ECleanupCmdUse3: CX* x=new(eleave) CX; TRAPD (error, x->usel(); if(error) delete x; User::Leave(error); delete x; Leave kezelése TRAP segítségével. Több függvényhívás esetén mindegyiket bele kellene tenni egy TRAP konstrukcióba. case ECleanupCmdUse3: CX* x=new(eleave) CX; CleanupStack::PushL(x); x->usel(); x->usel(); x->usel(); CleanupStack::PopAndDestroy(x); A CleanupStack használatával egyserűbben kódolhatjuk, ha egy eljárson belül több leavelő függvényt is meg kell hívnunk.
Mostmár minden rendben van? class CY : public CBase public: CY(); ~CY(); public: CX* ix; ; CY::CY() // bad bad ix=new(eleave) CX; class CX : public CBase public: void UseL(); public: TInt iint; ; void CX::UseL() TInt* pi=new(eleave) TInt; delete pi; CY::~CY() delete ix; Probléma: Az y példányosításakor az Y konstruktora leaveelhet és a félkész y által lefoglalt tárhely árván marad. CY* y=new(eleave) CY; CleanupStack::PushL(y); y->ix->usel(); CleanupStack::PopAndDestroy(); // y
Dinamikus osztály példányosítása - probléma class CY : public CBase public: CY(); ~CY(); public: CX* ix; ; CY::CY() // bad bad ix=new(eleave) CX; CY::~CY() delete ix; class CX : public CBase public: void UseL(); public: TInt iint; ; void CX::UseL() TInt* pi=new(eleave) TInt; delete pi; CY* y=new(eleave) CY; CleanupStack::PushL(y); y->ix->usel(); CleanupStack::PopAndDestroy(); // y CY* y; CY* temp = User::AllocL(sizeof(CY)); //Allocate memory temp->cy::cy(); //C++ constructor y=temp;
Megoldás: Kétfázisú konstrukció A konstruktort két részre bontjuk: 1. rész: Biztonságos, nem leave-elő a példányra mutató pointer biztosan felkerül a CleanupStack-re 2. rész: A veszélyesebb leave-elő rész. De ekkor már jó helyen van a pointer.
Kétfázisú konstrukció class CZ : public CBase public: static CZ* NewL(); static CZ* NewLC(); void ConstructL(); ~CZ(); public: CX* ix; ; A két fázist a NewL, NewLC függvényekbe becsomagolják. void CZ::ConstructL() ix=new(eleave) CX; CZ::~CZ() delete ix; CZ* CZ::NewL() CZ* self=new(eleave) CZ; CleanupStack::PushL(self); self->constructl(); CleanupStack::Pop(); return self; CZ* CZ::NewLC() CZ* self=new(eleave) CZ; CleanupStack::PushL(self); self->constructl(); return self;
Kétfázisú konstrukció - összefoglalás Az osztály standard konstruktorában nem hívunk leave-elő kódot A leave-elő hívásokat egy külön második fázisú konstruktorba tesszük. (ConstructL) Az osztály példányosításakor: Meghívjuk a standard konstruktort (new) A félig létrejött objektumot feltesszük a CleanupStack-re Meghívjuk a második fázisú konstruktort (ConstructL) (Levesszük a CleanupStack-ről) A két fázist a NewL, NewLC függvényekbe becsomagolják.
Cleanup-safe HelloWorld int HelloWorldL() CConsoleBase* console = Console::NewL(_L("Hello World"),TSize(KConsFullScreen,KConsFullScreen)); console->printf(ktxtmessage); console->printf(ktxtpressanykey); console->getch(); // get and ignore character delete console; return 0; Konzolos alkalmazásnál a CleanupStack-et nekünk kell létrehozni. TInt E32Main() UHEAP_MARK; CTrapCleanup* cleanup = CTrapCleanup::New(); TInt retval = KErrNone; if(cleanup) TRAP(retVal,HelloWorldL()); ASSERT_ALWAYS(!retVal, User::Panic(_L("Hello World Panic"),retVal)); delete cleanup; UHEAP_MARKEND; return retval; int main() printf("hello world!\n"; return 0;
Aszinkron események kezelése - ActiveObject CDeleyHello.cpp CDelayedHello* CDelayedHello::NewL() CDelayedHello* self = new (ELeave) CDelayedHello(); CleanupStack::PushL(self); self->constructl(); CleanupStack::Pop(self); return self; CDelayedHello::CDelayedHello() : CActive(0) CActiveScheduler::Add(this); void CDelayedHello::ConstructL() ienv = CEikonEnv::Static(); User::LeaveIfError(iTimer.CreateLocal()); CDelayedHello::~CDelayedHello() Cancel(); itimer.close();... ActiveHello projekt
Aszinkron események kezelése - ActiveObject CDeleyHello.cpp... // from CActive void CDelayedHello::RunL() ienv->infomsg(r_activehello_text_hello); void CDelayedHello::DoCancel() itimer.cancel(); TInt CDelayedHello::RunError(TInt /*aerror*/) return KErrNone; //Must be given if RunL() calls a leaving function... ActiveHello projekt
Deszkriptorok
Symbian OS C++ 3rd Edition
GUI alkalmazások felépítése Application creates Document creates, manages creates manages AppView (V) renders creates, manages AppUi (C) manages Model (M)
bld.inf PRJ_PLATFORMS DEFAULT bld.inf PRJ_MMPFILES Legkisebb.mmp
Legkisebb.mmp TARGET Legkisebb_0x0CBFF1BD.exe TARGETTYPE exe Legkisebb.mmp UID 0x100039CE 0x0CBFF1BD SOURCEPATH..\src SOURCE Legkisebb.cpp LegkisebbApplication.cpp LegkisebbAppView.cpp LegkisebbDocument.cpp LegkisebbAppUi.cpp USERINCLUDE..\inc CAPABILITY ReadUserData SOURCEPATH..\data START RESOURCE Legkisebb.rss TARGET Legkisebb_0x0CBFF1BD TARGETPATH resource\apps HEADER END START RESOURCE Legkisebb_reg.rss TARGET Legkisebb_0x0CBFF1BD_reg TARGETPATH \private\10003a3f\apps END LANG SC SECUREID 0x0CBFF1BD VENDORID 0 SYSTEMINCLUDE \epoc32\include LIBRARY euser.lib apparc.lib cone.lib eikcore.lib avkon.lib commonengine.lib efsrv.lib estor.lib aknnotify.lib gdi.lib
Legkisebb.mmp Legkisebb.mmp A.mmp fájl tartalma garfikus felületen is beállítható.
Legkisebb.rss
Legkisebb.pan,.rls,.hrh Legkisebb.pan Legkisebb.rls #ifndef LEGKISEBB_PAN_ #define LEGKISEBB_PAN_ // Caption string for app. #define qtn_caption_string "Legkisebb 0x0CBFF1BD" enum TLegkisebbPanics ELegkisebbUi = 1 ; inline void Panic(TLegkisebbPanics areason) _LIT(applicationName, "Legkisebb-0x0CBFF1BD"); User::Panic(applicationName, areason); #endif /*LEGKISEBB_PAN_*/ #define qtn_caption_string "Legkisebb 0x0CBFF1BD" #ifndef LEGKISEBB_HRH #define LEGKISEBB_HRH #define _UID3 0x0CBFF1BD #endif // LEGKISEBB_HRH Legkisebb.hrh
Legkisebb.cpp Legkisebb.cpp #include <eikstart.h> #include "LegkisebbApplication.h" LOCAL_C CApaApplication* NewApplication() return new CLegkisebbApplication; GLDEF_C TInt E32Main() return EikStart::RunApplication( NewApplication );
LegkisebbApplication osztály #include <aknapp.h> #include "Legkisebb.hrh" const TUid KUidLegkisebbApp = _UID3 ; class CLegkisebbApplication : public CAknApplication public: TUid AppDllUid() const; protected: CApaDocument* CreateDocumentL(); ; LegkisebbApplication.h #include "Legkisebb.hrh" #include "LegkisebbDocument.h" #include "LegkisebbApplication.h" LegkisebbApplication.cpp CApaDocument* CLegkisebbApplication::CreateDocumentL() return CLegkisebbDocument::NewL(*this); TUid CLegkisebbApplication::AppDllUid() const return KUidLegkisebbApp;
LegkisebbAppDocument osztály - definíció #include <akndoc.h> class CLegkisebbAppUi; class CEikApplication; class CLegkisebbDocument : public CAknDocument public: static CLegkisebbDocument* NewL(CEikApplication& aapp); static CLegkisebbDocument* NewLC(CEikApplication& aapp); virtual ~CLegkisebbDocument(); public: CEikAppUi* CreateAppUiL(); private: void ConstructL(); CLegkisebbDocument(CEikApplication& aapp); ; LegkisebbAppDocument.h
LegkisebbAppDocument osztály - implementáció #include "LegkisebbAppUi.h" #include "LegkisebbDocument.h" LegkisebbAppDocument.cpp CLegkisebbDocument* CLegkisebbDocument::NewL(CEikApplication& aapp) CLegkisebbDocument* self = NewLC(aApp); CleanupStack::Pop(self); return self; CLegkisebbDocument* CLegkisebbDocument::NewLC(CEikApplication& aapp) CLegkisebbDocument* self = new ( ELeave ) CLegkisebbDocument( aapp ); CleanupStack::PushL(self); self->constructl(); return self; void CLegkisebbDocument::ConstructL() CLegkisebbDocument::CLegkisebbDocument(CEikApplication& aapp) : CAknDocument(aApp) CLegkisebbDocument::~CLegkisebbDocument() CEikAppUi* CLegkisebbDocument::CreateAppUiL() return new ( ELeave )CLegkisebbAppUi;
LegkisebbAppUi osztály - definíció LegkisebbAppUi.h #include <aknappui.h> class CLegkisebbAppView; class CLegkisebbAppUi : public CAknAppUi public: void ConstructL(); CLegkisebbAppUi(); virtual ~CLegkisebbAppUi(); private: void HandleCommandL(TInt acommand); void HandleStatusPaneSizeChange(); private: CLegkisebbAppView* iappview; ;
LegkisebbAppUi osztály - implementáció void CLegkisebbAppUi::ConstructL() BaseConstructL(CAknAppUi::EAknEnableSkin); iappview = CLegkisebbAppView::NewL(ClientRect() ); CLegkisebbAppUi::CLegkisebbAppUi() CLegkisebbAppUi::~CLegkisebbAppUi() if (iappview) delete iappview; iappview = NULL; #include <aknappui.h> #include <avkon.hrh> #include <aknmessagequerydialog.h> #include <aknnotewrappers.h> #include <Legkisebb_0x0CBFF1BD.rsg> #include "Legkisebb.hrh" #include "Legkisebb.pan" #include "LegkisebbApplication.h" #include "LegkisebbAppUi.h" #include "LegkisebbAppView.h" void CLegkisebbAppUi::HandleCommandL(TInt acommand) switch (acommand) case EEikCmdExit: case EAknSoftkeyExit: Exit(); break; default: Panic(ELegkisebbUi); break; void CLegkisebbAppUi::HandleStatusPaneSizeChange() iappview->setrect(clientrect()); LegkisebbAppUi.cpp
LegkisebbAppView osztály - definíció #include <coecntrl.h> class CLegkisebbAppView : public CCoeControl public: static CLegkisebbAppView* NewL(const TRect& arect); static CLegkisebbAppView* NewLC(const TRect& arect); virtual ~CLegkisebbAppView(); public: void Draw(const TRect& arect) const; virtual void SizeChanged(); private: void ConstructL(const TRect& arect); CLegkisebbAppView(); private: HBufC *ihellomessage; LegkisebbAppView.h ;
LegkisebbAppView osztály implementáció 1. CLegkisebbAppView* CLegkisebbAppView::NewL(const TRect& arect) CLegkisebbAppView* self = CLegkisebbAppView::NewLC(aRect); CleanupStack::Pop(self); return self; #include <coemain.h> #include <gdi.h> #include <EIKENV.H> #include "LegkisebbAppView.h" CLegkisebbAppView* CLegkisebbAppView::NewLC(const TRect& arect) CLegkisebbAppView* self = new ( ELeave ) CLegkisebbAppView; CleanupStack::PushL(self); self->constructl(arect); return self; _LIT(KHelloMessage,"Hello Legkisebb!"); void CLegkisebbAppView::ConstructL(const TRect& arect) CreateWindowL(); SetRect(aRect); ActivateL(); ihellomessage=hbufc::newl(30); *ihellomessage=khellomessage; CLegkisebbAppView::CLegkisebbAppView() CLegkisebbAppView::~CLegkisebbAppView() delete ihellomessage; LegkisebbAppView.cpp
LegkisebbAppView osztály implementáció 2. void CLegkisebbAppView::Draw(const TRect& /*arect*/) const CWindowGc& gc = SystemGc(); TRect drawrect(rect()); gc.clear(drawrect); drawrect.shrink(10,10); gc.drawrect(drawrect); const CFont* fontused = CEikonEnv::Static()->TitleFont(); gc.usefont(fontused); TInt baselineoffset = (drawrect.height() + fontused- >HeightInPixels())/2; gc.drawtext(*ihellomessage,drawrect,baselineoffset,cgraphicscontext:: ECenter,0); gc.discardfont(); void CLegkisebbAppView::SizeChanged() DrawNow(); LegkisebbAppView.cpp
Carbide++
Futtatás Futtatáshoz válasszuk az emulátort, mert így megkapjuk a hibaüzenetet.
Hibaüzenetek értelmezése
Projektek First Legkisebb ActiveHello ConsLauncher OandX Peekaboo Tutorial: http://developer.symbian.org/wiki/index.php/going_beyond_hello:_a _Tutorial_for_Symbian_C%2B%2B_Applications
AandX
Kész alkalmazások importálása Carbide.C++ alá Az importálni kívánt projek bld.inf fájlja