Android alapú szoftverfejlesztés...

69
Android alapú szoftverfejlesztés haladóknak oktatási segédanyag, mely a Társadalmi Megújulás Operatív Program Határon átnyúló együttműködés a szakképzés és a felnőttképzés területén c. pályázati felhívás keretében megvalósított Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg. 2013.

Transcript of Android alapú szoftverfejlesztés...

Page 1: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

oktatási segédanyag, mely a

Társadalmi Megújulás Operatív Program

Határon átnyúló együttműködés a szakképzés és a felnőttképzés

területén c. pályázati felhívás keretében megvalósított

Mobil alkalmazásfejlesztés az informatikai tudás innovatív

alkalmazásával című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú

projekt keretében valósult meg.

2013.

Page 2: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

1

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

Tartalomjegyzék 1. Bevezetés.............................................................................. 2 2. Telefon kezelése .................................................................. 3

2.1 Emuláció .......................................................................... 3 2.2 Szolgáltatási információk .................................................... 5 2.3 Feliratkozás mobilhálózati eseményekre ............................... 7 2.5 Üzenetek (SMS, MMS)........................................................ 9 2.6 Telefonhívások kezelése a háttérben ...................................11

3. Strukturált adatok kezelése..................................................14 3.1 SQLite jellemzők...............................................................14 3.2 Programozott SQLite .........................................................16 3.3 Segédosztályok ................................................................20

4. Hálózati kommunikáció........................................................22 4.1. Mobil hálózatok elérése ....................................................22 4.2. Wifi hálózatok elérése ......................................................23 4.3. Wifi-Direct ......................................................................23 4.4. A HTTP és HTTPS kapcsolat...............................................25 4.4. XML adatok feldolgozása...................................................27 4.5. Külön szálon futó hálózati műveletek .................................28 4.6. WebView osztály..............................................................30 4.7. TCP/IP alapú kommunikáció..............................................31 4.8 Adatforgalom figyelése......................................................32

5. Helymeghatározás ..............................................................33 5.1 Helymeghatározást támogató eszközök ...............................33

5.2 Beállítások az Android operációs rendszerben ...................33 5.3 Helymeghatározás Wifi AP-k segítségével .........................34 5.4 Mobil cellainformációk a helymeghatározás szolgálatában...36 5.5 Az Android platform geolokációs szolgáltatásai..................39

6. Multimédiás eszközök..........................................................43 6.1 A kamera használata.........................................................43

6.1.1 A rögzítéshez használható megoldások, jogosultságok ....44 6.1.3 Saját kamera alkalmazás készítése...............................48

6.2. Hangok lejátszása............................................................55 6.2. Hangfelvételek készítése ..................................................57

7. Szolgáltatások....................................................................59 7.1 Service-ek létrehozása a Manifest állományban ....................60 7.2. Started Service ...............................................................61 7.3. IntentService ..................................................................63 7.4. Bound Service.................................................................64 7.5. Foreground szolgáltatások ................................................66 7.6. Szolgáltatások automatikus elindítása ................................68

Page 3: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

2

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

1. Bevezetés

Jelen jegyzetben, amely az „Android alapú szoftverfejlesztés kezdőknek” közvetlen folytatása, már ismertnek tekintjük a platform alapjait. Feltételezzük, hogy a fejlesztőeszköz használatát, az alkalmazások felépítését, közzétételi módját sikeresen elsajátította a kedves olvasó. Ily módon a folytatásban már mélyebb vizekre evezhetünk: megtanulhatjuk a segítségével a telefonhívások kezelését, indítását, vagy értesítést kaphatunk arról, hogy ha ilyen esemény következik be a készülékünkön. Hasonló érdekességeket tartogat az SMS-ek kezeléséről, saját rövid szöveges üzenetek írásáról szóló rész is. A strukturált adatkezelést megvalósító, SQLite technikákat bemutató rész az előző anyagból megismert adattárolási technikákat bővíti ki adatbázis-képességekkel. Hogyan tudjuk kommunikációra bírni programjainkat a hálózaton keresztül? Milyen módon vegyük figyelembe alkalmazásainkban a lokalizációs információkat? Van-e esélyünk erre GPS nélkül is? Hogyan kell le- vagy feltölteni állományokat a hálózaton keresztül, s milyen adatforgalommal jár ez? A tananyag a multimédiás eszközök kihasználásáról is szót ejt: vonjuk be programjainkba a beépített kamerát, vagy épp játsszunk le zenét alkalmazásunk segítségével. Hogyan lehet háttérben futó szolgáltatásokat létrehozni, több szálon futtatni? Ezekre a kérdésekre is igyekszünk pontos válaszokat adni a jegyzet befejező részében. Célunk, hogy a jegyzet és a hozzá tartozó gyakorlati példák felhasználásával a kedves érdeklődő végül önmaga is képes legyen összetett alkalmazások fejlesztésére melyek kihasználják az Android operációs rendszer által nyújtott lehetőségeket. Ezekre a kérdésekre is igyekeznek választ adni a haladóknak szóló fejezetek. A jegyzetben szereplő példaprogramok tesztelése (hasonlóan a kezdő tananyaghoz) Android 4.1 alatt, Samsung Galaxy SIII mobilokon, illetve Samsung Galaxy Tab2 tableteken történt.

Page 4: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

3

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

2. Telefon kezelése

Annak ellenére, hogy az Android eredetileg egy mobil operációs rendszernek készült, a kezdő jegyzetben még keveset foglalkoztunk azzal, hogy az eszközünk jó eséllyel egy mobiltelefon is egyúttal. Igaz, ez a helyzet a tabletek elterjedésével némileg megváltozott. Számos olyan Android készülék került általuk forgalomba, ami nem tartalmaz telefonhívásra alkalmas modult (Európában GSM, az Egyesült Államokban CDMA is). Természetes törekvés tehát, hogy megismerjük a rendszer telefóniával kapcsolatos részeit, de vegyük ugyanakkor figyelembe alkalmazásaink fejlesztésekor, hogy nem minden készülék tartalmaz ehhez megfelelő hardvert! A fejezet megértése igényli néhány mobiltelefonálással kapcsolatos fogalom (SIM, PIN kód, PUK kód, stb.) ismeretét. 2.1 Emuláció

Kezdjük az ismerkedést azzal, hogy a saját készülékünkön, vagy az emulátoron hívást, vagy üzenetküldést szimulálunk! Ehhez a legkézenfekvőbb módszer a fejlesztőeszköz használata. A DDMS-ről (Dalvik Debug Monitor Server), azaz a hibakeresést, naplózást, port-átirányítást végző eszközről már esett szó a kezdő tananyagban. A DDMS segítségével lehetőségünk van a bejövő hívások, érkező SMS üzenetek emulálására is, így megfigyelhetjük programjaink működését ilyen esetekben is. Ehhez első lépésként kattintsunk az Eclipse DDMS gombján, vagy válasszuk ki a Window/Open Perspective/DDMS menüpontot! A DDMS perspektíván belül az Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt a Telephony Actions szakaszon a bejövő szám megadását követően a Voice-ot választva hívást (Call), az SMS-t választva bejövő üzenetet adhatunk meg (Send).

1. ábra Az Emulator Control ablak

Page 5: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

4

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

Másik lehetőség a telnet kliens segítségével kapcsolódás akár távolról is a csatlakoztatott telefonhoz, vagy virtuális eszközhöz. Ehhez Windows XP, vagy Linux operációs rendszerek esetében a gyári telnet kliens programra, újabb Windowsok esetében pedig valamilyen külső programra van szükség. Ilyen ingyenes eszköz lehet például a putty1 terminál program. A belépéshez meg kell adni az eszköz nevét, vagy IP címét, továbbá azt a hálózati portot, amelyen keresztül a kapcsolat felépülhet. A telnet kliens segítségével a belépés történhet például így: telnet localhost 5554

2. ábra Távoli belépés konzolról

A telefonhívásokhoz használhatjuk a gsm call <telefonszám>, üzenetküldéshez pedig az sms send <telefonszám> <üzenet> formátumot! Segítséget a használható parancsokról, vagy az egyes parancsok paraméterezéséről a help segítségével kérhetünk. A módszerek kombinálásával akár konferenciahívást is készíthetünk! A hívásokkal és üzenetekkel párhuzamosan ellenőrizhetjük a naplót is fejlesztőeszközből, a LogCat segítségével.

1 PuTTY Download Page - http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html

Page 6: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

5

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

3. ábra LogCat hívás közben

A könnyebb követhetőség érdekében szűrjük a listát! A hívások esetében a com.android.phone, üzenetkezeléshez a com.android.mms az alapértelmezett alkalmazás. Tekintsük át az ehhez használt szolgáltatásokat! 2.2 Szolgáltatási információk

A programozott hívásokhoz, a telefonos hálózat adatainak lekérdezéséhez vezető úton elsőnek ismerkedjünk meg a TelephonyManager osztály (rendszerszolgáltatás) használatával! Az osztály az android.telephony csomag része. A feladata az eszközön elérhető telefonos szolgáltatásokkal kapcsolatos információk biztosítása. Ilyenek lehetnek például a SIM kártya telefonszáma, a hálózati operátor kódja, vagy roamingadatok is. Az alkalmazások ezen kívül fel is iratkozhatnak olyan eseményekre, amelyek a telefon állapotváltozásaival valamilyen módon összefüggenek. A szolgáltatás használatához ne példányosítsuk az osztályt közvetlenül, hanem kérjünk rá referenciát a Context.getSystemService(Context.TELEPHONY_SERVICE) segítségével, vagy a (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE) kasztoláson keresztül. Egy ilyen kódrészletre példa: TelephonyManager tm = (TelephonyManager) getSystemService(android.content.Context.TELEPHONY_SERVICE); Az információk lekérdezéséhez használható fontosabb metódusok listája:

· getLine1Number(): stringként visszaadja a készülékben lévő első SIM kártya számát.

Page 7: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

6

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

· getNetworkOperator(): megadja az aktuális beregisztrált szolgáltató (MNC, mobile network code) és ország (MCC, mobile country code) kódját, szintén string formátumban.

· getNetworkOperatorName(): a hálózati szolgáltató neve szöveges formában.

· getSimState(): a SIM kártya jelenlegi állapota. Lehetséges értékei:

o SIM_STATE_READY: a kártya hívásindításra és fogadásra kész.

o SIM_STATE_NETWORK_LOCKED: a kártya zárolt, hálózati PIN szükséges a feloldásához.

o SIM_STATE_ABSENT: nincs elérhető kártya a készülékben.

o SIM_STATE_PIN_REQUIRED: PIN kód megadására várunk, addig csak segélyhívások érhetők el.

o SIM_STATE_PUK_REQUIRED: ugyanaz, mint a PIN_REQUIRED, de PUK kódra vonatkozóan.

o SIM_STATE_UNKNOWN: a kártya állapota ismeretlen. Ilyen előfordulhat például állapotváltások között.

· getPhoneType(): Az eszköz milyen típusú mobil hanghívások átvitelére alkalmas. Lehetséges értékei:

o PHONE_TYPE_GSM: jellemzően Európában használt átviteli mód.

o PHONE_TYPE_CDMA: tipikusan az Egyesült Államokban, vagy Kanadában használt átvitel.

o PHONE_TYPE_SIP: hangátvitel jellemzően internetes hálózaton

o PHONE_TYPE_NONE: hiányzó modul. Táblagépeken előfordulhat.

· isNetworkRoaming(): logikai igaz értékkel tér vissza, ha jelenleg roamingol a kártya. Érdemes tekintettel lenni rá adatforgalmazás esetében is, akár az alkalmazásunkban is.

A fenti metódusokon túl még számos másik elérhető, a platform fejlődésével újabbak is. A mindenkori teljes listát elérhetjük az Android fejlesztői oldaláról3. Bizonyos függvények a működésükhöz igényelhetik az android.permission.READ_PHONE_STATE jogosultságot, aminek a segítségével a telefon állapotát olvasni tudjuk. API level 3, vagy ennél kisebb verziószám esetében ezt a jogot implicit is megkaphatjuk.

3Android Developer - TelephonyManager - http://developer.android.com/reference/android/telephony/TelephonyManager.html

Page 8: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

7

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

2.3 Feliratkozás mobilhálózati eseményekre

Ahhoz, hogy értesüljünk a hívásállapot változásairól, új hangposta üzenetről, vagy a GSM modul kikapcsolásáról, érdemes feliratkoznunk a minket érdeklő történésekre programunkból. Ehhez a Telephony Manager szolgáltatásunk listen() metódusával meg kell hívnunk egy saját PhoneStateListener implementációt, megadva, hogy mely események érdekelnek minket. //Saját PhoneStateListener készítése private PhoneStateListener sajatPhoneListener = new PhoneStateListener() { public void onCallStateChanged(int state, String incomingNumber) { try {

if (state == TelephonyManager.CALL_STATE_RINGING ) //TODO: Bejövő hívás történik } catch (Exception e) {

Log.i("Kivétel", "PhoneStateListener() e = " + e); }

} //Telephony Manager szolgáltatás igénybe vétele TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); //Feliratkozás változás-figyelésre tm.listen(sajatPhoneListener, PhoneStateListener.LISTEN_CALL_STATE); A fenti példában a vizsgálni kívánt állapot a bejövő hívás esete (CALL_STATE_RINGING). Az incomingNumber értéke a bejövő hívás hívószáma. Ezen kívül még más állapotokra is kíváncsiak lehetünk:

· CALL_STATE_OFFHOOK: legalább egy hívás folyamatban, vagy tartásban van.

· CALL_STATE_IDLE: nincs hívási aktivitás Nem csupán a hanghívás, de a mobilnet állapota is érdekelhet minket. Ebben az esetben a LISTEN_CALL_STATE helyett a LISTEN_DATA_CONNECTION_STATE-et kell megadni a listen() függvénynek. Az onCallStateChanged() függvény helyett a megfelelő callback függvény neve: onDataConnectionStateChanged(int state).

Page 9: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

8

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

A lehetséges TelephonyManager állapotok pedig:

· DATA_CONNECTING: mobilnet kapcsolat felépítése folyamatban van.

· DATA_CONNECTED: a mobilnet felépült. · DATA_SUSPENDED: a mobilnet kapcsolat felfüggesztésre

került, például egy bejövő hanghívás miatt. · DATA_DISCONNECTED: a mobilnet nem érhető el.

