iPad Mini kehittäjän näkökulmasta

Apple julkaisi eilen tukun uusia tuotteita, joiden mukana oli myös kokonaan uusi iOS-laite, iPad Mini. Tässä hiukan mietteitä kehittäjän näkulmasta.

iPad Mini on raudaltaan ja pikseleiltään sama kuin iPad2, eli sovellukset jotka toimivat iPad2:ssa toimivat myös iPad Minissä. Ainoat isommat erot ovat iPad Minin LTE-tuki, Siri ja luonnollisesti koko.

Internet-huhut olivat yhdessä vaiheessa sitä mieltä, että iPad Mini olisi kuvasuhteeltaan 16:9, joka olisi ollut sovelluksille huomattavasti isompi remontti, kuin iPhone 5 yhteensopivuuden tekeminen iPhone-sovelluksissa. Näin ollen kehittäjät voivat huokaista helpotuksesta, kun nykyiset sovellukset rullaavat enemmän tai vähemmän samalla tavalla kuin iPad 2:ssa.

Tervetuloa iPad Mini.

 

OpenGL ES 3.0 tulee – oletko valmis?

Aivan lyhykäisyydessään viimeaikojen uutisvirrasta poimittuna, Khronos Group on hiljattain julkaissut OpenGL ES 3.0 standardin speksin. ARM satsaa kovasti raudallaan sekä softatyökaluillaan mobiiligrafiikkaan ja toivon mukaan ensimmäiset laitteet nähdään jo pian!

Poimintoja tärkeimmistä uusista ominaisuuksista:

  • Alustariippumaton tekstuurien pakkaus on nyt osa standardia. Erittäin suuri helpotus cross-platform softan tekijöille.
  • Geometry instancing; monta kopiota samasta geometriasta (helppo esimerkki: metsän puut) voidaan nyt piirtää säästäen väylän kaistaa kun geometria tarvitsee lähettää vain kerran.
  • Occlusion queries: rautatuki artefaktien näkyvyyden selvittämiseen. Nopeuttaa ja suoraviivaistaa dramaattisesti vaikkapa lens flarejen yms. efektien toteuttamista.
  • Floating point, depth jne tekstuurit pakolliseksi osaksi speksiä
  • Ja mikä parasta, 3.0 speksi on 100% taaksepäin yhteensopiva 2.0 kanssa.

Lähteet:

 

Ehta modaalidialogi iOS:lla


Muista kehitysympäristöistä iOS-maailmaan tulevasta voi tuntua oudolta Cocoa Touchin modaalidialogien puute. Vaikkakin on mahdollista rakentaa kaiken muun päälle piirtyvä dialogi joka oletukselta (ja tämä on ohitettavissa) saa kaiken syötteen, dialogin näyttävä kutsu ei blokkaa vaan palaa välittömästi ja dialogin sulkeutuminen on otettava kiinni asynkronisella callbackilla (‘delegate’). Edes perustyökalu UIAlertView ei käyttäydy kuin muiden alustojen – vaikkapa MFC, Javascript, Symbian Series60/Series80, Java AWT/Swing, Qt, Android, GTK, jne.. – stock komponentit MessageBoxit ja InputDialogit.

Tämänkaltaista UI paradigmaa voi kuitenkin paitsi kaivata, myös tarvita; oletetaan etta ollaan rakentamassa iOS -toteutusta cross-platform sovellukseen jossa täytyy toteuttaa metodi vaikkapa seuraavasti:

std::string AskUserInput()
{
  // Semantiikka: kysy käyttäjältä syöte popup dialogilla 
  //   ja palauta annettu teksti
}

Miten tämä toteutetaan iOS:lla? Nostetaan kädet pystyyn, otetaan projektin arkkitehti Skypen päähän ja anotaan API:in muutosta joka mahdollistaisi syötteen kyselyn asynkronisen käsittelyn? Miksei, mutta jos yo. mekanismi on jo toteutettu viidelle muulle alustalle, on edessa iso muutostyö ja paljon hampaita kiristeleviä ihmisiä. Turha asynkronia näin yksinkertaisessa asiassa hankaloittaa sekä pirstaloittaa arkkitehtuuria.