2.4 Telefonhívás kezdeményezése programból Amennyiben saját alkalmazásunk tartalmaz, vagy listáz telefonszámokat is, felmerülhet az igény, hogy egy szám kiválasztása után közvetlen is hívható legyen programunkból, ne kelljen ehhez a felhasználónak számot másolni és belépni a tárcsázó programba. Ennek megvalósításához új engedélyre, vagy engedélyekre lehet szükségünk a programunk részére. Tárcsázáshoz az android.permission.CALL_PHONE jogosultságot kell igényelnünk a manifeszt állományunkban. Amennyiben android.permission.CALL_PRIVILEGED jogot igénylünk a futtatáshoz, akkor az alkalmazásunk képes bármilyen szám, akár segélyhívó közvetlen hívására is anélkül, hogy a felhasználónak a hívást a tárcsázó alkalmazáson keresztül előzetesen még jóvá kellene hagynia. A programozott hívás indítása legegyszerűbben egy intent küldésével lehetséges, ami megkapja a hívandó telefonszámot is. Hívás megvalósítása az ACTION_CALL intent segítségével: Intent hivoIntent = new Intent(Intent.ACTION_CALL); hivoIntent.setData(Uri.parse("tel:+36301234567")); try { startActivity(hivoIntent); } catch (ActivityNotFoundException e) { //TODO: nem találtunk tárcsázóprogramot } A tárcsázandó számot az URI tartalmazza. Üres bemenet esetében csak a tárcsázó program indul el. Amennyiben a telefon kapcsolatlistájában található személy ID-jét ismerjük, használható a következő formula is a tárcsázáshoz: Uri.parse("content://contacts/people/" + SZEMÉLY_ID)

Page 10: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

9

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

Vészhívások indítására az intent nem alkalmas, ehhez az ACTION_DIAL-t kell használnunk. Utóbbi intent abban is különbözik az ACTION_CALL-tól, hogy bár behozza a tárcsázó programot a kívánt telefonszámmal együtt, de magát a hívást a felhasználónak kell kezdeményeznie. Egy további lehetőség az ACTION_CALL_BUTTON intent használata, ami a nevének megfelelően a tárcsázó gomb megnyomásával egyenértékű. Ez az intent tehát úgy hívja be a tárcsázót, hogy nem ad át neki hívandó számot. 2.5 Üzenetek (SMS, MMS)

A hanghívásokon túl az operációs rendszer lehetővé teszi rövid szöveges, vagy multimédiás üzenetek küldését, kezelését is. SMS küldése Az ACTION_SENDTO intent segítségével lehetséges rövid üzenet küldése. A tényleges küldés az alapértelmezett SMS küldő alkalmazáson keresztül valósul meg. Az intent paraméterei:

· Uri.parse("sms:+36301234567") adatként. · "sms_body" kulccsal az üzenet törzse extraként.

A küldéshez az alkalmazásnak szüksége van az android.permission.SEND_SMS jogosultságra.

Intent smsKuldIntent = new Intent(); smsKuldIntent.setAction(Intent.ACTION_SENDTO); //A címzett telefonszáma smsKuldIntent.setData(Uri.parse("tel:+36301234567")); //Az üzenet szövege smsKuldIntent.putExtra("sms_body", "Szia! Ez egy tesztüzenet"); try{ startActivity(smsKuldIntent); } catch (ActivityNotFoundException e) { //TODO: Nincs alapértelmezett SMS küldő alkalmazás } SMS fogadása Ahhoz, hogy rövid üzenetek fogadni tudjunk, röviden meg kell ismerkednünk az SmsManager osztállyal. Az osztály az API level 4 óta az android.telephony csomagban található, ezt megelőzően a helye az android.telephony.gsm-ben volt. A változtatás oka többek között az,

Page 11: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

10

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

hogy az új osztály már nem csak GSM, hanem CDMA üzeneteket is kezel. A használatához először referenciát kell, hogy kapjunk az SmsManager.getDefault() segítségével. Amikor egy Androidos készülék SMS-t kap, broadcast üzenet generálódik. Ezt kell az alkalmazásunk által regisztrált saját BroadcastReceiver osztállyal elkapnunk. Az SMS üzenetek, a metaadataikkal (telefonszám, idő) együtt az intent extrái között található, „pdus” kulcs alatt. Az android.telephony.SmsMessage.createFromPdu() metódusnak a fenti bájt tömböt odaadva tudjuk az eredeti üzeneteket és a metaadatokat könnyen lekérdezni. Az SmsMessage osztály (hasonlóan az SmsManager osztályhoz) API level 4 óta érhető el a fenti helyen, korábban az android.telephony.gsm csomagban volt. A fogadáshoz a programnak android.permission.RECEIVE_SMS jogosultság bejegyzésére is szüksége van. SMS fogadása tehát: public class IncomingSMSReceiver extends BroadcastReceiver { private static final String SMS_RECEIVED "android.provider.Telephony.SMS_RECEIVED"; public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(SMS_RECEIVED)) //SMS érkezett { SmsManager sms = SmsManager.getDefault(); Bundle csomag = intent.getExtras(); if (csomag != null) { Object[] pdus = (Object[])csomag.get("pdus"); SmsMessage uzenet = null; for (int i = 0; i < pdus.length;i++)

{ uzenet=SmsMessage.createFromPdu((byte[])pdus[i]); //Az üzenet törzse String torzs = uzenet.getMessageBody(); //Az üzenet feladója String felado = uzenet.getOriginatingAddress(); }//for vége }//if vége }//SMS érkezett vége }//onReceive vége }//class vége

Page 12: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

11

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

MMS küldése Az ACTION_SEND intent segítségével nem pusztán üzenet, de adat továbbítása is lehetséges. Ezt kihasználva fájlt, például képet csatolhatunk a szöveges üzenet mellé. Ugyanazon android.permission.SEND_SMS jogosultság szükségeltetik itt is, mint az SMS küldés esetében. A küldéshez szükséges teendők:

· A címzett telefonszáma extra, amit az „address” kulcs határoz meg.

· Az üzenet szintén extra, „sms_body” kulcs rejti. · Az intent adat a csatolt fájl útvonala lesz, Uri.parse-ban

megadva. · setType() határozza meg a csatolás típusát. · Az intent adatfolyamát az EXTRA_STREAM-ben is meg kell

határozni (csatolt fájl útvonala). Példaprogram MMS küldésére: File csatoltFajl = new File("útvonal\képfájl_neve.jpg"); Uri csatolasUtvonala = Uri.parse(csatoltFajl.toString()); Intent mmsKuldIntent = new Intent(); mmsKuldIntent.setAction(Intent.ACTION_SEND); mmsKuldIntent.putExtra("address", "+3630123456"); mmsKuldIntent.putExtra("sms_body", "Szia! Csatoltam egy jó képet!"); mmsKuldIntent.setData(csatolasUtvonala); mmsKuldIntent.setType("image/jpg"); mmsKuldIntent.putExtra(Intent.EXTRA_STREAM, csatolasUtvonala); try{ startActivity(mmsKuldIntent); } catch (ActivityNotFoundException e) { //TODO: Nincs alapértelmezett MMS küldő program } 2.6 Telefonhívások kezelése a háttérben

A 2.3 fejezetben láttunk már példát bejövő hívás kezelésére. A módszer azonban csak akkor működőképes, ha az alkalmazásunk futó állapotban van. Ahhoz, hogy a háttérben is megbízhatóan teljesítő alkalmazást készítsünk, érdemes saját BroadcastReceiver osztályt készíteni az üzenet elfogásához. Röviden a szükséges teendők és információk:

Page 13: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

12

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

· Természetesen az android.permission.PHONE_STATE engedélyre szüksége van az alkalmazásnak.

· A szükséges intent-filter az android.intent.action.PHONE_STATE.

· Az intent extrái között kapjuk meg a hívással kapcsolatos információkat:

o A TelephonyManager.EXTRA_STATE jelzi az új állapotot. o A TelephonyManager.EXTRA_STATE_RINGING jelzi a

csörgést. o A hívó száma a

TelephonyManager.EXTRA_INCOMING_NUMBER-ben található.

o Az EXTRA_STATE_OFFHOOK állapot jelzi, hogy a hívás felépült a két fél között.

o Az EXTRA_STATE_IDLE pedig az az állapot, amikor a hívás elutasításra kerül.

Szükséges tehát egy osztály, ami BroadcastReceiver lesz, ebben valósítjuk meg az onReceive metódust, ami a következőképpen nézhet ki: public void onReceive(Context context, Intent intent) { String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); if(state.equals(TelephonyManager.EXTRA_STATE_RINGING)){ // Csörög a telefon, a bejövő szám a // TelephonyManager.EXTRA_INCOMING_NUMBER-ben van }else if(state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){ // A hívás fogadva

}else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)){

// A hívás eldobva, vagy elutasítva }

} Nyilván a saját BroadcastReceiver osztályt is be kell jegyezni a manifeszt állományban statikusan (vagy dinamikusan a Context.registerReceiver() segítségével): <receiver android:name=”SajátBroadcastReceiverOsztályNeve”> <intent-filter> <action android:name=”android.intent.action.PHONE_STATE”/> </intent-filter> </receiver>

Page 14: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

13

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

Hasonlóan bánhatunk a kimenő hívások kezelésével is. Annyi eltéréssel kell számolnunk a bejövő hívásokhoz képest, hogy ezúttal nem beérkező, hanem hívott számról beszélhetünk. Ennek a helye a TelephonyManager.EXTRA_PHONE_NUMBER lesz, ezúttal is az intent extra részében. További eltérés, hogy a megcélzott esemény nem az android.intent.action.PHONE_STATE lesz, hanem az android.intent.action.NEW_OUTGOING_CALL. Az ehhez szükséges jogosultság is meg fog változni, ezúttal az android.permission.PROCESS_OUTGOING_CALLS lesz az, amire szüksége van az alkalmazásunknak. public void onReceive(Context context, Intent intent) { String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); if(state.equals(TelephonyManager.EXTRA_STATE_RINGING)){ // Kicsöng a telefon, a hívott szám a // TelephonyManager.EXTRA_PHONE_NUMBER-ben van } } A hozzátartozó BroadcastReceiver bejegyzés pedig: <receiver android:name=”SajátBroadcastReceiverOsztályNeve”> <intent-filter> <action android:name=”android.intent.action.NEW_OUTGOING_CALLS”/> </intent-filter> </receiver>

Page 15: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

14

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

3. Strukturált adatok kezelése

A kezdő jegyzetben már tanulhattunk arról, hogyan kell alkalmazásunk adatait perzisztens módon tárolni. Az a fajta, fájl szintű, vagy egyszerű kulcs-érték párokon alapuló technika a hozzáférés módját is korlátok közé szorítja. A mobil eszközök teljesítményének a növekedésével természetes igénnyé vált, hogy hordozható készülékeinken is megvalósuljon olyan minőségű keresés és szelektálás adataink között, ami már adatbázis-kezelő program használatát is igényelheti. Szerencsénkre az Androidra „gyárilag” implementált SQLite adatbázismotor lehetővé teszi, hogy ne csupán online, de a kézi eszközeinken tárolt offline adataink kezelését is fejlettebb adattárolási módszerekkel biztosíthassuk. Ebben a fejezetben elsőként a közvetlen hozzáférés megtárgyalására koncentrálunk, ezt bővítjük a későbbiekben segédosztályokkal, amik a verziókezelést is támogatják. A fejezetben feltételezzük az SQL adatbázisokkal kapcsolatos alapfogalmak (kulcsok, tulajdonságok, SQL utasítások) ismeretének meglétét. 3.1 SQLite jellemzők

Az SQLite teljes értékű relációs adatbázis-kezelő, ami viszont nem támogat objektum-relációs réteget az adatbázis fölött (ORM, Object Relational Mapping) – noha kiegészítésként írtak már rá ilyet, lásd ORMLite, greenDAO -, ezért az adatokhoz tartozó sémát és lekérdezéseket nekünk kell megírni a programban. Maga az SQLite nem külön folyamatként, hanem a saját alkalmazásunk részeként fog futni. Ily módon az adatbázist tartalmazó fájl is alapesetben csak a mi programunk számára lesz elérhető. Az Android rendszerben jelenleg elérhető SQLite3 által közvetlenül támogatott adattípusok a következők4:

· INTEGER: előjeles egész 1,2,3,4,6 vagy 8 byte hosszon, a tárolni kívánt érték nagyságának függvényében

· REAL: lebegőpontos értékekhez, 8 byte-on · TEXT: UTF-8, vagy UTF-16-ban tárolt szöveg · BLOB: adatsorozatok, fájlok tárolásához használt típus · NULL: üres érték tárolására

Sajnos a logikai, az idő és a dátum adatok tárolására csak a fenti típusokkal közvetve van lehetőség.

4 Datatypes in SQLite Version 3 - http://www.sqlite.org/datatype3.html

Page 16: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

15

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

Támogatott az autoincrement tulajdonság, amivel például kulcsok automatikus értékadása válik kényelmessé. Az SQLite-hoz közvetlenül hozzáférhetünk konzolról, például debuggolás céljából. Erre a célra az Android SDK „tools” könyvtárában található programot használhatjuk. Ehhez a következő lépésekben juthatunk el:

· Nyissuk meg a parancssort, amennyiben a keresési útvonalban nem szerepelne, úgy keressük meg az adb parancsot az SDK-ban!

· Az „adb devices” futtatásával ellenőrizzük le, hogy fut legalább egy virtuális eszköz, vagy van csatlakoztatva Android készülék a számítógéphez.

· Kérjünk egy parancshéjat az „adb shell” elindításával! · Az sqlite3 futtatásával csatlakozhatunk az adatbázis-kezelőhöz.

A használható parancsokat a .help segítségével listázhatjuk.

4. ábra Sqlite konzol

Egy konkrét alkalmazás adatbázisának megtekintéséhez érdemes a program saját könyvtárában az adott adatbázist tartalmazó fájlt, mint paramétert megadva indítani az sqlite3 alkalmazást. Az Androidos címjegyzékbe (Személyek) például a következő úton léphetünk be (feltételezve, hogy a shell-be már beléptünk):

· cd /data/data/com.android.providers.contacts/databases · sqlite3 contacts2.db (adatbázis neve) · A táblákat, nézeteket a .tables parancs listázza. · Standard SQL parancsok használhatók.

Page 17: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

16

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

· Kilépés a .exit-tel lehetséges.

5. ábra A contacts adatbázis táblái és nézetei

A konzol segítségével importálni, exportálni, dump-olni is tudunk adatbázist. 3.2 Programozott SQLite

Az SQLite képességeit közvetlenül kihasználni az android.database.sqlite csomag segítségével tudjuk, mivel ez tartalmazza a privát adatbázisok menedzselésével kapcsolatos osztályokat. Az itt bemutatásra kerülő módszer alkalmazások között megosztott adatok kezelésére nem alkalmas. A következő példaprogram egy egyszerű, egyetlen táblából álló adatbázist kíván kezelni, amelybe az ismerőseink nevét kívánjuk csupán tenni. Először is hozzuk létre az adatbázisunkat, benne egy táblával! Amennyiben már létezik a fenti adatbázis, akkor csak nyissuk meg azt! Ugyanez vonatkozik a táblára is: csak akkor hozzuk létre, ha első alkalommal futtatjuk a programunkat. A könnyebb behelyettesíthetőség érdekében az adatbázist tartalmazó fájl és a tábla nevét tároljuk változókban! Az adatbázisfájl nevénél nem kötelező konvenció a .db kiterjesztés használata. Az adatbázist tartalmazó fájlt az openOrCreateDatabase() metódussal készíthetünk, SQL utasítást pedig az execSQL()-el tudunk futtatni. //Az adatbázist tartalmazó fájl és tábla neve

Page 18: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

17

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

final static String ADATBAZIS_NEVE = "adatbazis.db"; final static String TABLA_NEVE = "ismerosok"; //Az adatbazis neve a programunkban SQLiteDatabase adatbazis; //Hozzuk létre az adatbázisfájlt, vagy ha már létezik, nyissuk meg! adatbazis = openOrCreateDatabase(ADATBAZIS_NEVE, SQLiteDatabase.CREATE_IF_NECESSARY, null); //Az execSQL segítségével tetszőleges SQL parancsot //futtathatunk, itt arra használjuk, hogy a táblát létrehozzuk. adatbazis.execSQL("CREATE TABLE IF NOT EXISTS " + TABLA_NEVE + "(id INTEGER PRIMARY KEY AUTOINCREMENT, nev TEXT )"); Adat beszúrása a táblába Az SQLiteDatabase osztály insert() metódusával tudunk új adatot felvinni frissen létrejött táblánkba. Három paramétert igényel a működése:

· A tábla neve, ahová beszúrunk. · Null érték: az úgynevezett nullColumnHack helye, mivel

teljesen üres (NULL) sort nem enged beszúrni úgy, hogy legalább egy attribútum (oszlop) nevét nem adtuk meg.

· Érték: a kulcs az oszlop neve, az érték pedig a hozzátartozó beszúrandó elem lesz.

A beszúrás eredményeként visszaadott érték a sorazonosító (row ID) lesz, vagy -1, ha a beszúrás valamiért nem sikerült. Az értékpár tárolásához használhatunk például ContentValues példányt. ContentValues ertekpar = new ContentValues(); ertekpar.put("nev", "Janos"); long uj_id = adatbazis.insert(TABLA_NEVE, null, ertekpar); Adat törlése a táblából Ugyanezen osztály delete() metódusa szolgálhat hagyományosan egy, vagy több sor törlésére a táblánkból. A futtatásához szükséges paraméterek:

· A tábla neve, ahonnan törlünk.

Page 19: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

18

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

· whereClause: opcionális feltétel, ami az SQL WHERE utasításrészének felel meg. Elhagyható, ilyenkor null-t adjunk át.

· Amennyiben adtunk meg feltételt, annak paramétereit szöveges tömbben adhatjuk át. Feltétel hiánya esetén itt is null adható meg.

A metódus visszaadott értéke azon sorok száma, amelyekre vonatkozik a feltétel, más esetben 0-t kapunk vissza. Amennyiben adunk meg feltételt, abban a paraméterek helyét a „?” segítségével tudjuk megadni. A következő programrészlet azokat a sorokat fogja törölni a táblából, amelyeknek az ID mezője 2 és 4 közötti. String[] parameter = new String[2]; parameter[0] = "2"; parameter[1] = "4"; adatbazis.delete(TABLA_NEVE, "ID BETWEEN ? AND ?", parameter);

Adatok módosítása a táblában

A törléshez kicsit hasonló módon kell meghívni a módosításhoz szükséges update() metódust is. A különbség abban van, hogy itt az új értékeket is meg kell adni, amire a feltételnek eleget tevő sorok módosulni fognak. Az update() paraméterei tehát:

· A módosítandó tábla neve. · Az értékek, amire módosítunk. · Feltétel, amely a módosítandó sorokat kijelöli. Null megadása

esetén valamennyi sor módosulni fog. · Feltétel esetében szöveges paramétertömb.

A visszatérési érték a módosított sorok száma lesz. ContentValues ujertekpar = new ContentValues(); ujertekpar.put("nev", "Géza"); adatbazis.update(TABLA_NEVE, ujertekpar, "nev LIKE ?", new String[]{"János"} ); Ez a kis kódrészlet a János nevű ismerőseinket kereszteli át Gézára. A paramétertömb ezúttal nincs külön deklarálva és csak egyetlen elemű.

Page 20: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

19

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

Adatok lekérdezése az adatbázisból A lekérdezésekhez a névtúlterhelt query()használható, amely szabványos kurzort ad vissza a lekérdezés eredményeként. A query() tipikus paraméterlistája:

· A lekérdezéshez használt tábla neve. · A lekérdezésben részt vevő oszlopok szöveges tömbként

ábrázolva. Ha null-t adunk meg, valamennyi benne lesz az eredményben.

· Feltétel, hasonlóan az előzőekhez. Ha paramétereket is kívánunk benne megadni, azok helyét itt is a „?” jelöli. Null megadásakor valamennyi sor részt vesz a lekérdezésben.

· Feltétel megadása esetében szöveges paramétertömb, annak hiányában null.

· Csoportosító szűrő: az SQL GROUP BY utáni része, szövegesen. · HAVING szűrő szövegesen formában. · Sorba rendező szűrő (ORDER BY). · LIMIT szűrő: a lekérdezés sorainak számát korlátozó beállítás.

Az utolsó négy paraméter is opcionális, hiányukat null jelöli. Összetett, nem egyetlen táblát használó lekérdezésekhez használhatjuk a rawQuery() metódust is. Az eredményül szolgáló kurzor az android.database.Cursor osztályból származik. Néhány hasznos metódusát nem árt megismerni:

· getCount(): a kurzorban lévő sorok száma. · getPosition(): a kurzor jelenlegi sorpozíciója. · getColumnCount(): a kurzorban lévő oszlopok száma. · getColumnNames(): szöveges tömbbe kerül az összes oszlop

neve. · isLast(): visszaadja, hogy a kurzor az utolsó soron áll-e. · isAfterLast(): a kurzor az utolsó sort is feldolgozta? · moveToFirst(): az első sorra viszi a kurzort. · moveToNext(): a következő sorra lépteti a kurzort. · getType(): a paraméterként megadott sorszámú oszlop típusát

szövegesen visszaadja. · getInt(), getFloat(), getString(), stb: a paraméterükben

megadott számú oszlopban lévő aktuális értéket adja vissza a metódus nevében megadott típusként.

A kurzor által használt terület felszabadítását a close() metódus biztosítja.

Page 21: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

20

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

A következő kis programrészlet kiíratja a LogCat naplójára azoknak az ismerősöknek az ID-jét és nevét, akiknek az azonosítója 1-nél nagyobb. // A lekérdezéshez szükséges feltétel eredménye Cursor kurzor = adatbazis.query(TABLA_NEVE, null, "id>?", new String[]{"1"}, null, null, null); //A kurzort az első sorra állítjuk a biztonság kedvéért. kurzor.moveToFirst(); //Ciklussal a kurzor valamennyi sorát feldolgozzuk. do {

//Kitesszük a LogCat naplójába a lekérdezés eredményét. //Feltételezzük az adott számú és típusú adatokat //tartalmazó oszlopokat. Log.d("ismerosok", "ID: " + kurzor.getInt(0) + ", nev: " + kurzor.getString(1)); //Ha kész, a kurzor menjen tovább. kurzor.moveToNext(); //Túl vagyunk már az utolsó soron?

}while(!kurzor.isAfterLast()); //Ne felejtsük el bezárni. kurzor.close(); Egyéb adatbázis műveletek futtatásához (ALTER, DROP) használhatjuk a már korábban megismert execSQL() metódust! Adatbázis-műveleteink eredményét a fejezet elején tárgyalt sqlite3 segédprogrammal is leellenőrizhetjük. 3.3 Segédosztályok

Az adatbázisok verziókezelt menedzseléséhez, létrehozásához, frissítéséhez használhatjuk a Google által is ajánlott SQLiteOpenHelper osztályt. Ebből származtatva olyan saját osztályt hozhatunk létre, ami referenciaként szolgálhat a későbbiekben a teljes adatbázisnak. Konstruálása: SQLiteOpenHelper(Context kontextus, String név, SQLiteDatabase.CursorFactory kurzortípus, int verzió); A konstruktor paraméterei:

· Kontextus: az alkalmazás környezete. · Név: az adatbázisfájl neve, null esetén memória-adatbázist

épít.

Page 22: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

21

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

· Kurzortípus: saját kurzorimplementáció megadása, alapesetben pedig null.

· Verzió: folyamatosan növekvő verziószámmal kell az adatbázis újabb változatatait ellátni.

A helper osztályunk létrehozásakor kötelezően felülírandó (absztrakt) osztályok az onCreate() és az onUpgrade(). Mindkét metódus void típusú. Az onCreate() az adatbázis első létrehozásának alkalmával használandó. Itt érdemes megcsinálni azt az utasítássorozatot, ami a kívánt adatbázis-sémát létrehozza. Az onUpdate() metódus mondja meg, mi történjen az adatbázissal egy adott korábbi verzióról egy adott új verzióra történő átállás esetén. Amennyiben semmiképp nem tudjuk a régi verzió adatbázis-sémáját az újjal összeegyeztetni, ezen a ponton akár a régi adatbázis-séma el is dobható, az új létrehozása előtt. Ebben az esetben természetesen a frissítés gyakorlatilag a régi adatok elvesztését is jelenti egyúttal. További hasznos metódusok még:

· getWritableDatabase(): Létrehoz és/vagy megnyit egy adatbázist írásra és olvasásra. A megnyitást követően az adatbázis gyorsítótárazva lesz. Hiányzó jogosultságok, vagy telítődött tároló a hívás meghiúsulását okozhatják. A használatot követően a close() metódussal kell lezárni az adatbázist.

· getReadableDatabase(): Hasonlít az előzőhöz, az előbbiekben említett problémák esetében az adatbázis általában még olvasásra megnyitható.

A sémával kapcsolatos megjegyzés: noha egy táblának nem szükségszerűen kell egyedi azonosítót tartalmazni, de ilyen létrehozása nemcsak ajánlott, de célszerű azt „_id” néven hivatkozni. Ennek a magyarázata az, hogy a későbbiekben amennyiben ListView-val kívánjuk összekötni az adatokat, az oszlop között kell ilyen nevűnek lennie5. Azokat a változókat, amelyek az adatbázis metainformációit tárolják, úgymint verziószám, adatbázis neve és mások, célszerű statikus tagváltozókba helyezni.

5 Android Developers - Content Provider Basics - http://developer.android.com/guide/topics/providers/content-provider-basics.html#DisplayResults

Page 23: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

22

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

4. Hálózati kommunikáció

Napjaink mobil eszközei széles körű támogatást nyújtanak az internet eléréséhez mobil hálózatok, illetve a beépített Wifi interfészek segítségével is. Ezekhez az Android operációs rendszer is nagyon gazdag API támogatást nyújt. 4.1. Mobil hálózatok elérése

A mobil hálózatokon keresztül történő adatforgalom kezdeti nehézségeit a 3G és 4G technológia mára már kiküszöbölni látszik, és egyre több eszköz használja az internet elérésére a szolgáltatók mobil hálózatait. A mobil hálózati kommunikáció elérésének egyik alapvető eszköze az Android rendszerben a ConnectivityManager6 osztály. Ennek segítségével információkat kérdezhetünk le a hálózati kapcsolatokról, vagy azok változásairól, és reagálhatunk a hálózati kapcsolatokban bekövetkező eseményekre. Használata az ACCESS_NETWORK_STATE engedélyezésével lehetséges. Legelőször is érdemes leellenőrizni a hálózati kapcsolatok állapotait, hogy eszközünk milyen aktív kapcsolatokkal rendelkezik. Ehhez a ConnectivityManager osztály getNetworkInfo() függvényét célszerű használni, aminek bemenő paraméterében meg kell adnunk, hogy a wifi, vagy a mobil kapcsolatot szeretnénk lekérdezni. A kapcsolat aktív voltáról az isConnected() függvénnyel bizonyosodhatunk meg, melynek visszatérési értéke igaz, ha a kapcsolat létezik. ConnectivityManager man=(ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE); //Mobil hálózat elérésének vizsgálata NetworkInfo mobilinfo = man.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); if(mobilinfo!= null && mobilinfo.isConnected()){ csatlakozva.setText(R.string.igen); }else{ csatlakozva.setText(R.string.nem); }

6 Android Developers - Managing Network Usage - http://developer.android.com/training/basics/network-ops/managing.html

Page 24: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

23

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

A kapcsolatban beálló változások esetén egy CONNECTIVITY_ACTION intent keletkezik, melyet a BroadcastReceiver osztály segítségével tudunk kezelni. 4.2. Wifi hálózatok elérése

A Wifi hálózatok elérését a WifiManager osztályon keresztül tudjuk megvalósítani. Segítségével megtekinthetőek a már konfigurált hálózatok valamint az aktív hálózatok, de információkat szerezhetünk az elérési pontokról, fel- vagy lecsatlakozhatunk ezen pontokra, illetve lekérdezhetjük az egyes elérési pontok jelerősségét is. Használatához szükségünk van az ACCESS_WIFI_STATE és a CHANGE_WIFI_STATE engedélyezésére. Az aktuálisan aktív Wifi kapcsolatunk állapotáról a getConnectionInfo() függvény segítségével kaphatunk információkat, míg a már beállított Wifi hálózatok listáját getConfiguredNetworks() függvénnyel kérdezhetjük le. ConnectivityManager man = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); NetworkInfo mWifi = man.getNetworkInfo(ConnectivityManager.TYPE_WIFI); //Wifi hálózat elérésének vizsgálata if (mWifi!=null && mWifi.isConnected()) { WifiManager wman=(WifiManager)

this.getSystemService(Context.WIFI_SERVICE); WifiInfo winfo=wman.getConnectionInfo(); StringBuffer s=new StringBuffer(); //A már konfigurált Wifi hálózatok lekérése, SSID-jük kiiratása List<WifiConfiguration> conf=wman.getConfiguredNetworks(); for(WifiConfiguration c: conf){ s.append(c.SSID+" "); } mentett.setText(s.toString()); }

4.3. Wifi-Direct

A Wifi-direct az Android 4.0-ás (API 14) verziójától érhető el, és lehetővé teszi, hogy két eszköz Wifi hálózaton közvetlenül kommunikáljon Wifi elérési pont közbeiktatása nélkül. Így a Bluetooth hatótávolságánál jóval nagyobb távolságból is képes lehet két arra alkalmas eszköz kommunikálni egymással.

Page 25: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

24

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

Használatához alkalmazásunkat el kell látni a CHANGE_WIFI_STATE, ACCESS_WIFI_STATE, és INTERNET engedélyekkel a manifest állományban. A Wifi-directet a WifiP2pManager osztály egy példányán keresztül érhetjük el. Ennek az osztálynak az initialize() metódusával hozhatjuk létre a WifiP2pManager.Channel osztályba tartozó csatornát, melyen keresztül majd a kommunikáció folyik a két eszköz között. Az elérhető Wifi-direct eszközöket, a discoverPeers() metódus meghívásával térképezhetjük fel. A megtalált eszközhöz való csatlakozáshoz, létre kell hoznunk egy WifiP2pConfig osztálybeli elemet, melybe elhelyezzük az eszköz paramétereit. A kapcsolódást a WifiP2pManager.connect() metódusával tudjuk létrehozni, amely bemenő paramétereinél kell megadnunk a korábban létrehozott csatornát és a WifiP2pConfig osztálybeli elemet. A Wifi-direct kapcsolatok kialakítása közben többféle broadcast intent keletkezhet, mint például:

· WIFI_P2P_STATE_CHANGED_ACTION – Jelzi, hogy a Wi-Fi Peer-To-Peer kapcsolat aktív-e.

· WIFI_P2P_PEERS_CHANGED_ACTION – Jelzi, ha az aktuális elérhető elemek listája megváltozott.

· WIFI_P2P_CONNECTION_CHANGED_ACTION – Jelzi, ha a Wi-Fi P2P kapcsolat állapota megváltozott

· WIFI_P2P_THIS_DEVICE_CHANGED_ACTION – Jelzi, ha az eszköz konfigurációja megváltozik.

Ezeket az intent-eket a BroadcastReceiver osztály segítségével tudjunk kezelni. List<WifiP2pDevice> eszkozok = new ArrayList(); … TextView wd_eszkozok = (TextView) this.findViewById(R.id.textView_eszkozok_data); WifiP2pManager man = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE); Channel csatorna = man.initialize(this, getMainLooper(), null); Button gomb_keres= (Button) this.findViewById(R.id.button_keres); //WifiDirect eszközök keresése gombnyomásra gomb_keres.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //A discoverPeers() metódus hatására a PeerListener eseménykezelő fog végrehajtódni

Page 26: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

25

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