Ongelman ratkaisu lähtee UI frameworkin toiminnan ymmärtämisestä. Käytännössä kaikissa moderneissa käyttöliittymäkirjastoissa on 1-N UI säiettä jotka palvelevat käyttöliittymää ns. event loopin [tapahtumakieriö?! -toim.] kautta. Event loop ottaa inputteja komponenteilta, timereilta tai vaikkapa muilta säikeiltä alustakohtaisten viestinvälitysmekanismien välityksellä ja tarjoilee niitä kiinnostuneille kuuntelijoille. Käytännössä jokaisella säikeellä on tasan yksi (tai nolla, jos kyseessä ei ole UI:ta palveleva säie) event loop, ja yleisesti mobiilikäyttöjärjestelmissä UI:ta palvelee vain yksi säie kerrallaan. Käyttöliittymä kumminkin toimii jouhevasti niin kauan kuin tätä säiettä ei varata kerralla liian pitkäksi aikaa; pitkäkestoinen laskenta täytyy joka jakaa pieniin osiin tai tehdä muissa säikeissä.

Event loopiin perustuvat järjestelmät sallivat yleensä event looppien sisäkkäisen ajamisen (nesting); tällöin vanha event loop aktivaatio pysähtyy eikä jatka ennenkuin sisempi loop aktivaatio loppuu. Tätä voidaan hyväksikäyttää asynkronian poistamiseksi; API-metodin sisällä ajetaan uusi aktivaatio, kuunnellaan asynkronista vastausta ja lopetataan aktivaatio kun vastaus on saatu. Tällöin APIa kutsuva metodi blokkaa koko suorituksen ajan. On kuitenkin erotettava tämä semaforin odottamisesta; säiehän ei ole idlena vaan suorittaa sisempää event loop aktivaatiota.

Rakennetaan esimerkkitoteutus iOS:lle. Halutaan tehda seuraavanlainen kutsu:

-(void) requestAndLogInput {
  InputDialog* dlg = [InputDialog dialog];
  [dlg showModal];
  NSLog(@"Dialog input was = %@", [dlg getInput]);
}

Aloitetaan dialogin luomisesta. Tähän käytetään normaalia Cocoa Touch boilerplatea:

+(InputDialog*) dialog {
    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"InputDialog"
                                                      owner:nil
                                                    options:nil];
    InputDialog* dlg = [[[nibViews objectAtIndex:0] retain] autorelease];

    return dlg;
}

Määritellään metodi jolla dialogi näytetään modaalisti – se asettuu päällimmäiseksi ja blokkaa kunnes dialog on suljettu. iOS:lla event loop tunnetaan nimellä Run loop, ja sitä vastaavat APIt ovat NSRunLoop sekä Core Foundationin CFRunLoop*:

-(void) showModal {
    // Lisätään dialogimme keywindow'n ylimmän lapsen ylimmäksi lapseksi,
    // jolloin dialogi päätyy päällimmäiseksi view stackiin
    UIWindow* keyWindow = [[UIApplication sharedApplication] keyWindow];
    UIView* topmostView = [keyWindow.subviews objectAtIndex:0];
    [topmostView addSubview:self];

    // Sijoitetaan dialogi suunnilleen keskelle ruutua    
    CGSize s = topmostView.bounds.size;
    self.center = CGPointMake(s.width / 2, (s.height / 2) - 70);

    // Ajetaan nested run loop aktivaatio jolloin showModal kutsu blokkaa
    CFRunLoopRun();
}

Lopuksi määritellään dialogin sulkeva IBAction:

-(IBAction) okPressed {
    // Poistetaan dialogi näkyvistä
    [self removeFromSuperview];

    // Pysäytetään sisempi run loop aktivaatio; kontrolli palaa 
    // ulommalle ja showModal -kutsu palaa
    CFRunLoopStop(CFRunLoopGetCurrent());
}

Valmista! Ja koska kyseessä  ovat erilliset run loop aktivaatiot eivätkä erilliset run loopit, esimerkiksi timerit jatkavat eloaan ja autorelease pool on jaettu aktivaatioiden välillä.

 Esimerkkiprojekti Xcode 4.5 -projektina. 40kB zip

 

Jo yli puolet iPhoneista päivitetty uusimpaan iOS 6.0 käyttöjärjestelmään

iPhonen käyttäjät ovat päivittäneet puhelimiaan ahkerasti. Alle kahdessa viikossa on iOS 6.0:n osuus noussut kuuteenkymmenen prosenttiin kaikista iPhoneista ja iPadeissa vastaavat luku on 45% (lähde: BGR).