man.discoverPeers(csatorna, new WifiP2pManager.ActionListener() {

@Override public void onSuccess() { wd_eszkozok.setText(R.string.felderites_rendben); } @Override public void onFailure(int reason) { wd_eszkozok.setText(R.string.felderites_hiba); } }); } }); … PeerListListener PLlistener = new PeerListListener(){ @Override public void onPeersAvailable(WifiP2pDeviceList peers) { eszkozok.clear(); eszkozok.addAll(peers.getDeviceList()); … }

4.4. A HTTP és HTTPS kapcsolat

A legtöbb hálózati kapcsolattal rendelkező Androidos alkalmazás a HTTP protokollt használja adatok küldésére és fogadására, ezért érdemes ezzel egy kicsit jobban megismerkedni. Az Android tartalmazza mind a Java HTTP API-jait, mind pedig az Apache fejlesztésében megalkotott API-t is. Ezáltal a HTTP kapcsolatok széles körben támogatott elemei az Androidos rendszereknek, mely az alapvető HTTP GET és POST üzenettípusoktól kiindulva a streaming-es le- és feltöltésen keresztül egészen a HTTPS titkosított kapcsolatokig terjed. A fejlesztők a Gingerbread, azaz a 2.3-as Android verziójától a HttpURLConnection osztály használatát javasolják szemben az Apache HttpClient osztályával7. A HTTP kapcsolat alapfeltétele, hogy alkalmazásunknak rendelkeznie kell az INTERNET engedéllyel. Ennek hiányában az operációs rendszer nem fogja engedélyezni az adatforgalmat. Célszerű az ilyen jellegű hálózati kommunikációt mindig külön szálban futtatni, hogy a lassú le vagy feltöltés ne blokkolja a felhasználói felületünket. Szintén érdemes minden esetben fokozott figyelmet 7 Android Developers – Connecting to the Network - http://developer.android.com/training/basics/network-ops/connecting.html

Page 27: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

26

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

fordítani a beérkezett válaszok HTTP státuszkódjára, amely segítségével mindig ellenőrizni tudjuk a tranzakció sikerességét. A 200-as státuszkód jelzi a sikeres műveletet. A hálózati kommunikációt megkezdése előtt pedig minden esetben ellenőrizzük, hogy egyáltalán elérhető-e a hálózat, amit a getActiveNetworkInfo() és isConnected() függvényekkel tehetünk meg. A kapcsolat létrehozásához a HttpURLConnection osztály egy elemét kell létrehoznunk, majd beállítanunk a kapcsolatra vonatkozó jellemzőket, mint például az üzenet típusát a setRequestMethod() metódussal. A kapcsolatot a connect() metódussal hozhatjuk létre, aminek létrejöttét a státuszkód lekérő getResponseCode() metódussal ellenőrizhetünk. Ezután már csak egy csatornát kell „nyitnunk” a getInputStream() metódussal és kezdhetjük is a letöltést a távoli gépről. try{ URL url = new URL("URL címe"); HttpURLConnection httpcon =(HttpURLConnection)

url.openConnection(); InputStream be=null; HttpURLConnection httpcon=null; httpcon.setConnectTimeout(1000); httpcon.setReadTimeout(1500); httpcon.setRequestMethod("GET"); httpcon.connect(); be=httpcon.getInputStream(); }catch(Exception ex){…}

Az InputStream csatorna egy olvasható bájt folyamot ad át nekünk. Az olvasott adatokat a felhasználásuk célja szerint érdemes konvertálni vagy dekódolni a megfelelő formátumra. Ha például egy képet szeretnénk letölteni, akkor azt a BitmapFactory osztály decodeStream() metódusával alakíthatjuk Bitmap osztálybeli képformátumra. httpcon.connect(); be=httpcon.getInputStream(); Bitmap letoltottkep=BitmapFactory.decodeStream(be);

Ha pedig szöveges állományt szeretnénk letölteni, akkor azt a következő módon tehetjük meg:

Page 28: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

27

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

httpcon.connect(); int meret=httpcon.getContentLength(); be=httpcon.getInputStream(); byte[] buffer = new byte[meret]; be.read(buffer);

Előfordulhat, hogy az URL-ben paramétert is szeretnénk átadni. Hogy elkerüljük a hibás paraméterek átadását, célszerű az URLEncoder osztály encode() metódusát használni az átadandó szövegre, amely az URL paraméterátadásnál megszokott MIME formátumra alakítja az eredeti szöveget. A HTTPS protokoll lehetővé teszi számunkra, hogy biztonságos kapcsolatot alakíthassunk ki két számítógép között. Ennek a protokollnak a használatával a kommunikációnk titkosított lesz, így az ilyen kapcsolatoknak nagy hasznát vehetjük, ha olyan alkalmazásokat szeretnénk írni - például banki alkalmazások -, ahol nem szeretnénk, ha a kommunikációnkat lehallgatnák és visszafejtenék. Használatához először generálnunk kell egy titkosított aláírást, amivel el kell látnunk alkalmazásunkat, majd ezt be kell állítanunk a szerver oldalon is. A HTTPS kapcsolat létrehozása nagyon hasonlít a HTTP kapcsolat létrehozásához, csak itt a HttpsURLConnection osztályt kell használnunk. 4.4. XML adatok feldolgozása

A hálózati kommunikáció során az alkalmazások valamilyen szabvány formát használnak arra, hogy adataikat eljuttassák a másik félhez. Ezek közül is a leggyakrabban használt formátum az XML. Ennek segítségével a jól olvasható adatokat strukturált rendszerben tudjuk kezelni. Az Android kétféle feldolgozási módot is rendelkezésünkre bocsát az XML állományok feldolgozásához. Az első a SAX (Simple API for XML), melynek már a SAX2 verziója a használatos. Segítségével az XML állományunkat folyamatosan, a betöltés folyamán dolgozhatjuk fel az alkalmazással, és a megadott részekhez érve eseményeket válthatunk ki. A másik feldolgozási módszer már a DOM-ra (Document Object Model) alapul. Ebben az esetben a teljes XML állomány betöltésre kerül, majd a DOM által meghatározott struktúrából kérdezhetőek le a szükséges elemek. Ennek megvalósítása a DocumentBuilder és a DocumentBuilderFactory osztályokon keresztül van lehetőségünk. Ezek felhasználásával létrehozhatunk egy Document osztályba tartozó példányt, mely az XML állományunkat reprezentálja. Ennek az osztálynak a getElementsByTagName() metódusával tudjuk kikeresni az állományból a megadott nevű elemet.

Page 29: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

28

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

//A koponyeg.hu RSS szolgáltatása által nyújtott XML állomány

feldolgozása DocumentBuilderFactory

fact=DocumentBuilderFactory.newInstance(); DocumentBuilder db=fact.newDocumentBuilder(); Document doc=db.parse(new InputSource(url.openStream())); doc.getDocumentElement().normalize(); StringBuffer seged=new StringBuffer(); //Az XML elem elérése NodeList

hely=doc.getElementsByTagName("koponyeg:elhelyezkedes"); Element v=(Element)hely.item(0); seged.append("Régió: "+v.getAttribute("regio")+"\n");

4.5. Külön szálon futó hálózati műveletek

Ha alkalmazásunk állandóan megszakítja az interaktivitását a felhasználóval a hálózati kommunikációk kényszerszünetei miatt, melyek blokkolhatják a felhasználói felületet, az komoly felhasználói élményvesztést jelent. Ennek elkerülésére, mint azt már az előbbiekben említettük, a hálózati kommunikációval járó műveleteket célszerű külön szálakon futtatni. Itt azonban felmerül az a probléma, hogy amikor a másik végrehajtási szálon a hálózati kommunikáció eredményeként beérkezett adatokat szeretnénk megjeleníteni a felhasználói felületen, azt nem engedi meg számunkra a rendszer. A probléma megoldására többféle megoldás is létezik:

· Activity.runOnUiThread(Runnable): Ennek használatakor a függvény Runnable objektuma által végrehajtandó folyamat hozzá tud férni a felhasználói felülethez.

· View.post(Runnable): Aszinkron módon frissíthetjük az adott nézet elem tartalmát.

· View.postDelayed(Runnable, long): ugyanaz,mint az előző, csak itt megadhatjuk a frissítés késleltetési idejét is a második paraméterben.

· Message-Handler mechanizmus: Itt üzenetek formájában tud információkat átadni a háttérben futó szál a főszálnak.

· AsyncTask: A legegyszerűbb módja, hogy újmunkafolyamatot indítsunk a felhasználó felületről. Ezt fogjuk mi is megvizsgálni közelebbről.

Az AsyncTask használatakor első lépésben létre kell hoznunk egy saját osztályt, mely az AsyncTask absztrakt osztályból fog öröklődni. Ebben felül kell definiálnunk a doinBackground() metódust, megadva itt, hogy mit szeretnénk a háttérben futó szálon végrehajtatni.

Page 30: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

29

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

Amikor egy aszinkron szál végrehajtódik, akkor négy lépésen megy keresztül:

· Először a felhasználói felület meghívja az onPreExecute() metódus, amellyel inicializálhatjuk a folyamatunkat, és amely a főszálban fut le.

· Ezt követi a doInBackground() metódus végrehajtása, amiben a tényleges feladat zajlódik, amelyet végre kell hajtani a háttérben.

· A doInBackground() futása alatt a publishProgress() metódus meghívásával a fő szálban futó onProgressUpdate() metódussal tudunk információt küldeni a felhasználói felület felé.

· Végső lépésként az onPostExecute() metódus fut le a főszálban, amely segítségével a háttérszál futási eredményét tudjuk átadni, mint paraméter.

Az AsyncTask osztály azon kívül, hogy absztrakt, generikus osztály is egyben. Ez azt jelenti, hogy az öröklődéskor meg lehet adni neki a hívási paraméter, a végrehajtás közben visszaadott érték és az eredmény típusát. Ha valamelyiket nem akarjuk megadni, ott a Void értéket kell átadni. Gyakran felmerülő problémát jelenthet az AsyncTask használatakor az, hogy ha a hívó Activity valamilyen oknál fogva háttérbe kerül, mielőtt még a háttérben futó szál befejeződne, akkor az Activity felhasználói felülete már nem lesz elérhető. Ilyen gyakori eset a képernyő elfordítása. Ennek kezeléséről a programozónak kell gondoskodnia. private class Adatletolto extends AsyncTask<String, Void,

String>{ @Override protected String doInBackground(String... arg0) { … } protected void onPostExecute(String res){ … } protected void onProgressUpdate(Void... a){ … }

Page 31: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

30

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

4.6. WebView osztály

Hasznos eszköz lehet a weboldalak megjelenítésére - a View osztály leszármazottja - a WebView8 osztály, amely kizárólag egy weboldal tartamát képes megjeleníteni. Megjelenését tekintve hasonlóan működik, mint egy webböngésző, de nem található rajta sem navigációs vezérlők, sem címsor, viszont mind a CSS, mind a JavaScript kezelésére alkalmas. Használatához szükséges alkalmazásunknak az INTERNET engedéllyel rendelkeznie. Ha ilyen elemet szeretnénk alkalmazni, akkor nincs más teendőnk, mint megadni azt az Activity layout-jában, pl. a következő módon: <WebView android:id="@+id/webView_bongeszo" android:layout_width="match_parent" android:layout_height="fill_parent" android:layout_alignLeft="@+id/editText_cim" android:layout_below="@+id/progressBar_letoltes" android:layout_marginTop="16dp" />

A forráskódban a loadURL() függvény segítségével adhatjuk meg, hogy milyen URL-ről töltse be a weboldalt az WebView elembe az alkalmazásunk. A WebSettings objektum segítségével szabályozhatjuk a WebView elemünk megjelenését, melyet a WebView getSettings() metódusával kérhetjük le. Ennek segítségével szabályozhatjuk az oldal megjelenését, megadhatjuk a betűtípusokat, az oldal nagyítását, a JavaScript engedélyezését és még sok minden mást is. WebView bongeszo=(WebView)

this.findViewById(R.id.webView_bongeszo); bongeszo.setInitialScale(50); WebSettings ws=bongeszo.getSettings(); ws.setJavaScriptEnabled(true); ws.setBuiltInZoomControls(true);

Fontos ismételten megjegyeznünk, hogy csak és kizárólag egy weboldal megjelenítésére szolgál a WebView elem, és nem úgy működik, mint egy böngésző program. Ezt akkor érzékeljük csak igazán, ha például a megjelenített weboldal egy linkjére klikkelünk. Ennek hatására ugyanis nem a WebView elemben jelenik meg a

8 Android Developers - Building Web Apps in WebView - http://developer.android.com/guide/webapps/webview.html

Page 32: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

31

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

linkhez tartozó oldal, hanem meghívásra kerül a rendszer alapértelmezett webböngésző programja, és abban jelenik meg a kérdéses oldal. Bár a WebView nem úgy működik, mint egy böngésző, de számos eszköz áll rendelkezésünkre, hogy hasonló működésre bírjuk. Az előbbi példa esetében egy esemény váltódik ki, ami az új URL megnyitását eredményezi, és ez az alapértelmezett webböngészőt hívja segítségül. Ezt, és még sok más eseményt is, lehetőségünk van kezelni a WebView objektumhoz tartozó WebViewClient objektum segítségével, melyben felülírhatjuk az alapértelmezett viselkedést. Például az új URL megnyitásánál a shouldOverrideUrlLoading() metódust kell átírnunk, ha a WebView elemen belül szeretnénk megnyitni az új oldalt is. Ezen felül a WebView támogatja az előre hátra navigálást is, melyeket a WebView goForward() és goBack() függvényeivel valósíthatunk meg. További lehetőség, hogy a WebChromeClient objektum segítségével a JavaScript események egy részét is kezelni tudjuk. Felüldefiniálhatjuk többek közt az onJSAlert() függvényt, amely JavaScript figyelmeztető ablakot jelenít meg, vagy az onProgressChanged() függvényt, amely az oldal letöltöttségi állapotát adja meg százalékban. ProgressBar letoltes=(ProgressBar)

this.findViewById(R.id.progressBar_letoltes); … //A letöltöttségnek megfelelően állítjuk be a progressbar-t bongeszo.setWebChromeClient(new WebChromeClient() { public void onProgressChanged(WebView view, int

progress){ letoltes.setProgress(progress); } });

4.7. TCP/IP alapú kommunikáció

A hálózaton található számítógépeken futó szolgáltatásokat nem csak a HTTP protokollon keresztül érhetjük el, hanem direkten a TCP/IP protokollon keresztül is. Ebben az esetben ismernünk kell a célszámítógép IP címét és a szolgáltatásához tartozó port azonosítóját. Ezek ismeretében már kialakíthatunk úgynevezett Socket-alapú kommunikációt is. Ebben az esetben először is létre kell hoznunk a Socket osztály egy példányát, amelynél meg kell adnunk a cél számítógép IP címét és a szolgáltatás port számát. Innentől kezdve a Socket osztály

Page 33: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

32

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

getInputStream() és getOutputStream() metódusaival tudunk olvasási és írási csatornát nyitni a célgéphez. Socket s=null; … try{ s=new Socket("www.focusautosiskola.hu",80); Log.d("SAJAT", ""+s.getInetAddress()); s.setSoTimeout(500000); ki=s.getOutputStream(); ki.write("GET index.html HTTP/1.1\n".getBytes()); ki.flush(); … }catch(Exception ex){ // Hibakezelés }

4.8 Adatforgalom figyelése

A hálózati kommunikációt használó alkalmazásoknál gyakran merül fel a kérdés, hogy vajon mikor, és milyen mértékben használja az alkalmazás a hálózatot, és mennyi adatfogalmat bonyolít rajta. Az ilyen és ehhez hasonló kérdések megválaszolására használhatjuk az Android TrafficStat osztályát. A TrafficStatt osztály segítségével statisztikát kaphatunk az összes, küldött vagy fogadott bájtok mennyiségéről, amit lebonthatunk akár csak a mobil hálózati kommunikációra, vagy akár a processzek szintjéig is. Az osztály az adatokat azonban csak a szükséges kommunikációs interfész utolsó bekapcsolástól kezdve tárolja, így annak kikapcsolásakor az adatok nullázódnak. A TrafficStatt osztály getMobileRxBytes() és getMobileTxBytes() metódusaival lehet lekérdezni a mobil interfészen keresztül fogadott, vagy küldött bájtok számát, míg a getTotalRxBytes() és getTotalTxBytes() metódusai az összes hálózati interfészről kimenő és bejövő bájtok számát adja vissza. int le = TrafficStats.getTotalRxBytes()/1024; osszle.setText(Long.toString(le)+" kb"); int fel = TrafficStats.getTotalTxBytes()/1024; osszfel.setText(Long.toString(fel)+" kb");

Page 34: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

33

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

5. Helymeghatározás

Androidos készülékünk a mobiltelefon és a Wifi modulok mellett még számos szenzorral, kiegészítő eszközzel rendelkezhet. Ilyen jellemző eszköz lehet egy gyorsulásmérő, giroszkóp, közelségérzékelő szenzor, egy, vagy több kamera, de találhatunk érzékelőket hőmérséklet, páratartalom, vagy műholdas pozíció meghatározására is mobilunkban, táblagépünkben. Ezek programból történő felhasználása számos területet, szakmát megcélozhat: használhatjuk ezek segítségével eszközünket navigációs berendezésként, fényképezőgépként, vagy edzéstámogató berendezésként. Ebben a fejezetben a helymeghatározást segítő megoldásokkal foglalkozunk. 5.1 Helymeghatározást támogató eszközök

A földrajzi pozíció meghatározása a mobil eszközök egyik népszerű képessége, hiszen a segítségükkel sokkal személyesebbé, közvetlenebbé, vagy hasznosabbá tehetjük alkalmazásunkat. Lehetővé válik, hogy az időjárási adatokat (vagy éppen a reklámokat) aktuális helyzetünktől függően kaphassuk meg, megoszthassuk barátainkkal friss felfedezéseinket, vagy éppen nyomon kövessük elvesztett készülékünket. Attól függően, hogy milyen szenzorokkal rendelkezünk, hol vagyunk (zárt helyiségben, vagy szabadban), rendelkezünk-e internetes kapcsolattal, illetve milyen pontosan, mennyi energiafogyasztás mellett kívánjuk meghatározni a készülék helyét, több megoldáshoz is folyamodhatunk. A leggyakoribb helymeghatározási lehetőségek jelenleg a következők:

· Wifi Access Point alapján (WPS, Wifi Based Positioning System) · Mobilhálózat cellainformációinak a segítségével · Műholdas (GPS/Glonass) helymeghatározással · Kevert módszer (A-GPS): adathálózaton keresztül támogató

szerverek segítenek értelmezni a GPS jelet gyenge vételi viszonyok esetében.

5.2 Beállítások az Android operációs rendszerben

A helymeghatározással összefüggésben a felhasználók maguk is rendelkezhetnek arról, hogy milyen geolokációs szolgáltatásokat engednek meg, illetve kapcsolnak be készülékükön. A Settings/Location services (Beállítások/Helymeghatározás) menüpontban a következő lehetőségek adottak:

Page 35: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

34

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

6. ábra Location services menü

Az első menüpont fogja engedélyezni az alkalmazásaink számára a Wifi és mobilhálózat használatát helymeghatározás céljából a Location szerviz segítségével. A második menüponttal a GPS-t is bekapcsolhatjuk/használhatjuk ugyanerre a célra. A harmadik menüponttal a Google igyekszik testre szabni szolgáltatásait a helyinformációk továbbításának engedélyezésével. 5.3 Helymeghatározás Wifi AP-k segítségével

A módszer lényege az, hogy ha ismerjük a csatlakozásra használt hozzáférési pont kártyacímét (MAC address), esetleg hálózati azonosítóját (SSID), az AP helyzetét is ismerve közelítőleg meghatározhatjuk helyzetünket a hozzáférési pont hatósugarának függvényében. Ehhez az információhoz persze - durván egyszerűsítve koncentrikus körök formájában - a használt AP jelerőssége is hozzájárulhat.

Page 36: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

35

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

Természetesen a haszna csak akkor mutatkozik meg, ha az adott Wifi pontról vannak előzetes információink, például egy saját, vagy egy osztott adatbázis formájában. Utóbbira példákat közösségi alapon szerveződve és természetesen cég által szolgáltatva is egyaránt láthatunk. Előbbihez tartozik több olyan Android program, amik ingyenes Wifi hálózatokat földrajzi alapon is nyilvántartanak (Free Zone, Wifi Free és társai), vagy a Wifi és mobilhálózati adatokat egyaránt nyilvántartó openBmap. Céges szolgáltatásra lehet példa az egyik legnagyobb adatbázis, a Google Maps Geolocation API. Sajnos ennek használata egyrészt regisztrációhoz kötött, másrészt fizetős is. A jelenlegi gyakorlat alapján ingyenesen napi 100 lekérést enged meg a rendszerük9. Példa a Geolocation API használatára: wifiAccessPoints Az API használatához egy POST típusú kérés szükséges a következő címre: https://www.googleapis.com/geolocation/v1/geolocate?key=API_KEY

Mint látható az URL-be be kell fűzni a regisztráció során kapott kulcsot is. A kérés törzsét JSON formátumban kell leírni a következők szerint: { "wifiAccessPoints": [

{ "macAddress": "01:23:45:67:89:AB", "signalStrength": -61, "age": 0, "channel": 6, "signalToNoiseRatio": 35 }

] }

A fentiek közül a macAddress a Wifi AP kártyacíme és egyúttal az egyetlen kötelező mezője a kérésnek. A többi mező (jelerősség, kapcsolódás életkora ezredmásodpercben, a csatorna száma és a jel/zaj arány) opcionálisan írható a kérésbe.

9 Google Developers –The Google Maps Geolocation API - https://developers.google.com/maps/documentation/business/geolocation/

Page 37: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

36

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

Sikeresen elküldött érvényes kérés esetén a válasz a következő formátumban érkezhet: { "location": { "lat": 47.95, "lng": 21.72 }, "accuracy": 210.2 }

A válasz a szélességi és hosszúsági fokokat, valamint a méterben megadott közelítő pontosságot fogja tartalmazni (vagy 404 hibakódot, ha az adott AP nem szerepel az adatbázisban). 5.4 Mobil cellainformációk a helymeghatározás szolgálatában

A mobilhálózatban az eszköz által látott mobilcellák és a jelerősség ismeretében is meghatározható a becsült pozíció. Erre – hasonlóan a Wifi-s helymeghatározáshoz – használhatunk API hívást, vagy csupán az aktuális cella adatainak az ismeretében kerestethetünk adatbázisban is. Nézzük meg a Geolocation API használatát cellainformáció alapján történő pozíció-meghatározás lekérdezésére: A lekérdezés az előzőekkel megegyező, POST típusú, az URL is ugyanaz. A lekérdezés törzse: { "homeMobileCountryCode": 216, "homeMobileNetworkCode": 70, "radioType": "gsm", "carrier": "vodafone HU", "cellTowers": [ { 'cellId': 2082249, 'locationAreaCode': 236, 'mobileCountryCode': 216, 'mobileNetworkCode': 70, 'age': 0, 'signalStrength': -62, 'timingAdvance': 14 } ] }

Page 38: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

37

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

A mezők jelentései:

· homeMobileCountryCode: az eszköz saját hálózatának ország kódja (MCC)

· homeMobileNetworkCode: a saját hálózat azonosítója (MNC) · radiotype: Európai hálózat esetében „gsm” · carrier: a szolgáltató neve · cellTowers: az elérhető cellák információi · cellId: a cella egyedi azonosítója (CID) · locationAreaCode: a használt hálózat területkódja (LAC) · mobileCountryCode: a használt hálózat országkódja (MCC) · mobileNetworkCode: a használt hálózat azonosítója (MNC) · age: a kapcsolódás életkora, ezredmásodpercben, opcionális

mező · signalStrength: a jelerősség dBm-ben, opcionális mező · timingAdvance: interferenciák elkerülésére használt időeltolás,

opcionális mező A sikeres kérésre adott válaszok is hasonlóak lesznek, mint a Wifi részben leírtak. A fenti kérés sikeres elküldéséhez természetesen szükségünk van a fenti információkra. A jegyzet korábbi fejezetében már szó esett arról, hogyan tudjuk lekérdezni a készülékben található mobil kommunikációs (GSM) eszköz adatait. Ismét a TelephonyManager referenciájára lesz szükségünk, hogy a lekérdezéshez szükséges adatokat megkapjuk. Az aktuális hálózat MCC és MNC adatait a getNetworkOperator() metódustól kaphatjuk meg szöveges formában, oly módon, hogy az első három karakter az országkódot, az utolsó kettő pedig a használt hálózat kódját határozza meg. A SIM kártya saját hasonló adatait (SIM MCC+MNC) a getSimOperator() fogja ugyanilyen formában visszaadni. A szolgáltató nevét megkaphatjuk a getNetworkOperatorName() metódustól String formában. A cellainformációkhoz az aktuális TelephonyManager referenciánk getCellLocation metódusa fog hozzájuttatni. Ennek a visszatérési értéke GsmCellLocation típus lesz. Ezen osztály példányát faggathatjuk a cellId-ról és a LAC (Location Area Code) adatairól rendre a getCid() és a getLac() metódusok segítségével. Az alkalmazás sikeres futtatásához szükséges az AndroidManifest fájl jogosultságainak bővítése is:

Page 39: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

38

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

<uses-permission android:name= "android.permission.ACCESS_COARSE_LOCATION"/>

A lekérdezést például a következő kódrészlet végezheti el: TextView tv1 = (TextView) findViewById(R.id.textView1); TextView tv2 = (TextView) findViewById(R.id.textView2); TextView tv3 = (TextView) findViewById(R.id.textView3); TextView tv4 = (TextView) findViewById(R.id.textView4); TextView tv5 = (TextView) findViewById(R.id.textView5); TextView tv6 = (TextView) findViewById(R.id.textView6); TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); String networkOperator = tm.getNetworkOperator(); if (networkOperator != null) {

String mcc = "mcc: " + Integer.parseInt(networkOperator.substring(0, 3));

String mnc = "mnc: " + Integer.parseInt(networkOperator.substring(3));

String operator = "operator: " + tm.getNetworkOperatorName();

String sim = "sim mcc+mnc: " + tm.getSimOperator(); GsmCellLocation celloc = (GsmCellLocation) tm.getCellLocation(); String cellid = "cellid: " + celloc.getCid(); String lac = "lac: " + celloc.getLac(); tv1.setText(cellid); tv2.setText(lac); tv3.setText(mcc); tv4.setText(mnc); tv5.setText(operator); tv6.setText(sim); }

A futtatás lehetséges eredményét a következő ábra szemlélteti:

Page 40: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

39

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

7. ábra Hálózati és cellainformációk

5.5 Az Android platform geolokációs szolgáltatásai

A platform több API csomagot is biztosít annak érdekében, hogy programjainkban kényelmesen meghatározhassuk aktuális pozíciónkat. Lehetőség van gyorsabb, de pontatlan helymeghatározásra, mint amilyeneket a Wifi és a GSM hálózat adatai alapján szerezhetünk, illetve mód nyílik a pontosabb, ám kissé lassabban hozzáférhető műholdas pozíció lekérdezésére is. A programok, amelyek ezeket igénybe veszik, ennek megfelelően kétféle jogosultságok is igényelhetnek a futtatásukhoz ezzel kapcsolatban: A már eddig is használt ACCESS_COARSE_LOCATION jogosultság lehetővé teszi a közelítő pontosságú meghatározást kikapcsolt, vagy hiányzó műholdkapcsolat esetén, vagy zárt helyiségben. A precízebb, például GPS támogatással meghatározott pozícióhoz az ACCESS_FINE_LOCATION jogot kell a manifeszt állományunkhoz hozzáadni. A helymeghatározást végezhetjük az android.location csomagból, vagy használhatjuk a Google Play Services csomag Location Services szolgáltatását is. Utóbbi olyan új szolgáltatásokat is tartalmaz, mint például az aktivitás-figyelés, a hatékonyabb energia-felhasználás, vagy a Geofence, azaz földrajzi területek lefedése poligonokkal, terület alapú funkciók készítéséhez.

Page 41: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

40

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

A Google Play Services-hez azonban egyrészt külön SDK-t kell letölteni, másrészt pedig a használata a Google Maps Geolocation API-hoz hasonlóan korlátozottan ingyenes. Ezen korlátozások elkerülése érdekében nézzük meg az eredeti android.location csomagot részletesebben! A rendszer helymeghatározó szolgáltatásaihoz való hozzáférést biztosítva a LocationManager az egyik kulcsosztálya a csomagnak. Ezen szolgáltatások segítségével lehetővé válik az eszköz pozíciójának menetrendszerű lekérdezése, vagy közelségi (proximity) értesítést kapjunk előre meghatározott földrajzi területekre való belépéskor. Az osztály - hasonlóan a TelephonyManager-hez - nem példányosítható közvetlenül, ezért ahhoz hasonlóan a getSystemService() használatát igényli: LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_ SERVICE);

Kezdjük a használatát azzal, hogy ellenőrizzük le, be van-e kapcsolva a nagy pontosságú helymeghatározást lehetővé tevő GPS modul! Ehhez vegyük igénybe az isProviderEnabled() metódust, aminek a kérdéses providert, azaz pozíció-meghatározó eszközt paraméterként megadva visszakapjuk annak állapotát! A szóba jöhető providerek listája:

· LocationManager.GPS_PROVIDER: értelemszerűen GPS modul · LocationManager.NETWORK_PROVIDER: hálózat alapú

meghatározáshoz · LocationManager.PASSIVE_PROVIDER: más alkalmazásoktól,

szolgáltatásokból eredő helymeghatározás. Ez is nagy pontosságú helymeghatározást igényel (ACCES_FINE_LOCATION), de nem garantálja azt.

Ha az adott provider nincs bekapcsolva, felajánlhatjuk a bekapcsolását a korábban bemutatott Settings/Location Services menüpontban. Ehhez a Settings.ACTION_LOCATION_SOURCE_SETTINGS intent-re van szükség. A pozíció megfigyelését a requestLocationUpdates() metódussal kezdhetjük el. A metódust névtúlterhelt, a programunkban használt változatnak a következő paraméterekre van szüksége:

· provider: a használt provider

Page 42: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

41

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

· minTime: az energiatakarékosság okán a lekérdezés minimum ennyi milliszekundumonként fog történni. Háttérben futó alkalmazásokhoz a Google min. 5 percet (5*60*1000) értéket ajánl, előtérben futókhoz természetesen kisebb intervallumok célszerűek.

· minDistance: a változtatáshoz nemcsak a minimum idő letelte, de az is szükséges, hogy az itt, méterben megadott pozícióváltozás is bekövetkezzen.

· listener: LocationListener interfészt megvalósító objektum. A LocationListener absztrakt függvényeinek a megvalósításával meghatározhatjuk, hogy mi történjen a megfigyelés közben bekövetkező eseményeknél. A megvalósítandó függvények a következők:

· onLocationChanged(Location): a felvett új pozíció · onProviderEnabled(Provider): a megadott provider-t

bekapcsolta a felhasználó · onProviderDisabled(Provider): a vizsgált provider-t kikapcsolta

a felhasználó · onStatusChanged(Provider, státusz, extrák): váratlanul

megváltozott a provider állapota. Az új állapotok lehetnek: o OUT_OF_SERVICE: a provider tartósan üzemképtelen o TEMPORARY_UNAVAILABLE: ideiglenes elérhetetlenség o AVAILABLE: újra elérhető a provider

Amennyiben az onStatusChanged() függvény visszaadja a pozíciónkat, a Location típusú objektum getLatitude() és getLongitude() függvényeivel lehet megkapni a szélességi és magassági koordinátáinkat. Amennyiben az adott provider nem adott sikeres koordinátákat vissza, célszerű másikat használni: ellenőrizzük le, hogy engedélyezve van-e például a NetworkProvider, és használjuk vele a getLastKnownLocation(Provider) metódust, ami szintén Location típusban adja vissza az utolsó ismert helyzetünket! A vezérlést megvalósító programrészlet: TextView tv1 = (TextView) findViewById(R.id.textView1); LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE); if (!lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) tv1.setText("A GPS modul nincs bekapcsolva!"); else lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,60000, 10, this);