Samaan aikaan Androidilla vain hiukan yli 25% käyttäjistä on päivittänyt laitteensa 4.x.x käyttöjärjestelmiin (lähde: developer.android.com). Suurin osa käyttäjistä on jymähtänyt Gingerbreadiin, joka ilmestyi jo kaksi vuotta sitten. Syy niheään päivitykseen löytyy laitevalmistajista, jotka tarjoavat erittäin hitaasti ja huonosti päivityksiä vanhempiin laitteisiin (ja myös uudempiinkin). Apple puolestaan tukee laitteita takautuvasti huomattavasti ahkerammin ja uusin iOS 6.0 oli tarjolla heti nyt jo kolme vuotta vanhaan iPhone 3gs puhelimeen (iPad 1 tosin tipahti hiukan harmillisesti kyydistä pois).

iPhone-kehittäjille käyttäjien päivitysinto tarkoittaa sitä, että uusien käyttöjärjestelmien tuomat edut päästään hyödyntämään nopeammin. iOS 5.0 mukana tulleet storyboardit nopeuttavat kehitystyötä ja iOS 6.0 mukana viimeisimmätkin navigaatiokomponentit muuttuivat kustomoitavaksi niin, että omien viritysten tekeminen ei ole enää tarpeellista.

Nykyisellä vauhdilla jo ensi keväänä on perusteltua tähdätä iOS 6.0+ käyttöjärjestelmille suunnattujen applikaatioiden tekeminen.

Android-tablettien osuus hurjassa kasvussa.

Pew Reseach Center’s Project for Excellence in Journalism on julkaissut tutkimustilastoja Yhdysvaltojen aikuisväestön tablet-tietokoneiden käytöstä ja laitteiden osuuksista.

Hulppea 25% amerikkalaisista aikuisista omistaa tablet-tietokoneen. Luku on valtava ottaen huomioon, että ensimmäinen nykyaikainen tablet iPad ilmestyi vuonna 2010.

Applen hallitsemat tablet-markkinat ovat myös mullistuneet vuoden aikana. Siinä missä vielä vuosi sitten Applen iPadit olivat 81% koko markkinasta, on tämän vuoden luku 52% ja Androideilla 48%. Androidin markkinaosuudesta 21% menee Kindle Firellä, joka käyttää Amazonin omaa versiota Androidista. Kehittäjille tämä on sinäänsä huono uutinen, koska tämä lisää entisestään fragmentaatiota Android-alustalla (nyt kauppoja onkin 2).

Markkinaosuuksien kasvusta huolimatta iPad on edelleen tablettien webbiselaustilastoissa ykkösenä hurjalla 85% osuudella.

Yllä olevat luvut kerättiin ennen Kindle Fire HD:n ja Google Nexus 7:n kauppoihin tuloa, joten Android-laitteet ovat jo todennäköisesti ohittaneet iPadit Yhdysvalloissa. Laitteista jälkimmäinen on vihdoin myös rantautumassa Suomeenkin ja hinnaksi näyttää asettuvan 269 euroa.

Applen vastaiskua tablettien parissa odotellessa.

Lua mobiilisoftassa

Käyttämällä skriptikieliä mobiiliohjelmoinnissa voidaan mm. edistää koodin jakamista eri alustojen kesken. Varsinkin peliohjelmoinnissa skriptien käyttäminen on suosittua: Pelilogiikkaa on helposti paljon ja sen kaiken kirjoittaminen erikseen eri ohjelmointikielillä eri alustoille olisi aikaavievää ja virhealtista. Eräs varsin suosittu skriptikieli on Lua. Lua on suhteellisen helppo yhdistää toiseen projektiin ja siihen on helppo luoda applikaatiospesifisiä laajennuksia. Lua on lähtökohtaisestikin suuniteltu laajennuskieleksi ja verrattuna esim. Pythoniin Lua on hyvin kevyt. Tässä artikkelissa opastetaan, miten Luaa voi käyttää iOS- ja Android-projekteissa.

Vaihe 0 — Luan lähdekoodi

Hae Luan lähdekoodi http://www.lua.org/ftp/.

Huomaa, että Lua on muuttunut jonkin verran versioiden välillä. Tämä ohje on kirjoitettu versiolle 5.2.1. Jotkin aiemmalle versiolle kirjoitetut Lua-ohjelmat eivät välttämättä toimi suoraan uusimmalla versiolla. Näitä ohjeita voi soveltaa myös muihin versioihin, mutta ne eivät välttämättä toimi sellaisenaan.


iOS

Vaihe 1 — Lua-kirjasto

  1. Luo uusi kirjastoprojekti. Tässä esimerkissä olen antanut projektille nimeksi Lua521 Luan versionumeron mukaan.Luo uusi kirjastoprojekti
  2. Poista projektiin automaattisesti luodut ProjektinNimi.m ja .h -tiedostot.
  3. Lisää projektiin Luan lähdekoodipaketista src-hakemiston sisältö lukuunottamatta tiedostoja lua.c, luac.c ja Makefile.
  4. Mene projektin Build Phases -ikkunaan ja lisää Copy Headers -vaihe.
    Build Phases
    Lisää vaihe
  5. Lisää Copy Headers -vaiheeseen projektin tiedostot lauxlib.h, lua.h, lua.hpp, luaconf.h ja lualib.h ja siirrä ne Public-osaan.
    Copy Headers -vaihe
  6. Lua-kirjasto on nyt valmis käytettäväksi.

Vaihe 2 — Lua-kirjaston käyttäminen toisessa projektissa

Esimerkkinä käytän kokonaan uutta projektia, mutta Luan lisääminen olemassaolevaan projektiin tapahtuu samoin.

  1. Lisää projektiin viittaus Lua-kirjastoprojektiin. Tämä tapahtuu kuten minkä tahansa muunkin olemassaolevan tiedoston lisääminen. Tässä tapauksessa lisättävä tiedosto on kirjastoprojektin .xcodeproj-projektitiedosto.
    Lisätään viittaus kirjastoon
    Lisättävä tiedosto on kirjastoprojektin .xcodeproj-tiedosto
  2. Aseta projektin targetti riippuvaiseksi Lua-kirjaston targetista.
  3. Lisää Lua-kirjasto linkitettäviin kirjastoihin.
    Appin Build Phase konffit
  4. Lisää Header Search Pathsiin polku $(BUILT_PRODUCTS_DIR)/usr/local/include.
    Header Search Paths
  5. Nyt projektissa voidaan käyttää Luaa.

Android

Vaihe 1 — Lua-kirjasto

Lua on kirjoitettu C-kielellä, joten Androidissa tarvitsemme NDK:ta sen kääntämiseen. Kätevintä on luoda siitä NDK-moduli. Android NDK:n ja NDK-modulien käytön opastaminen tässä olisi hieman liian työlästä, ja NDK:ssa on varsin hyvät dokumentaatiot ja esimerkit. Mainittakoon joka tapauksessa, että jos et ole aiemmin käyttänyt ja/tai luonut NDK-moduleita, helpointa on luoda yksi hakemisto jonka alle sijoitat kaikki NDK-modulit. Tämän hakemiston polku pitää olla NDK_MODULE_PATH-ympäristömuuttujassa.

  1. Luo uusi hakemisto NDK-modulihakemiston alle (tämän hakemiston nimi on luotavan modulin nimi).
  2. Kopioi tähän hakemistoon Lua-lähdekoodipaketin src-hakemiston sisältö lukuunottamatta tiedostoja lua.c, luac.c ja Makefile.
  3. Luo tähän hakemistoon seuraavansisältöinen Android.mk-tiedosto:
    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE := liblua521
    LOCAL_CFLAGS := -D"getlocaledecpoint()='.'"
    LOCAL_SRC_FILES := \
    	lapi.c \
    	lauxlib.c \
    	lbaselib.c \
    	lbitlib.c \
    	lcode.c \
    	lcorolib.c \
    	lctype.c \
    	ldblib.c \
    	ldebug.c \
    	ldo.c \
    	ldump.c \
    	lfunc.c \
    	lgc.c \
    	linit.c \
    	liolib.c \
    	llex.c \
    	lmathlib.c \
    	lmem.c \
    	loadlib.c \
    	lobject.c \
    	lopcodes.c \
    	loslib.c \
    	lparser.c \
    	lstate.c \
    	lstring.c \
    	lstrlib.c \
    	ltable.c \
    	ltablib.c \
    	ltm.c \
    	lundump.c \
    	lvm.c \
    	lzio.c
    
    LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
    include $(BUILD_STATIC_LIBRARY)

    Android.mk-tiedoston rivi LOCAL_MODULE := liblua521 määrää luotavan kirjaston nimen — sen voi muokata haluamakseen.

  4. Lua-kirjastomoduli on valmis käytettäväksi