Page 43: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

42

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

Mint látható a LocationListener-t a vezérlést megvalósító osztályban oldjuk meg az egyszerűség kedvéért, ezért az osztály deklarációját ne felejtsük el bővíteni a implements LocationListener-rel! Ugyanezen oknál fogva az osztályból ne hagyjuk ki a kötelező függvényeket sem! //Mi történjen, ha új pozíciót vett fel az eszköz? public void onLocationChanged(Location pozicio) { TextView tv1 = (TextView) findViewById(R.id.textView1); String szoveg = "Szélesség: " + pozicio.getLatitude() + ", hosszúság: " + pozicio.getLongitude(); tv1.setText(szoveg); } //A felhasználó kikapcsolta a GPS-t? public void onProviderDisabled(String provider) {} //A felhasználó bekapcsolta a GPS-t? public void onProviderEnabled(String provider) {} //Váratlanul változott meg a provider állapota? public void onStatusChanged(String provider, int status, Bundle extras) {} A program figyelmeztet a kikapcsolt GPS-re, illetve bekapcsolt állapotban kiírja a kapott pozíciót:

Page 44: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

43

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

6. Multimédiás eszközök

A mobileszközökön napjainkra mindennapossá váltak azok a multimédiás eszközök, amelyek kép, videó, vagy hang rögzítésére, lejátszására adnak lehetőséget. Bár a technológia fejlődésével ezek minősége folyamatosan javul (sőt a csúcskategóriás készülékek esetében már a „megapixel-háború” is lecsengeni látszik, lásd a HTC One-t10), jelenleg még ritkán haladja meg az általuk készített anyag egy kompakt céleszköz (pl. fényképezőgép) által biztosított minőséget. Ami viszont hallatlan előnyt biztosít az utóbbiakkal szemben nekik már most is, az a közvetlen, sokoldalú felhasználási lehetőség, amivel a rögzített anyag azonnal munkára fogható, közzé tehető, vagy beágyazható az alkalmazásainkba. Ezért nézzük át, hogyan használhatjuk fel hatékonyan ezeket mi is! 6.1 A kamera használata

Az Android framework számos kamerát, és kamerafunkciót támogat közvetlenül, lehetővé téve a gyors képrögzítést, vagy videó felvételét. Ezek sokoldalúsága miatt azonban van néhány szempont, amit érdemes mérlegelni azt követően, hogy eldöntöttük, szeretnénk használni ilyen funkciókat a programunkban. Szükségesség: milyen szerepet fog betölteni a kamerahasználat a programunkban? Lényeges-e, hogy csak olyan gépeken engedjük meg a futtatását, amelyek rendelkeznek kamerával? Ha olyan alkalmazást fejlesztünk, ahol a felhasználói profilba szeretnénk megengedni, hogy saját fotó is kerülhessen, az talán nem olyan fontos kameraszükséglet szempontjából, mintha képmegosztó program írását tervezzük. Amennyiben lényeges, úgy azt a jogosultságokban jeleznünk kell, lásd később. Hol tároljuk: szeretnénk-e közzétenni, vagy más alkalmazásokból elérhetővé tenni az elkészült fotókat, videókat, avagy megelégedünk azzal, hogy csak a saját programunkból érhetőek el ezek? Őrizzük-e meg őket azután is, ha esetleg a programunk már eltávolításra került? Gyári kameraprogram, vagy saját felület: megelégedünk-e azzal, hogy gyors képrögzítés céljából behívjuk az eszköz saját alkalmazását erre a célra, vagy szeretnénk testreszabott felületet is készíteni ehhez? Mindenesetre az Androidon létező intentek használatával az adatcsere kényelmesen megoldható a gyári program és a sajátunk között is.

10 TechRadar – HTC One Review -http://www.techradar.com/reviews/phones/mobile-phones/htc-one-1131862/review/7

Page 45: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

44

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

6.1.1 A rögzítéshez használható megoldások, jogosultságok

1) Az elsődleges célpontunk képrögzítésre az

android.hardware.Camera osztály lehet, amely a Camera szerviz klienseként vezérli a vonatkozó hardvert.

2) Mozgókép és hangzó anyag rögzítéséhez használható az android.media.MediaRecorder osztály is.

3) Amennyiben megelégedünk más alkalmazás behívásával, használhatunk intent-eket is: · A MediaStore.ACTION_IMAGE_CAPTURE kép, · A MediaStore.ACTION_VIDEO_CAPTURE pedig nyilván videó

rögzítésére ad lehetőséget, közvetlen hardverhasználat nélkül. 4) Ha pedig csupán előnézeti kép szükségeltetik, erre alkalmas lehet

az android.view.SurfaceView osztály is. Mivel sokrétűen rögzíthetünk képet, és megszabhatjuk azt is, hogy a kamerának mely funkcióira van szükségünk, az ehhez kötődő jogosultságokból is több van. Nézzük át röviden ezeket is! Amennyiben közvetlenül kívánjuk használni a kameramodult, szükségünk van a következő engedélyre alkalmazásunknak: <uses-permission android:name="android.permission.CAMERA" />

Abban az esetben, ha más alkalmazásból oldjuk meg a kérdést (intent), erre nincs szükségünk (pontosabban nem nekünk van rá szükség). Ha nem némafilmet kívánunk rögzíteni, videó esetében célszerű a hangrögzítés engedélyét is megkérni: <uses-permission android:name="android.permission.RECORD_AUDIO" /> Rögzítés esetében, ha az külső tárolóra, pl. SD kártyára történik, ne felejtkezzünk el írási jogot kérni rá! <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> Ha szeretnénk előírni a Google Play piacterén, hogy csak azokra a készülékekre lehessen letölteni az alkalmazást, amelyek rendelkeznek kamerával, a következő megkötést tehetjük:

Page 46: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

45

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

<uses-feature android:name="android.hardware.camera" />

Enyhébb előírás, amikor csak jelezzük, hogy a program képes kamerakezelésre, de nem szükséges a használathoz: <uses-feature android:name="android.hardware.camera" android:required="false" /> Lehetőség van speciális kameraképességek előírására is, mint amilyen a villanó, autófókusz, előlapi kamera. 6.1.2 Képrögzítés intent segítségével A leggyorsabb és legegyszerűbb módja a rögzítésnek, ha behívjuk az alapértelmezett programot a feladat elvégzésére. Ehhez készítsünk először egy Camera intent-et, az esemény típusának a megfelelőt (kép, vagy videó) megadva! Ezután indítsuk el az intent-ünket a startActivityForResult() metódus hívásával! Az indítást követően meg fog jelenni a képrögzítő alkalmazás, amivel megtörténhet az exponálás. Utolsó lépésként az általunk készített onActivityResult() metódus indul el, megkapva a kívánt adathalmazt intent-ben. A startActivityForResult() metódusnak paraméterbe magát az intent-et és egy azonosító kódot kell megadni, ami akkor lehet hasznos, ha több hívás esetében visszatéréskor tisztázni szeretnénk, hogy melyik eredményt dolgozzuk fel éppen. A képet a programban egy ImageView fogja megjeleníteni. Ehhez előtte egy Bitmap dekódolására van szükség az intent-ből kinyert adatstream-nek. A példaprogram kódja: public class MainActivity extends Activity { private Bitmap bitmap; private ImageView imageView; private static final int KERES_KODJA = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = (ImageView) findViewById(R.id.imageView1); Intent fotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

Page 47: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

46

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

startActivityForResult(fotoIntent, KERES_KODJA); } protected void onActivityResult(int keresKodja, int eredmenyKodja, Intent intentAdat) { if (keresKodja == KERES_KODJA && eredmenyKodja == Activity.RESULT_OK) try { //Kitakarítjuk a bitmap-et if (bitmap != null) { bitmap.recycle(); } //Készítünk egy InputStream-et, betöltjük az intent adatait. InputStream stream = getContentResolver().openInputStream(intentAdat.getData()); bitmap = BitmapFactory.decodeStream(stream); stream.close(); //A dekódolt stream-et átadjuk az ImageView-nak. imageView.setImageBitmap(bitmap); } //A kivételek nem csak hasznosak, de itt kötelezőek is... catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } A program futtatásának eredményeképp először betöltődik a készülék alapértelmezett kameraalkalmazása (8. kép), majd a mentést követően visszakapjuk a programunkban a mentett képet (9. kép).

Page 48: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

47

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

8. ábra Az intent behívja a kamera alkalmazást

9. ábra A kész képet visszaadja az elfordítás után

Page 49: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

48

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

6.1.3 Saját kamera alkalmazás készítése

Amennyiben akár a kinézet testre szabása, akár különleges effektusok használata érdekében szükséges ennél tovább menni az alkalmazásunkban, a megoldást egy saját kamerakezelő rutin jelentheti. Az elkészítésének általános menete: 1) A kamera detektálása, hozzáférés igénylése. 2) Egy előnézeti osztály legyártása az előnézeti kép számára. 3) Egy előnézeti layout készítése, amiben a kép megjelenik és

tartalmazza a szükséges vezérlőeszközöket. 4) Csatlakozás Listenerekhez, hogy a vezérlőeszközök elindítsák a

képrögzítést. 5) Mentés fájlba. 6) A kamera-erőforrás felszabadítása, hogy más is használhassa. A kamera detektálása API 9 előtt a következő metódushoz hasonlóan történt: //Van-e kamera az eszközünkben? private boolean vanKamera(Context kornyezet) {

if (kornyezet.getPackageManager().hasSystemFeature( PackageManager.FEATURE_CAMERA) ){ //Van szerencsére return true; } else { //Sajnos nem érzékeltünk kamerát return false; }

}

Az API 9 óta ennél egyszerűbb megoldást jelent a Camera.getNumberOfCameras() metódus, mert ez még a számukat is visszaadja (pl. egy előlapi és egy hátlapi kamera esetén = 2). A kamerához a hozzáférés igénylése a Camera.Open() metódussal történhet. API 9 után a metódusnak a megnyitni kívánt kamera sorszámát is megadhatjuk, ha ezt elhagyjuk, akkor az első hátlapi eszközhöz próbál hozzáférni. Az igénylés meghiúsulását kivételkezeléssel tudjuk érzékelni. public static Camera kameraFoglalás(){

Camera k = null; try {

Page 50: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

49

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

//Kísérlet a kamerapéldány megszerzésére k = Camera.open(); } //Kivétel esetében nincs eszköz, vagy foglalt. catch (Exception e){ //TODO: jelezzük a sikertelenséget } //Visszaadjuk a megszerzett kamerát, vagy null-t, ha nem //sikerült. return k;

}

A kivételkezelés elmulasztása azt eredményezheti, hogy a programunkat esetleg leállítja a rendszer. Az előnézeti osztály elkészítése Ahhoz, hogy lássuk, hogy fog kinézni a kép, vagy videó, keresőablak híján szükségünk van egy előnézeti osztályra, ami a kamera által látott képet mutatja közel valós időben. Ez az osztály célszerűen a SurfaceView kiterjesztése legyen, ami a nézetért felelős. Az osztály valósítja meg a SurfaceHolder.Callback interfészt is, azoknak a callback eseményeknek az elkapása érdekében, amelyek a nézet létrehozásánál, illetve törlésénél meghívódnak (surfaceCreated(), surfaceChanged(), surfaceDestroyed()). A surfaceChanged() metódus az előnézeti kép váltásakor hívódik, ilyen történhet például a kamera forgatása esetében. Példa az előnézeti osztály megvalósítására: public class KameraElonezet extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder holder; private Camera kamera; public KameraElonezet(Context context, Camera camera) { super(context); kamera = camera; //Saját SurfaceHolder készítése, //Jelezzük azt, hogy nálunk vannak a callback-hez használt //függvények holder = getHolder(); holder.addCallback(this); //elavult beállítás, de Android 3.0-nál korábbi rendszereknél

Page 51: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

50

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

//szükséges holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } //Létrejött a Surface, mondjuk meg, hová rajzolja a kamera az //előnézeti képet public void surfaceCreated(SurfaceHolder holder) { try {

kamera.setPreviewDisplay(holder); kamera.startPreview(); } catch (IOException e) { Log.d("KAM", "Hiba az előnézet beállításakor."); }

} //Mi a teendő az előnézet bezárásakor? public void surfaceDestroyed(SurfaceHolder holder) { //TODO: előnézet bezárásakori feladatok elkészítése } //Az előnézet változása public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { if (holder.getSurface() == null){ //Nincs is Surface, kilépünk a metódusból return; } //A változtatások előtt állítsuk le az előnézetet. try {

kamera.stopPreview(); } catch (Exception e){ //Lehet, hogy nem létezik a preview? }

//Az új beállításokkal újraindítjuk. try {

kamera.setPreviewDisplay(holder); kamera.startPreview(); } catch (Exception e){ Log.d("KAM", "Sajnos nem sikerült újraindítani az

előnézetet."); }

} }

Page 52: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

51

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

Az előnézet layout-ba öntése Ahhoz, hogy az előnézeti kép és a felvételt vezénylő gomb(ok) megjelenhessenek, most is biztosítani kell egy olyan layout-ot a program számára, amiben ez megfelelően megtörténhet. A legtöbb kameraprogram alapértelmezetten fekvő tájolásban működik. Ehhez célszerű a layout-ot horizontálisan berendezni és az Activity-ben is jelezni az igényünket a következő bejegyzéssel: android:screenOrientation="landscape" Az egyszerűség kedvéért helyezzük egy FrameLayout-ba az előnézeti képet, és egy gombbal biztosítsuk az exponálás lehetőségét. <FrameLayout android:id="@+id/kamera_elonezet" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1" /> <Button android:id="@+id/felvevo_gomb" android:text="Felvétel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> Ha készen vagyunk, bővítsük az Activity-nket! Adjunk hozzá Camera példányt és KameraElonezet-et is! Ezután foglaljuk le a kamerát és a FrameLayout-hoz adjuk hozzá a kamera előnézeti képét! private Camera kamera; private KameraElonezet elonezet; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //Foglaljuk le a kamerát kamera = kameraFoglalás(); //Készítsünk előnézetet elonezet = new KameraElonezet(this, kamera);

Page 53: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

52

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

FrameLayout frameElonezet = (FrameLayout)findViewById(R.id.kamera_elonezet); frameElonezet.addView(elonezet); Ezzel megoldottuk a kamera előnézeti képét. Ahhoz, hogy rögzíteni tudjuk a képet, a Camera.takePicture() metódusát fogjuk használni. A takePicture() callback mechanizmusokon keresztül biztosítja a képrögzítést. Névtúlterhelt metódus, a rövidebb változatban három paramétert kíván a hívásakor:

· shutter: a képrögítés időpontja, vagy null · raw: a tömörítetlen kép adatfolyama, vagy null · jpeg: a tömörített kép adatfolyama, vagy null

A metódus csak akkor érvényes, ha az előnézet aktív. A hívást követően amíg a jpeg callback vissza nem tér, nem lehet új előnézetet kérni, vagy másik képet rögzíteni. A fotó rögzítéséhez használt saját Camera.PictureCallback() megoldásunknak meg kell valósítania az absztrakt onPictureTaken() metódust. Ez akkor fog meghívódni, amikor már elérhető a kamerától származó adatfolyam. Ezeket (adat és kamera) paraméterben kapunk meg tőle. A tulajdonképpeni feladat ilyenkor, hogy gondoskodjunk a kapott Jpeg adatfolyam fájlba mentéséről. Először meghatározzuk a fájl nevét, amihez a rögzítés időpontját is felhasználjuk, majd lekérdezzük az SD kártya Pictures mappájának a helyét. Egy FileOutputStream-nek átadva az adatokat elmentjük a kapott képet. private PictureCallback kep = new PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { try { //A kép neve IMG_időpont.jpg formátumú lesz. String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); File pictureFile; //Az sdcard Pictures mappájába szeretnénk elmenteni. File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); pictureFile = new File(dir.getPath() + File.separator + "IMG_"+ timeStamp + ".jpg"); FileOutputStream kimenet = new FileOutputStream(pictureFile); kimenet.write(data); kimenet.close(); } catch (FileNotFoundException e) { Log.d("CAM", "A file nem található: " + e.getMessage());

Page 54: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

53

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

} catch (IOException e) { Log.d("CAM", "Filehozzáférési hiba: " + e.getMessage()); } } }; Ha megvan az eszközünk a rögzítésre, éljünk vele! Adjuk hozzá a Listenert a nyomógombhoz! A feladat a takePicture( null, null, sajátPictureCallback) paraméterekkel való meghívása. //Adjunk hozzá egy felvevő gombot és egy Listenert Button captureButton = (Button) findViewById(R.id.felvevo_gomb); captureButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { //Kattintásra rögzítse a képet kamera.takePicture(null, null, kep); } } ); Ezzel gyakorlatilag készen vagyunk, immár működik az előnézet és gombnyomásra rögzíthetjük a fotókat. Már csak egy, de annál lényegesebb feladat van hátra. Ha nincs szükségünk a kamerára, szabadítsuk fel a foglalás alól, hogy más alkalmazások is hozzáférhessenek! Erre praktikus időpont lehet az Activity pause állapotba lépése. A felszabadítása a Camera.release() metódussal történhet. //El ne felejtsük elengedni a kamerát! @Override protected void onPause() { super.onPause(); if (kamera != null){ kamera.release(); kamera = null; }; }; Most már valóban készen vagyunk az alkalmazással, a felület további testre szabását és saját funkciók hozzáadását a kedves olvasóra bízzuk!

Page 55: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

54

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

10. ábra Előnézeti kép a programban

11. ábra Az elkészült fájl neve

Page 56: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

55

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

6.2. Hangok lejátszása

A mai modern okostelefonok alapvető funkciói közé tartozik a különböző hangformátumú állományok lejátszása, illetve hangfelvételek készítése. A beépített rendszerhangok, figyelmeztetések lejátszását a RingtoneManager osztály segítségével tudjuk elérni, melynek TYPE_ALARM, TYPE_NOTIFICATION és TYPE_RINGTONE konstansai az alapértelmezett riasztási, értesítési és csengőhangot adják meg. Ezek lejátszása a Ringtone osztály egy példányán keresztül, a play() metódussal lehetséges. Uri csengo = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE); Uri ertesito = RingtoneManager.getDefaultUri(

RingtoneManager.TYPE_NOTIFICATION); … Ringtone ring = RingtoneManager.getRingtone(this, csengo); ring.play(); … ring.stop();

A hangok lejátszásánál lehetőségünk van arra is, hogy megmondjuk, hogy az adott hang milyen csatornán keresztül kerüljön lejátszásra. A különböző csatornák különböző tulajdonságokkal bírnak, így nem mindegy, hogy a lejátszandó hang milyen csatornán keresztül lesz lejátszva. A figyelmeztetés hangcsatornája például a beállított hangerőtől függetlenül mindig hangosan fog megszólalni. A csatornák konstansait az AudioManager osztály tartalmazza, mint például STREAM_ALARM, STREAM_MUSIC, STREAM_NOTIFICATION, stb. Ezeket a Ringtone osztály példányának setStreamType() metódusával tudjuk beállítani. Az AudioManager osztálynak ezen kívül még számos szolgáltatása van, melyek az AUDIO_SERVICE rendszerszolgáltatáson keresztül érhetőek el. Például az AudioManager osztály példányán keresztül elnémíthatjuk a mikrofont, vagy az adott csatornát a setMicrophoneMute() és a setStreamMute() metódussal. Bizonyos csatornák esetében a play() metódussal indított lejátszás folyamatosan ismétlődik, ezért ügyeljünk arra, hogy ilyen esetekben nekünk kell gondoskodnia a lejátszás megállításáról a stop() metódussal. A hangok lejátszásának másik módja a MediaPlayer osztály használata, amely minimális beállítások mellett képes audió és videó

Page 57: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

56

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

anyagok lejátszására.11 Jelenleg a következő kiterjesztésű formátumok támogatottak az Android alatt: .3gp, .mp4, .m4a, .aac, .flac, .mp3, .mid, .xmf, .mxmf, .rtttl, .rtx, .ota, .imy, .ogg, .mkv, .vaw.12 Először létre kell hozni a MediaPlayer osztály egy példányát, és ezen keresztül beállítani a paramétereket. A setDataSource() metódussal adhatjuk meg a lejátszás adatforrását. Ezt célszerű URI segítségével beállítani, ha nem rendszerhangot szeretnénk lejátszani. Ezt követi a prepare() metódus, mely felkészíti a telefont az azonnali lejátszásra, lefoglalja az erőforrásokat, és elvégzi a szükséges dekódolásokat. Amennyiben nagyobb hanganyagot szeretnénk lejátszani, ott problémát okozhat a prepare() metódus használata, mert hosszú időre megakaszthatja a felhasználói felületünk működését. Ebben az esetben helyette a prepareAsync() metódust érdemes használni, amely egy háttérszálban indítja el a felkészítési folyamatot. A felkészítés végén a OnPreparedListener eseményfigyelő onPrepared() függvénye hívódik meg. A lejátszást a start() függvénnyel indíthatjuk el, a stop() függvénnyel fejezhetjük be, de rendelkezésünkre áll a pause() és a seekTo() metódus is, melyekkel szüneteltethetjük és a megfelelő pozíciótól folytathatjuk lejátszást. Mivel a média anyagok lejátszása nagyon sok erőforrást igényel a rendszertől, ezért nagyon fontos, hogy miután befejeztük a műveletet, akkor szabadítsuk fel az erőforrásokat a release() metódussal. MediaPlayer lejatszo= new MediaPlayer(); lejatszo.setAudioStreamType(AudioManager.STREAM_MUSIC); lejatszo.setDataSource(getApplicationContext(), eleresiut); lejatszo.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); … lejatszo.prepare(); lejatszo.seekTo(0); lejatszo.start();

A hosszú időtartamú média anyagok lejátszása közben problémát okozhat az is, hogy a rendszer egy bizonyos idő elteltével hajlamos alvó állapotba kerülni. Ennek eredménye lehet, hogy alacsonyabb teljesítményre kapcsol a processzor és a Wifi, ami megakaszthatja a 11 Android Developers - Media Playback – http://developer.android.com/guide/topics/media/mediaplayer.html 12 Android Developers - Android Supported Media Formats - http://developer.android.com/guide/appendix/media-formats.html

Page 58: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

57

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

hanganyagok lejátszását. Ennek elkerülésére használjuk az Android „wake lock” funkcióját. Ennek segítségével jelezhetjük a rendszernek, hogy alkalmazásunk bizonyos erőforrásokat szeretne használni akkor is, amikor a rendszer üresjáratba kerül. Használatához szükséges, hogy alkalmazásunk a WAKE_LOCK engedéllyel rendelkezzen. MediaPlayer esetében a setWakeMode() metódusával tudjuk beállítani ezt a szolgáltatást, míg a Wifi esetében egy WifiLock osztály példányával tudjuk elérni ugyanezt. Ha olyan lejátszó alkalmazást szeretnénk készíteni, amely a háttérben fut, akkor az alkalmazásunkat, mint szolgáltatást érdemes megírni. Az ezzel kapcsolatos ismereteket lásd a későbbi fejezetekben. 6.2. Hangfelvételek készítése13

A hangfelvételek készítése a MediaRecorder API segítségével lehetséges, ha az adott eszköz hardveresen támogatja ezt. Felhasználásához szükséges, hogy alkalmazásunk rendelkezzen a RECORD_AUDIO és a WRITE_EXTERNAL_STORAGE engedélyekkel. Első lépésben a MediaRecorder osztály egy példányát kell létrehozni, majd ennek a setAudioSource() metódusával kell beállítani a forrást, ami általában a beépített mikrofon - MediaRecorder.AudioSource.MIC - szokott lenni. Ezután a setOutputFormat() metódussal be kell állítani a kimeneti állomány formátumát. Itt célszerű mindig az AMR audió formátumot használni, mert az MP4-es konténer formátumot egyes eszközök hibásan használják. Ezt követi a kimeneti állomány megadása a setOutputFile() metódussal. A következő lépésben az audió állomány kódolását kell beállítani a setAudioEncoder() metódussal. Itt lehetőségünk van pár nem kötelező beállítással tovább pontosítani a felvétel tulajdonságait. Maximalizálhatjuk például a felvételünk hosszát ms-okban a setMaxDuration() metódussal, vagy megszabhatjuk azt is, hogy mekkora nagyságú állományt készíthetünk a setMaxFileSize() metódus segítségével. De meghatározhatjuk a felvétel bit- és mintavételezési rátáját is a setAudioEncodingBitRate() és a setAudioSamplingRate() metódusokkal. Ha elvégeztük a fenti beállításokat, akkor a prepare() metódussal készíthetjük fel az eszközünket ténylegesen a felvétel elkészítésére, amit a start() és a stop() metódusokkal indíthatunk el és állíthatunk meg. Legvégső lépésként pedig soha ne feledkezzünk meg arról, hogy ha elkészült a felvételünk, akkor lehetőleg azonnal szabadítsuk fel az erőforrásokat a release() metódus segítségével.

13 Android Developers - Audio Capture - http://developer.android.com/guide/topics/media/audio-capture.html

Page 59: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

58

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

MediaRecorder felvevo = new MediaRecorder(); felvevo.setAudioSource(MediaRecorder.AudioSource.MIC); felvevo.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); … felvevo.setOutputFile(eleresiut); felvevo.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); felvevo.prepare(); … felvevo.start(); … felvevo.stop();

Page 60: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

59

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

7. Szolgáltatások14

A szolgáltatások, vagy másképpen Service-ek, alkalmazásunk olyan részei, melyek hosszú futásidejű műveleteket hajtanak végre a háttérben felhasználói felület nélkül. A szolgáltatásoknak nagyon sokféle felhasználási területe van, többek között kezelhet I/O műveleteket, hálózati le- vagy feltöltéseket, de akár zenelejátszást is. A szolgáltatásoknak két nagyobb csoportja van:

· Started – Alkalmazásunkból a startService() metódussal indíthatjuk. Általában egy feladat végrehajtását végzi el, és nem ad vissza semmilyen eredményt a hívójának. Ha szeretnénk visszajelzést, akkor arról nekünk magunknak kell gondoskodnunk.

· Bound – Az alkalmazásunk egy komponense hozzákapcsolódik a szolgáltatáshoz a bindService() metódussal. Az ilyen szolgáltatás kliens szerver alapú interfészt bocsát a rendelkezésünkre, amelyen keresztül egyszerűen kommunikálni tudunk a szolgáltatással. Csak addig fut a Service, ameddig legalább egy komponens kapcsolódik hozzá.

Bár elvileg ez a két szolgáltatás elkülönül, de nincs akadálya annak sem, hogy egy szolgáltatás egyszerre Started és Bound Service-ként is működjön. Fontos megjegyezni, hogy a Service minden esetben a fő szálon fut, és nem hoz létre magának külön szálat és nem fut külön processzben. Ezért ha olyan műveletet szeretnénk végrehajtatni vele, ami blokkolhatja az alkalmazásukat, akkor a szolgáltatáson belül kell létrehoznunk az ilyen művelet végrehajtásához szükséges külön szálat. A szolgáltatást a Service osztály, vagy annak egy alosztályának egy példányaként kell létrehoznunk, majd felül kell definiálnunk néhány metódust. Ezek közül a legfontosabbakat kiemeljük:

· onStartCommand() – Csak a Started Service esetében kell implementálni. Ez a metódus hívódik meg, amikor egy Activity elindítja a szolgáltatásunkat a startService() metódussal. Ha implementáljuk ezt a metódust, akkor nekünk kell gondoskodnunk arról, hogy a Service a munka elvégzése után befejezze önmagát, amit a stopSelf() metódussal érhetünk el, vagy egy másik komponenssel is leállíttathatjuk a szolgáltatást a stopService() metóduson keresztül.

· onBind() – Csak a Bound Service esetén kell implementálni. Akkor hívódik meg, ha egy komponens hozzá akar kapcsolódni

14 Android Developers – Services - http://developer.android.com/guide/components/services.html

Page 61: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

60

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

a szolgáltatáshoz a bindService() metódussal. A metódust egy interfésszel kell ellátni, hogy a kliens kommunikálni tudjon azon keresztül a szolgáltatással. Mindig létre kell hozni, de ha nem Bound Service-t szeretnénk létrehozni, akkor visszatérési értéke null kell, hogy legyen.

· onCreate() – A rendszer hívja meg ezt a metódust, amikor először hozzuk létre a szolgáltatást.

· onDestroy() – A rendszer hívja meg, amikor a szolgáltatás már nincs többé használatban. Ebben kell gondoskodni az erőforrások felszabadításáról.

Az Android kevés memória esetén leállíthatja a szolgáltatásainkat. A régóta futó szolgáltatások kerülnek először leállításra, mivel ezek az idő teltével alacsonyabb prioritást kapnak a rendszertől a leállítási listában. Általában a Bound Service-ek kerülnek leállításra legutoljára, míg az előtérben futó Foreground Service-eket soha sem állítja le a rendszer. Ha elindítunk egy szolgáltatást, akkor érdemes felkészíteni arra, hogy kényszerített leállítása után, amikor ismét megfelelő erőforrások állnak rendelkezésre újraindulhasson az. A Started Service-eket a rendszer automatikusan újraindítja, míg a Bound Service-ek újraindításáról nekünk kell gondoskodni. Ilyenkor használhatjuk fel azt a tulajdonságát a szolgáltatásoknak, hogy egyszerre működhetnek Stated és Bound Service-ekként is. Ezért ha azt szeretnénk, hogy a Bound Service szolgáltatásunk újrainduljon, akkor nincs más teendőnk, mint egy startService() metódust is meghívni a szolgáltatásnál, és implementálni az onStartCommand() metódust. 7.1 Service-ek létrehozása a Manifest állományban15

A szolgáltatásunkat fontos deklarálnunk a Manifest állományban, hogy a rendszer tudomást szerezzen annak létezéséről. A szolgáltatásokat a <service> taggal deklarálhatjuk, melyet az <application> elemben helyezhetünk el. A <service> tag egyetlen kötelezően megadandó eleme a name attribútum, amelynél meg kell adnunk az osztályunk nevét, amely implementálja a szolgáltatást. Ezen túlmenően még megadhatunk további attribútumokat is, mint például az

· enable – Ha értéke igaz, akkor a szolgáltatást példányosítani és futtatni is lehet, ellenkező esetben nem. Alapértelmezett értéke true.

· exported – Ha értéke true, akkor a szolgáltatást más alkalmazások komponensei is használhatják. Alapértelmezett

15 Android Developers - <service> - http://developer.android.com/guide/topics/manifest/service-element.html

Page 62: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

61

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

értéke false, de ha legalább egy filter-t megadtunk a szolgáltatáshoz, akkor már true.

· icon – alapértelmezetten a szolgáltatás az alkalmazásunk ikonját használja, de ennek segítségével külön ikon is adható a szolgáltatáshoz.