Vaihe 2 — Lua-kirjaston käyttäminen toisessa projektissa

Koska itse Lua-kirjasto on NDK-moduli, pitää sitä myös käyttää NDK:lla käännettävän koodin kautta. Ohjeita NDK:n käyttöön saa muualta. Lua-kirjastomodulia käytetään kuten mitä tahansa NDK-modulia: Projektin Android.mk-tiedostoon lisätään tieto linkitettävästä kirjastosta, ja tiedoston lopussa kutsutaan import-module-makroa. Esimerkkiprojektini Android.mk-tiedosto näyttää seuraavanlaiselta:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE  := luatest
LOCAL_CFLAGS := -std=c99 -Wall -Werror
LOCAL_SRC_FILES := luatest.c
LOCAL_STATIC_LIBRARIES := lua521
LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)

$(call import-module,lua521)

Kiinnostavat rivit ovat siis LOCAL_STATIC_LIBRARIES := lua521, jossa linkitetään Lua-modulin luoma liblua521-kirjasto, sekä $(call import-module,lua521), jolla sisällytetään projektiin lua521-niminen Lua-moduli.


Vaihe 3 — Lua-koodin ajaminen

Jokaiselle sopivia tyhjentäviä ohjeita on hankala kirjoittaa, joten valaisen asiaa muutamalla yksinkertaisella esimerkillä. Näitä esimerkkejä soveltamalla päässee yllättävän pitkälle. Kaikki esimerkkikoodi on C:tä (ei Objective- tai -++); koodiesimerkkien yhdistäminen muuhun koodiin, oli kyse sitten Android- tai iOS-koodista, jätetään harjoitustehtäväksi.

Yksinkertaisen skriptin ajaminen

Lua-kirjaston funktiot on esitelty kolmessa .h-tiedostossa: lua.h, lualib.h sekä lauxlib.h. Koodiesimerkit on kirjoitettu olettaen että kaikki kolme otsikkotiedostoa on sisällytetty lähdekooditiedostoon. Huom! Nämä ovat nimeomaan C-headereita; jos Luaa käytetään C++-koodissa, sisällytettävä tiedosto on lua.hpp (ei muita).

Seuraava koodinpätkä ajaa lyhyen Lua-skriptin:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
static void luaExample() {
     lua_State* lua = luaL_newstate();
     luaL_openlibs(lua);
     const char* script = “print(\”Printing from Lua!\”)\n”;
     luaL_loadstring(lua, script);
     int err = lua_pcall(lua, 0, 0, 0);
     if(err != LUA_OK) {
          const char* errMsg = lua_tostring(lua, -1);
          printf(“Error while calling Lua function: %s”, errMsg);
     }
     lua_close(lua);
}

Rivillä 2 luodaan uusi lua_State-olio. Tämä olio on käytännössä kokonainen Lua-tulkki. Tapauksesta riippuen appissa voisi olla yksi lua_State koko sen elinkaaren ajan, tai siinä voidaan vaikka luoda ja tuhota lua_Stateja tarpeen mukaan. Kutsun palauttama olio on periaatteessa käytettävissä suoraan, mutta käytännössä aina pitää tehdä myös rivillä 3 oleva kutsu, joka lataa Lua-tulkkiin Luan standardikirjastot.

Rivillä 5 ladataan merkkijonosta Lua-funktio. Tässä kohtaa lienee parasta paneutua hieman Lua-tulkin C-APIin. Lua-tulkilla on oma operandipino. Suurin osa C-API-kutsuista käsittelee tätä pinoa. Esimerkiksi Lua-funktion kutsuminen tapahtuu siten, että pinoon työnnetään ensiksi kutsuttava funktio ja sitten funktion kaikki argumentit ensimmäisestä aloittaen. Tämän jälkeen kutsutaan lua_call()- tai lua_pcall()-funktiota. Tämä kutsu poistaa funktion ja argumentit pinosta, kutsuu funktiota ja funktion palattua työntää funktion mahdolliset paluuarvot pinoon ensimmäisestä aloittaen. Kun uusi Lua-tulkki luodaan, pino on tyhjä. Kutsu luaL_loadstring()-funktioon lukee annetusta merkkijonosta Lua-funktion ja työntää sen pinon päällimäiseksi. Tämän kutsun jälkeen siis pinon päällimmäinen (ja ainoa) olio on tuo Lua-funktio (tai virheilmoitus, jos funktion lataaminen epäonnistui).

Rivillä 6 kutsutaan edellisellä rivillä ladattua funktiota. Käytettävä C-funktio on lua_pcall(). Ero lua_call()- ja lua_pcall()-funktioiden välillä on, että lua_pcall() (p = protected) palauttaa virhekoodin jos kutsu epäonnistuu. Funktio lua_call() sen sijaan käyttää Lua-tulkin virheenhallintajärjestelmää, joka lähtökohtaisesti tulostaa virheilmoituksen ja kaataa koko appin. Kannattaa siis lähes poikkeuksetta käyttää lua_pcall():ia. Lua_pcall():lle annettavat argumentit ovat: lua_State, argumenttien määrä, paluuarvojen enimmäismäärä, ja message handlerin indeksi pinossa (0 ei ole validi indeksi, vaan se merkitsee, että message handleria ei ole; pinon indeksit alkavat 1:stä). Tässä tapauksesssa emme välitä yhtään argumenttia funktiollemme, emmekä ole kiinnostuneita sen mahdollisista paluuarvoista.

Rivillä 7 tarkistetaan, onnistuiko funktiokutsu. Jos paluuarvo on LUA_OK (0), funktiokutsu onnistui, ja pinossa on nyt funktion mahdolliset paluuarvot, kuitenkin enintään niin monta arvoa kuin lua_pcall()-kutsussa oli määrätty. Tässä tapauksessa pino on siis tyhjä. Muu paluuarvo merkitsee, että funktiota suoritettaessa tapahtui virhe. Paluuarvo kertoo virheen laadusta jotain (katso tarkempaa tietoa Lua Reference Manualista), mutta tässä tapauksessa tarkastelemme vain virheilmoitusta. Jos kutsu epäonnistuu, pinoon työnnetään (funktion paluuarvojen sijaan) virheilmoitus. Rivillä 8 haemme virheilmoituksen pinosta. Indeksi -1 tarkoittaa pinon päällimmäisintä alkiota (negatiiviset indeksit lasketaan pinon päältä alkaen). Tässä esimerkissä tänne ei koskaan tulla, mutta voit kokeilla virheenhallintaa muokkaamalla Lua-skriptin virheelliseksi.

Rivillä 11 lua_State tuhotaan. Lua_close()-kutsun jälkeen lua_Statea ei voi enää käyttää. Jos appissa luodaan Lua-tulkkeja tarpeen mukaan, on tärkeää muistaa tuhota ne lua_close()-kutsulla kun niitä ei enää tarvita, etteivät ne jää varaamaan muistia.

Argumentit ja paluuarvot

Seuraavassa on esimerkki Lua-funktiosta joka ottaa argumentteja ja palauttaa arvoja:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
static void argsAndRetsExample() {
    lua_State* lua = luaL_newstate();
    luaL_openlibs(lua);

    const char* script = "\
local arg1, arg2 = ...\n\
print(\"Lua function was called with \" .. arg1 .. \" and \" .. arg2)\n\
local ret1 = arg1 + arg2\n\
local ret2 = arg1 * arg2\n\
return ret1, ret2\n\
";
    luaL_loadstring(lua, script);

    lua_Number arg1 = 3.7, arg2 = -.1;
    printf("calling Lua function with %f and %f\n", arg1, arg2);
    lua_pushnumber(lua, arg1);
    lua_pushnumber(lua, arg2);
    int err = lua_pcall(lua, 2, 2, 0);
    if(err == LUA_OK) {
        lua_Number ret1 = lua_tonumber(lua, 1);
        lua_Number ret2 = lua_tonumber(lua, 2);
        printf("Lua function returned %f and %f\n", ret1, ret2);
    } else {
        const char* errMsg = lua_tostring(lua, -1);
        printf("Error while calling Lua function: %s\n", errMsg);
    }

    lua_close(lua);
}

Ajettaessa tämä tulostaa seuraavaa:

calling Lua function with 3.700000 and -0.100000
Lua function was called with 3.7 and -0.1
Lua function returned 3.600000 and -0.370000