· label –a szolgáltatásnak a felhasználói felületen megjelenő neve.

· permission – azt határozhatjuk meg ezzel, hogy a szolgáltatást hívó félnek milyen engedélyekkel kell rendelkeznie, hogy futtathassa a szolgáltatást. Ha nem állítjuk ezt be külön, akkor az <application> elemnél megadott engedélyek vonatkoznak a szolgáltatásra is.

· process – a processz nevét adhatjuk meg, amelyben a szolgáltatás fut. Ha nem adjuk meg, akkor a szolgáltatás az alkalmazás processzében fut. Amennyiben a megadott processz neve kettősponttal kezdődik, úgy a szolgáltatás elindításakor egy új processz indul az itt megadott névvel.

7.2. Started Service16

A Started Service-t akkor érdemes használni, ha az induló adatok megadása után már nincs szükségünk további kommunikációra a szolgáltatással. Bár a Started Sevice is képes kommunikálni komponensekkel, de a sok kommunikációt tartalmazó szolgáltatásokat inkább Bound Service-ként érdemes elindítani. A Started Service egy alkalmazásból a startService() metódussal indítható el, és mindaddig fut, ameddig le nem állítja önmagát a feladat elvégzése után a stopSelf() metódussal, vagy ameddig az alkalmazás le nem állítja a stopService() metódussal. A startService() metódus paramétere egy Intent, amellyel az indítandó szolgáltatást adhatjuk meg. Az Intent konstruktorában ilyenkor első paraméterként az aktuális környezetet (Context), míg másodiként a szolgáltatás osztályát adhatjuk meg. Az átadandó Intent tartalmaz egy startId egyedi azonosítót, amely minden új szolgáltatás híváskor új értéket vesz fel. //Szolgáltatás indítása Context kont=this.getApplicationContext(); … Intent indulas = new Intent(kont , StopperService.class); startService(indulas); … 16 Android Developers – Services - http://developer.android.com/guide/components/services.html

Page 63: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

62

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

Intent leallitas = new Intent(kont , StopperService.class); stopService(leallitas);

A legeslegelső startService() hívás esetén az onCreate() metódus kerül futtatásra, amelyben inicializálhatjuk a szolgáltatást. Ezt követi az onStartCommand() metódus végrehajtása, ahol azt adhatjuk meg, hogy mit is hajtson végre a szolgáltatásunk. A metódus végrehajtása alapértelmezetten a felhasználói felület szálán történik, ezért ügyeljünk arra, hogy ha hosszabb ideig tartó folyamatot akarunk elindítani, akkor azt vagy külön szálon, vagy Async Task-ban futtassuk. Az onStartCommand() metódus egy egész számmal tér vissza, amely megadja, hogy hogyan folytatódjon a szolgáltatás futtatása, ha azt a rendszer korábban leállította. A lehetséges konstans értékek a következőek:

· START_NOT_STICKY – A leállított szolgáltatás nem indul újra automatikusan, csak ha azt a programból újra el nem indítják.

· START_STICKY – A rendszer automatikusan megpróbálja újraindítani a leállított szolgáltatást, de nem küldi újra az eredeti Intent-et, hanem helyette egy null értékű Intent-el indítja újra a szolgáltatást.

· START_REDELIVER_INTENT - A rendszer automatikusan megpróbálja újraindítani a leállított szolgáltatást az eredeti Intent elküldésével.

//A started service felépítése public class StopperService extends Service{ @Override public IBinder onBind(Intent arg0) { return null; } @Override public void onDestroy() { super.onDestroy(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { … //Itt adjuk meg, hogy mit csináljon a szolgáltatás … return super.onStartCommand(intent, flags, startId); } }

Page 64: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

63

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

A szolgáltatás futása alatt információkat tud visszaküldeni az őt meghívó alkalmazásnak. Ennek több lehetséges módja is van. Az információkat eljuttathatjuk a hívó félhez Broadcast Intent küldésével, melyet a szolgáltatásból a sendBroadcast() metódussal tudunk megvalósítani. A szolgáltatást hívó alkalmazáshoz ilyenkor írnunk kell egy Broadcast Receiver-t, melyet regisztrálnunk kell a registerReciever() metódussal, illetve meg kell adnunk egy Intent Filtert is. Magukat az információkat ezek után az onRecieve() metódus felüldefiniálásával tudjuk kiolvasni a kapott Boradcast Intent-ből. Másik lehetőség, ha a Messenger osztályt használjuk fel a kommunikációra. Ilyenkor a szolgáltatásnak implementálnia kell a Handler osztályt, és hozzá kell kapcsolni egy Messenger elemet. Ez utóbbi köti össze a hívó processzt a Handler elemmel, és ezen keresztül lehet Message osztályba tartozó üzenetet küldeni a szolgáltatás felé, melyet a szolgáltatásban implementált Handler osztály handleMessage() metódusával tudunk feldolgozni. Harmadik lehetőség a Pending Intent használata, ami akkor lehet hasznos számunkra, amikor egy Activity-nek akarunk üzenetet küldeni a szolgáltatásból. Ekkor az Activity createPendingResult() metódusának futtatása eredményeként egy Pending Intent-et kapunk, amit átadunk a szolgáltatásnak. A Service tárolja ezt az Intentet, s ezen keresztül tud visszajelezni az Activity felé. A Pending Intent send() metódusának meghívására az Activity onActivityResult() metódusa hívódik meg, amivel kiolvashatjuk a szükséges adatokat. A szolgáltatást külső elemekből a stopService() metódussal állíthatjuk le, melynek bemenő paramétere egy Intent, amely a szolgáltatást azonosítja. Az így leállított szolgáltatás törlődik, még akkor is, ha több alkalmazás is használhatja éppen a szolgáltatást. Másik lehetőség, ha a szolgáltatás saját magát állítja le a stopSelf() metódussal. Bármelyik metódussal is kezdeményezzük a szolgáltatás leállítását, a leállítás utolsó lépéseként az onDestroy() metódus hajtódik végre, amelyben lehetőségünk van a háttérszálak leállítására és az erőforrások felszabadítására. 7.3. IntentService

Mivel a legtöbb alkalmazás nem igényli több kérés egyidejű kiszolgálását, ezért ezekben az estekben használhatjuk az IntentService osztályt, amely nagyban megkönnyítheti a munkánkat, hiszen leveszi a vállunkról a több szálon futó szolgáltatások kezelését. Ez az osztály ugyanis a szolgáltatás indításakor automatikusan egy külön szálon indítja el a Service onHandleIntent() metódusát, amelyben megadhatjuk, hogy mit szeretnénk csinálni. Az IntentService osztály továbbá a startService() hívások által beérkezett

Page 65: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

64

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

Intent-eket egy várakozási sorba helyezi, és ezeket egymás után hajtja végre. Vigyázzunk arra, hogy mivel ebben az esetben az onStartCommand() metódus gondoskodik a háttérszál indításáról és a kérések sorba állításáról, ezért ha ezt a metódust felüldefiniáljuk, akkor feltétlenül meg kell hívnunk az ősosztály (super) onStartCommand() metódusát. //Az IntentService felépítése public class LetoltesSzolgaltatas extends IntentService{ public LetoltesSzolgaltatas() { super("LetoltesSzolgaltatas"); } @Override protected void onHandleIntent(Intent arg0) { //Ide kerülnek a végrehajtandó feladatok } @Override public IBinder onBind(Intent intent) { return null; } }

7.4. Bound Service

Ez a szolgáltatás alkalmas leginkább a tipikus kliens-szerver kapcsolat megvalósítására. A Bound Service lehetőséget biztosít, hogy különböző komponensek, Activity-k kapcsolódjanak hozzá, küldjenek és fogadjanak üzeneteket a szolgáltatástól. A szolgáltatás nem fut korátlan ideig, hanem csak addig működik, ameddig kiszolgálja a hozzá kapcsolódott komponenseket. A kliens oldal az bindService() metódus meghívásával tud kapcsolódni a szolgáltatáshoz. A metódusnak három paramétere van. Az első egy Intent, amellyel az indítandó szolgáltatást azonosíthatjuk. A második egy ServiceConnection osztályba tartozó elem, amely megkapja, hogy a kapcsolat létrehozása sikeres volt-e vagy nem. A harmadik paraméter egy egész szám, amellyel a kapcsolathoz tartozó beállításokat adhatjuk meg, mint például a BIND_AUTO_CREATE értéket, amely hatására a szolgáltatás automatikusan létrejön, ha még nem létezett.

Page 66: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

65

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

A bindService() metódus hívásával tud tehát a komponens kapcsolódni a szolgáltatáshoz. Ekkor az Android meghívja az onBind() metódust, amely visszatérési értéke egy IBinder interfész osztálybeli elem, amelyen keresztül tud majd kommunikálni a kliens a szolgáltatással. Ezt az IBinder elemet kapja meg a kliens. Az IBinder elemet több úton is megkaphatjuk. Az egyik legegyszerűbb módja az, amikor a szolgáltatás és a kliens is ugyanannak a processznek a része. Ekkor egyszerűen csak örököltetnünk kell az interfészünket a Binder osztályból, és az új osztályunk példányát kell visszaadnunk az onBind() metódussal. Ha különböző processzekben fut a kliens és a szerver, akkor az interfészünket Messenger-rel együtt kell megadnunk. Ekkor a szolgáltatás definiál egy Handler-t, amely segítségével kezelni tudja a beérkező Message üzeneteket, és amely elérhetővé teszi az IBinder elemet a kliensnek. Ez a legegyszerűbb módja, hogy kezeljük a különböző folyamatok közötti kommunikációt, mivel a Messenger a beérkező üzenetekből egy várakozási sort hoz létre egy szálban. public class CountDownService extends Service { private final IBinder szolgaltatasBinder= new SajatBinder(); public class SajatBinder extends Binder{ CountDownService getService(){ return CountDownService.this; } } @Override public IBinder onBind(Intent arg0) { return szolgaltatasBinder; } public String uzenet(){ // return "A szolgáltatás eredményesen lefutott"; } }

A külön processzekben futó szolgáltatáshoz kapcsolódás másik lehetséges módja, az AIDL (Android Interface Definition Language), amelyre a fenti Messeneger-t használó technika is épül, de ezzel itt részletesen nem foglalkozunk.

Page 67: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

66

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

A kliens oldalon, létre kell hoznunk a ServiceConnection osztályból származó saját osztályt, és implementálnunk kell annak az onServiceConnected() és onServiceDisconnected() metódusait. Az onServiceConnected() metódus bemenő paramétere a szolgáltatás onBind() metódusától kapott IBinder elem, amin keresztül aztán a kliens kommunikálhat a szolgáltatással. private ServiceConnection kapcsolat =new ServiceConnection(){ @Override public void onServiceConnected(ComponentName arg0, IBinder arg1) { SajatBinder bind= (SajatBinder) arg1; CountDownService szolgaltatas = bind.getService(); } @Override public void onServiceDisconnected(ComponentName arg0) { … } };

7.5. Foreground szolgáltatások17

A Foreground Service-ek olyan szolgáltatások, amelyek - szemben a többi szolgáltatással – mindig láthatóak az értesítési területen. Ezen szolgáltatások előnye, hogy ezeket a rendszer kiemelten kezeli, így kevés rendszererőforrás esetén is csak a legvégső esetben zárja be az ilyen szolgáltatásokat az Android. Bármilyen szolgáltatást lehet az előtérben futtatni, ehhez nincsen más dolgunk, mint meghívni a szolgáltatás startForeground() metódusát, melynek két bemenő paramétere van. Az első egy egész szám, amely egyedileg azonosítja az értesítést, míg a második maga az értesítés, ami egy Notification osztályba tartozó elem. A szolgáltatás mindaddig Foreground szolgáltatás marad, s így látható az értesítési területen, amíg a szolgáltatás fut, illetve amíg az előtérben való futást le nem állítjuk a stopForeground() metódussal. //Egy Foreground szolgáltatás indítása public void onCreate() { NotificationManager notifman = (NotificationManager)

this.getSystemService(Context.NOTIFICATION_SERVICE);

17 Android Developers - Running a Service in the Foreground - http://developer.android.com/guide/components/services.html#Foreground

Page 68: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

67

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

Notification notif = new Notification(R.drawable.ic_launcher, "Stopperóra", System.currentTimeMillis());

Intent notifint = new Intent (this, StopperForegroundActivity.class);

PendingIntent pi = PendingIntent.getActivity(this, 0, notifint, 0);

notif.setLatestEventInfo(this, "Stopperóra", "00:00", pi); startForeground(11, notif); super.onCreate(); }

Az értesítsek olyan üzenetek, melyeket a felhasználói felületen kívül tudunk megjeleníteni az értesítési területen, amely lefele való húzással megjeleníthető. Ha lenyitottuk az értesítési területet, akkor ott normál és - a 4.1-es Android-tól kezdve - nagy méretű értesítéseket találhatunk. Az értesítés felhasználói felületét, és a hozzá kapcsolódó műveleteket egy NotificationCompat.Builder elem tárolja, melynek build() metódusával hozhatjuk létre magát a Notification18 osztály elemét. A Notification elemnek három kötelező eleme van:

· Kicsi ikon – a setSmallIcon() metódussal állítható be · Az értesítés címe- a setContentTitle() metódussal állítható be · Részletes leírás – a setContentText() metódussal állítható be

Bár opcionális lehetőség csak, de ezen felül minden értesítéshez érdemes hozzáadni egy műveletet, aminek segítségével a felhasználó közvetlenül el tudja érni az alkalmazásunk egy Activity-jét. Ha a felhasználó az értesítésére kattint, akkor egy PendingIntent osztályba tartozó Intent elemet küld ki a rendszer a NotificationCompat.Builder osztály setContentIntent() metódusával. A Notification elem használatához szükségünk lesz a NotificationManager osztály egy példányára, mivel az értesítés szolgáltatás rendszerszolgáltatásnak minősül. Ennek az osztálynak a notify() metódusával tudjuk megjeleníteni az értesítést, melynek első paramétere egy értesítés azonosító, a második pedig maga a Notification objektum. Ha az értesítést vissza szeretnénk vonni, akkor pedig a cancel() metódust kell meghívni az értesítés referenciájával.

18 Android developers – Notification - http://developer.android.com/guide/topics/ui/notifiers/notifications.html

Page 69: Android alapú szoftverfejlesztés haladóknakzeus.nyf.hu/~gyiszaly/targyak/android/jegyzetek/Android...Emulator ablakot kell kiválasztanunk (Window/Show View/Emulator Control). Itt

Android alapú szoftverfejlesztés haladóknak

68

Az oktatási segédanyag a „Mobil alkalmazásfejlesztés az informatikai tudás innovatív alkalmazásával” című, TÁMOP-2.2.4-11/1-2012-0055 kódszámú projekt keretében valósult meg.

NotificationManager notifman = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE); Notification notif = new Notification(R.drawable.ic_launcher, "Stopperóra", System.currentTimeMillis()); notif.setLatestEventInfo(kont, "Stopperóra", p+":"+mp, pi); notifman.notify(11, notif);

7.6. Szolgáltatások automatikus elindítása

Előfordulhat, hogy olyan alkalmazásra, vagy szolgáltatásra van szükségünk, amely az eszköz indításakor automatikusan elindul. Erre is kínál megoldást az Android. Nincs más dolgunk, mint első lépésben alkalmazásunkat vagy szolgáltatásunkat felíratni egy BroadcastReciever-rel a BOOT_COMPLETED eseményre. Ebben az esetben az eszköz elindulása után a BroadcastReceiver megkapja majd az eseményt jelző Intent-et, és lefuttatja a BroadcastReceiver onReceive() metódusát, amiben gondoskodhatunk az alkalmazásunk, vagy szolgáltatásunk elindításáról.