Tarkastellaan ensin Lua-koodia. LuaL_loadstring()-funktiolla ladattu Lua-funktio on tyypiltään nk. vararg-funktio. Kaikki sen saamat argumentit on luettavissa vararg-lausekkeesta joka on muodoltaan kolme peräkkäistä pistettä (...). Lause local arg1, arg2 = ... rivillä 6 asettaa (paikallisen) muuttujan arg1 arvoksi funktion ensimmäisen argumentin ja muuttujan arg2 arvoksi toisen argumentin. Jos argumentteja ei ole tarpeeksi, muuttujan arvoksi tulee nil. Rivillä 10 Lua-funktio palauttaa arvoja. Lua-funktio voi palauttaa useita arvoja, tässä tapauksessa palautetaan kaksi arvoa.

Rivillä 12 ladataan funktio kuten edellä. Funktion argumentit työnnetään pinoon funktion jälkeen. Tässä kutsumme funktiota kahdella argumentilla. Riveillä 16 ja 17 työnnetään argumentit pinoon. Rivillä 18 suoritetaan kutsu. Argumenttien määrä on 2, ja haluamme enintään 2 paluuarvoa. Kutsun onnistuttua lukemme riveillä 20 ja 21 paluuarvot pinon pohjalta (funktio ja argumentit ovat poistuneet pinosta).

C-funktioiden kutsuminen Lua-koodista

Esimerkki C-funktion kutsumisesta:

1
2
3
4
static int myCFunction(lua_State* lua) {
    int numArgs = lua_gettop(lua);
    printf("myCFunction called with %d args\n", numArgs);
    for(int i = 1; i

Ajettaessa tämä tulostaa seuraavaa:

myCFunction called with 2 args
arg 1: 5
arg 2: bacon
myCFuntion returned 3.141592 and second retvalue

Lua-koodista kutsuttava C-funktio on muuten samankaltainen kuin mikä tahansa C-funktio, mutta se saa argumenttinsa ja palauttaa arvonsa Lua-pinon kautta. Kaikkien Luasta kutsuttavien funktioiden on otettava yksi argumentti tyypiltään lua_State* ja palautettava int. Kun funktiota kutsutaan, annetussa lua_Statessa on pinossa funktion saamat argumentit (ensimmäinen pinon pohjimmaisena jne.), eikä mitään muuta. Argumenttien määrä saadaan tietää pinon korkeudesta. Argumenttien tyypit voidaan myös selvittää. Funktio palauttaa arvoja siten, että kaikki paluuarvot työnnetään pinoon ensimmäisestä aloittaen ja funktion varsinainen paluuarvo (C-mielessä) kertoo Lua-paluuarvojen määrän. Pinoa ei tarvitse erikseen “puhdistaa” ennen palaamista.

Rivillä 2 selvitetään argumenttien määrä. Riveillä 4-6 oleva silmukka tulostaa kaikki funktion saamat argumentit (muutettuaan ne ensin merkkijonomuotoon). Funktio palauttaa kolme arvoa; ne työnnetään järjestyksessä pinoon riveillä 8-10. Rivillä 11 palautetaan paluuarvojen määärä.

Riveillä 18-21 on taulukko, jonka avulla C-funktio liitetään Luaan. Taulukossa on tietueita, joissa ensimmäinen arvo on funktion nimi Luassa ja toinen arvo on pointteri varsinaiseen funktioon. Viimeisenä on on tietue, jossa molemmat arvot ovat NULL. Tässä liitetään vain yksi funktio, mutta samalla voisi liittää useammankin funktion. Rivi 23 lukee em. taulukon ja luo Lua-taulukon, jossa on annetuilla nimillä viittaukset annettuihin C-funktioihin. Tässä tapauksessa siis (Lua-)taulukko, jossa on arvolla "myCFunction" viittaus rivin 1 funktioon. Rivillä 24 tämä taulukko annetaan globaalin muuttujan "myCFunctions"-arvoksi.

Lua-koodissa rivillä 27 kutsutaan "myCFunctions"-taulukon "myCFunction"-jäsentä, ts. edellä määriteltyä C-funktiota. Funktiokutsu tehdään kuten muutkin Lua-funktiokutsut. Tässä funktiolle annetaan 2 argumenttia. Funktion 2 ensimmäistä paluuarvoa asetetaan paikallisten muuttujien a ja b arvoiksi. Kuten tulosteesta huomataan, funktion kolmas paluuarvo jää huomiotta.

Voit kokeilla panna breakpointin myCFunction()-funktioon. Ohjelman pysähtyessä ko. breakpointiin, voit tarkastella kutsupinoa ja huomata, että kutsu on tullut Lua-tulkin sisältä.

Windows Phone -sovelluskehitys Androidin ja iPhonen rinnalla

Windows Phonen astuminen iOS:n ja Androidin rinnalle on saanut aikaan hieman hämmennystä joidenkin loppukäyttäjien ja sovellusten tilaajienkin mielissä. Siinä missä iOS (iPhone/iPad), Android ja jopa poistuva Symbian muistuttavat toisiaan yleisasultaan – kukin tietenkin erottautuen omilla pienillä eroavaisuuksillaan – Windows Phone on tietoisesti suuntautunut erilleen muista omalla Metro-suunnittelukielellään.

Ohje: maassa maan tavalla

Suunnitellessa uutta sovellusta mille tahansa alustalle, yleispätevänä ohjenuorana voitaneen antaa:

Kunkin tyyppisen mobiililaitteen käyttäjä haluaa systemaattisen käyttäjäkokemuksen ja käyttöjärjestelmälle tunnusomaisen sekä varsin yhdenmukaisen ulkoasun sovelluksille. Sovelluksen käyttöönotto on tällöin mahdollisimman luontevaa eikä vaadi syvällistä perehdytystä.

Tämä halu kumpuaa usein niin käyttäjien puolelta kuin myös käyttöjärjestelmien toimittajilta! Jotkut palvelujen kehittävät mieltävät ajattelevansa käyttäjän parasta pyrkimällä mahdollisimman yhdenmukaiseen käyttöliittymään laitteen käyttöjärjestelmästä riippumatta, mutta päätyvät tekemään käyttäjälle karhunpalveluksen. On poikkeustilanne, että ihmiset käyttävät samaa sovellusta eri käyttöjärjestelmillä, mutta varmaa, että he käyttävät sitä muiden saman käyttöjärjestelmän sovellusten lomassa.

Suunniteltaessa sovellusta usealle alustalle täysin samaa konseptia ei siis voi aina käyttää. Vaikka iPhone ja Android-sovellukset voivat muistuttaa toisiaan hämmentävän paljon, niillä on eronsa. WP:llä erot ovat muihin alustoihin verrattuna huomattavat.

Esimerkki

Alla käytännön vertailuesimerkki IMDB-sovelluksesta Applen iPhonelle ja Windows Phonelle.

Kuva 1. iPhone versio IMDB:n sovelluksesta.

Kuva 2. WP-versio IMDB-sovelluksesta.

Edellä esitetyt kuvat osoittavat selvästi, että iPhonen käyttäjät eivät pitäisi WP:n tyylisestä sovelluksesta puhelimessaan (mikäli se edes pääsisi Applen App Storeen), eivätkä vastaavasti Windows Phone -käyttäjät halua iPhone/Android-tyylistä sovellusta laitteilleen. Toisilleen vieraat esitystavat vaativat sovelluksen arkkitehdilta jo konseptin varhaisessa vaiheessa ydinidean hahmottamiskykyä ja näkemystä mitä kukin loppukäyttäjä haluaa siltä.

Microsoftin sivuilta löytyy (englanniksi) esimerkkejä erilaisista sovellustyypeistä Windows Phonella.

Päätelmä

Metro-tyyliohjeita noudattavat Windows Phone -sovellukset ovat niin toimintalogiikaltaan kuin ulkoasultaan selvästi erilaisia kuin iOS/Android-taustaiset sovellukset, vaikka tarjottava toiminnallisuus olisi sama.

Windows Phonen tarjoaman visuaalisesti rikkaan Panorama-näkymän avulla yritysten brändäys ja yleensäkin halutut teemat sovelluksissa voidaan saada muita alustoja vahvemmin esille (kuva 2). Tämä toki vaatii ylimääräisen graafisen aineiston luomista.

Vaikka Windows Phonella vältetään käyttöliittymäelementtien ylimääräisiä koristeita, kuten liukuvärejä, tekstuureita, varjostuksia ja kiiltoefektejä, sisältö pyritään vastaavasti tuomaan esiin niin, että lopputulos voi hyvin olla kauniimpi kuin muilla alustoilla.

 

 

tarinoita mobiilikehityksen maailmasta.