A modern Android alkalmazásokban a hálózati kérések kezelése alapvető fontosságú, mivel a legtöbb mobilalkalmazás az internetre támaszkodik valamilyen módon. Az Android az internetes lekérdezésekhez több könyvtárat is biztosít, például az Apache HttpClient és az HttpURLConnection. Az Apache HttpClient volt a hivatalosan ajánlott könyvtár az Android 2.3 Gingerbread (API 9) előtt, azonban a későbbi verziókban a Google az HttpURLConnection-ra helyezte a hangsúlyt. Android 6.0-tól kezdve az Apache HttpClient teljesen eltűnt a fejlesztői környezetből, így az HttpURLConnection maradt az alapértelmezett választás. Bár az HttpURLConnection továbbra is működik és használható, van néhány hátránya: nem a legegyszerűbb könyvtár a webes kérések írásához, és rengeteg ismétlődő kódrészletet igényel. Erre a problémára jelent megoldást a Google Play csoportban dolgozó Ficus Kirkpatrick, aki kifejlesztette a Volley nevű könyvtárat.

A Volley egy egyszerűsített csomagoló réteg, amely alapértelmezés szerint az HttpURLConnection-t használja, de képes más könyvtárakkal is együttműködni. Az egyszerű használat, a memória kezelés és a számos beépített funkció lehetővé teszik a fejlesztők számára, hogy a lehető leghatékonyabban kezeljék az internetes kéréseket. A Volley egyik legnagyobb előnye, hogy jelentősen csökkenti a szükséges kód mennyiségét. Ahelyett, hogy ismétlődő try/catch blokkokkal kellene megírni a kódot, a könyvtár magától kezeli az alapvető ellenőrzéseket, lehetővé téve, hogy a fejlesztő a feladatra összpontosítson.

A Volley segítségével egyszerűen kezelhetők a különböző típusú kérések, mint például a sztringek, JSON, képek vagy egyéni adatstruktúrák. A könyvtár nemcsak a könnyű kérések kezelésére alkalmas, hanem a hálózati válaszok kezelésében is kiemelkedően teljesít. Azonban a nagy fájlok letöltésekor nem ez a legjobb választás, mivel a visszakapott objektumok memóriában történő feldolgozása nem optimális nagy fájlok esetén. Ilyen esetekben inkább a DownloadManager-t ajánlott használni, amely jobban kezeli a nagy adatokat.

Az internetes kérések végrehajtása a Volley-ban egyszerű: először létre kell hozni egy új kérés sorozatot (RequestQueue), majd hozzá kell adni a kívánt kérésünket. Például egy sztring típusú kérést készíthetünk, amely egy URL-t kér le, és annak válaszát egy TextView-ban jelenítjük meg. A kérés feldolgozása közben a Volley automatikusan kezeli az összes szükséges háttérfeladatot, mint például a válaszok gyorsítótárazása és a háttérben történő szálkezelés. Az alapértelmezett szálkezelő négy szálat biztosít, ami optimális teljesítményt nyújt a legtöbb alkalmazás számára.

A Volley hátránya, hogy nem támogatja a streaming tartalmakat vagy a nagy fájlok folyamatos letöltését. Ezt a kérdést az HttpURLConnection könyvtár továbbra is jobban kezeli, így ha olyan alkalmazásra van szükség, amely folyamatos adatfolyamot kezel, akkor célszerűbb a hagyományos HttpURLConnection-t választani.

A Volley integrálása egy Android projektbe meglehetősen egyszerű. Ehhez először le kell töltenünk a Volley forrást az Android Open Source Project weboldaláról, majd hozzá kell adnunk azt a projektünkhöz Android Studio-ban. A megfelelő engedélyek és függőségek beállítása után máris kezdhetjük a kérések végrehajtását. Az alapértelmezett kérések között szerepel a GET típusú kérés, amelyet könnyedén beállíthatunk, hogy egy URL-t lekérjen és annak válaszát feldolgozza.

Fontos tudni, hogy a Volley nemcsak egyszerű kéréseket támogat, hanem a válaszokat gyorsítótárba is menti, így a későbbi kérések gyorsabbak lehetnek, ha az adatokat már egyszer letöltöttük. Azonban, ha egy adott kérésre már nincs szükség, és azt még mindig próbáljuk végrehajtani, akkor a válasz eldobásával elkerülhetjük a felesleges adatforgalmat. Ha például egy ListView-ban görgetve frissítjük az elemeket, a nem szükséges válaszok eldobása fontos lépés a hatékonyság érdekében.

A Volley tehát egy hatékony, de könnyen használható megoldás, amely lehetővé teszi az Android alkalmazások számára, hogy gyorsan és hatékonyan kezeljék az internetes kéréseket. Azonban fontos megérteni, hogy a megfelelő könyvtár kiválasztása mindig az alkalmazás igényeitől függ. Ha nagy fájlok letöltésére vagy streamingre van szükség, akkor más eszközöket kell választanunk.

Hogyan szerezzük meg az utolsó ismert helyet Android alkalmazásban?

Az egyik leggyakoribb feladat a mobilalkalmazások fejlesztése során az utolsó ismert hely megszerzése. Ezt a feladatot egyszerűen elvégezhetjük a Google Location API-k segítségével, minimális erőforrás-igénnyel. Ennek köszönhetően nem kell aggódnunk a túlzott akkumulátorhasználat miatt, mivel az alkalmazásunk nem felelős az állandó helymeghatározási frissítésekért. Az alábbiakban bemutatjuk, hogyan érhetjük el ezt az egyszerű, de hasznos funkciót Android Studio környezetben.

Előkészületek

Első lépésként hozzunk létre egy új projektet Android Studioban, és nevezzük el például "GetLastLocation"-nek. Használjuk az alapértelmezett telefon és táblagép beállításokat, majd válasszuk az "Empty Activity" lehetőséget, amikor az aktivitás típusát kérdezi a rendszer. Ezután hozzá kell adnunk a szükséges engedélyeket az Android Manifest fájlhoz, hogy hozzáférhessünk a helymeghatározási funkciókhoz.

Hogyan csináljuk?
Elsőként nyissuk meg az Android Manifest fájlt, és adjuk hozzá a következő engedélyt a helymeghatározás eléréséhez:

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

Ezután, a Gradle Scripts szekcióban, nyissuk meg a build.gradle (Module: app) fájlt, és adjuk hozzá a következő sort a függőségek szekcióhoz:

gradle
compile 'com.google.android.gms:play-services:8.4.0'

A következő lépés az activity_main.xml fájl megnyitása, ahol cseréljük ki az alapértelmezett TextView elemet egy új XML struktúrára, amely tartalmaz egy Button és egy TextView elemet, hogy megjeleníthessük a helyet a felhasználónak.

Ezután nyissuk meg a MainActivity.java fájlt, és adjuk hozzá az alábbi globális változókat:

java
GoogleApiClient mGoogleApiClient;
TextView mTextView; Button mButton;

Most hozzunk létre egy osztályt, amely kezeli a ConnectionCallbacks interfészt, amely a GoogleApiClient csatlakozásakor fog végrehajtódni:

java
GoogleApiClient.ConnectionCallbacks mConnectionCallbacks = new GoogleApiClient.ConnectionCallbacks() { @Override public void onConnected(Bundle bundle) { mButton.setEnabled(true); } @Override public void onConnectionSuspended(int i) {} };

Ezen kívül hozzuk létre az OnConnectionFailedListener osztályt is, hogy kezelni tudjuk a csatlakozási hibákat:

java
GoogleApiClient.OnConnectionFailedListener mOnConnectionFailedListener = new GoogleApiClient.OnConnectionFailedListener() {
@Override public void onConnectionFailed(ConnectionResult connectionResult) { Toast.makeText(MainActivity.this, connectionResult.toString(), Toast.LENGTH_LONG).show(); } };

Ezután adjuk hozzá a setupGoogleApiClient metódust, amely inicializálja a GoogleApiClient-et:

java
protected synchronized void setupGoogleApiClient() { mGoogleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(mConnectionCallbacks) .addOnConnectionFailedListener(mOnConnectionFailedListener) .addApi(LocationServices.API) .build(); mGoogleApiClient.connect(); }

Ezután a getLocation metódust hozzuk létre, amely meghívja a getLastLocation() metódust, és megjeleníti az utolsó ismert helyet:

java
public void getLocation(View view) {
try { Location lastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); if (lastLocation != null) { mTextView.setText(DateFormat.getTimeInstance().format(lastLocation.getTime()) + "\n" + "Latitude=" + lastLocation.getLatitude() + "\n" + "Longitude=" + lastLocation.getLongitude()); } else { Toast.makeText(MainActivity.this, "null", Toast.LENGTH_LONG).show(); } } catch (SecurityException e) { e.printStackTrace(); } }

A programot ezután futtathatjuk egy valódi eszközön vagy emulátoron.

Hogyan működik?
Ahhoz, hogy meghívhassuk a getLastLocation() metódust, először be kell állítanunk a GoogleApiClient-et. A setupGoogleApiClient() metódusban hívjuk meg a GoogleApiClient.Builder metódust, majd csatlakozunk a könyvtárhoz. Amint a könyvtár csatlakozott, a ConnectionCallbacks.onConnected() metódusunkat hívja meg. Itt engedélyezzük a gombot, amely segítségével a felhasználó kérheti az utolsó helyet.

Fontos, hogy a rendszer felelős a hely frissítéséért, és bár az alkalmazásunk kérheti a helyet, a rendszer felelős a GPS-eszközök megfelelő használatáért. A getLastLocation() hívás nem frissíti folyamatosan a helyet, csak visszaadja az utolsó ismert helyet. Ez hasznos lehet olyan alkalmazásokban, amelyek nem igényelnek folyamatos helymeghatározást, hanem csak egy-egy eseményhez kapcsolódóan kérik le a helyet, mint például egy geokódolás során.

Fontos megemlíteni, hogy a helymeghatározás pontossága a jogosultságok beállításától függ. Az alapértelmezett ACCESS_COARSE_LOCATION jogosultságot használjuk, de ha pontosabb adatokat szeretnénk, kérhetjük az ACCESS_FINE_LOCATION engedélyt is.

További tudnivalók
Ha problémák merülnek fel a GoogleApiClient csatlakozása közben, az OnConnectionFailedListener metódus fog lefutni, és hibát jelenít meg. A helymeghatározás tesztelése nehézkes lehet, mivel nem könnyű valódi körülmények között tesztelni a helyet. Az Android Studio emulátora azonban lehetőséget biztosít GPS-adatok szimulálására, így könnyen tesztelhetjük az alkalmazásunk helymeghatározási funkcióit anélkül, hogy valóban mozgásba kellene hoznunk az eszközt.

Ha szeretnénk szimulálni a helyet az emulátorban, kövessük az alábbi lépéseket:

  1. Navigáljunk a Tools | Android | Android Device Monitor menüpontba.

  2. Válasszuk az Emulator Control fület.

  3. Adjuk meg a kívánt GPS koordinátákat a Location Controls szekcióban.

Fontos, hogy a hely szimulálása GPS adatokat küld, tehát az alkalmazásunknak képesnek kell lennie a GPS adat fogadására. Ha a getLastLocation() metódust használjuk, előfordulhat, hogy nem kapunk szimulált GPS adatokat, mivel ez a módszer nem csak a GPS-eszközöket használja a hely meghatározásához, hanem egyéb forrásokat is.

Hogyan működik az Android GCM (Google Cloud Messaging) értesítési rendszer, és hogyan kell azt megfelelően implementálni?

Az Android alkalmazásokban a push értesítések kezelése a GCM (Google Cloud Messaging) szolgáltatáson keresztül történik, amely ma már Firebase Cloud Messaging (FCM) néven ismert. Az implementáció alapja több szolgáltatás (Service) létrehozása, amelyek mind külön feladatokat látnak el, miközben a tényleges kommunikációs logika a Google API-n belül van kapszulázva. Ez azt jelenti, hogy a fejlesztői oldalról minimális kódolás szükséges, azonban fontos a pontos struktúra és a szolgáltatások megfelelő szétválasztása.

A GCM-hez való regisztráció külön háttérfolyamatban történik, hogy ne blokkolja az alkalmazás felhasználói felületét. Ezért hozunk létre egy IntentService típusú osztályt, például GCMRegistrationService néven, amely a onHandleIntent() metódusban végzi el a regisztrációt a GCM szerverrel. A getToken() hívás itt történik, amely lekéri az eszközhöz tartozó egyedi azonosítót (token), amit később a szervernek is továbbítani kell.

A token lekérdezését és mentését követően az alkalmazás egy másik szolgáltatáson keresztül (GCMInstanceService, amely az InstanceIDListenerService-t örökli) figyeli, hogy megváltozott-e a token. Amennyiben igen, újraindítja a regisztrációs folyamatot a korábban definiált GCMRegistrationService segítségével.

Az értesítések fogadása a GCMService nevű osztályban történik, amely a GcmListenerService-t örökli, és az onMessageReceived() metódusban reagál a beérkező üzenetekre. Itt a fejlesztő határozza meg, milyen műveletek történjenek egy-egy értesítés beérkezésekor, legyen az logolás, felhasználói értesítés, adatfrissítés, stb.

Az alkalmazás indításakor az onCreate() metódusban kell elindítani a regisztrációs szolgáltatást. Azonban egy fontos, gyakran elhagyott lépés, hogy előtte ellenőrizni kell, elérhető-e a Google Play Services az eszközön. Ezt a isGooglePlayServicesAvailable() metódus segítségével tehetjük meg, amely szükség esetén hibaüzenetet vagy dialógust jelenít meg a felhasználónak, és megakadályozza az alkalmazás folytatását, ha a szolgáltatás nem támogatott.

A Play Services jelenléte kritikus, mivel az egész GCM-folyamat erre épül. Ennek hiányában a token regisztráció, illetve az értesítések fogadása sem fog működni. Az isGooglePlayServicesAvailable() metódus ezért gyakorlatilag minden GCM-re épülő alkalmazásnál elengedhetetlen kellékké válik, különösen éles környezetben.

A GCM implementáció során fontos megérteni, hogy nem maga a fejlesztő küldi közvetlenül az értesítéseket, hanem a szerveren keresztül kommunikál a GCM backend-del. Ehhez a szerveroldalon is szükség van a megfelelő token tárolására és kezelésére. A kliens oldalon tehát a token megszerzése, mentése és továbbítása történik, míg a szerveroldal felelős a tényleges üzenetküldésért.

A Google által biztosított tesztalkalmazások és API referenciák nagy segítséget nyújtanak a fejlesztés során, különösen akkor, ha az alapértelmezett működéstől eltérő, testreszabott viselkedést szeretnénk beépíteni. Az https://developers.google.com/cloud-messaging/ oldalon további részletek találhatók a GCM kapcsolat működéséről, az értesítések formátumáról, valamint a szerveroldali integrációról is.

A GCM beállítása ugyan sokrétegűnek tűnhet, de az architektúrája rendkívül jól szeparált: az egyik réteg a token megszerzéséért felel, a másik a token frissítését figyeli, míg a harmadik az értesítéseket fogadja. Ennek az elválasztásnak köszönhetően az egyes komponensek tesztelhetők, módosíthatók, és újrafelhasználhatók maradnak, ami a modern Android fejlesztés egyik alappillére.

A fejlesztőnek tudnia kell, hogy a Google Cloud Messaging már nem fejlesztett technológia, és a Firebase Cloud Messaging (FCM) váltotta fel. Azonban az FCM visszafelé kompatibilis a GCM-mel, így a korábbi kódok egy része még működőképes lehet, de hosszú távon mindenképpen javasolt az áttérés az FCM-re.

A Google bejelentkezés integrációja egy másik olyan lépés, amely szintén a Google Services komponenseire épül. A google-services.json konfigurációs fájl, a Gradle beállítások és az autentikációs könyvtárak használata kulcsfontosságú. A Google fiókkal való bejelentkezés lehetővé teszi az alkalmazás számára, hogy hitelesített módon azonosítsa a felhasználót, valamint hozzáférjen más Google API-khoz is (pl. Google Drive, Gmail).

Fontos, hogy a fejlesztő tisztában legyen a SHA-1 aláírási kulccsal, a csomagnév egyediségével, valamint a Google Developer Console használatával. A szolgáltatás helyes működése nagymértékben függ ezek precíz beállításától, különösen, ha az alkalmazást éles környezetbe szánjuk, és a Play Áruházba töltjük fel.

A megfelelő struktúra, szolgáltatások elválasztása, Play Services ellenőrzés, Google API kulcsok pontos beállítása, valamint a szerveroldali infrastruktúra megléte elengedhetetlen az értesítési rendszer hibamentes működéséhez. A tapasztalt fejlesztők számára világos, hogy bár a kód rövidnek tűnik, a mögöttes logikai és infrastruktúra-feltételek összetettek – és ez az, ami megkülönbözteti a gyakorlati alkalmazást a csupán szintaktikailag helyes implementációtól.

Hogyan működik az Android aktivitásainak kezelése és elrendezések alkalmazása?

Az Android alkalmazások fejlesztésekor az egyik legfontosabb feladat az aktivitások és azok életciklusának kezelése. Egy aktivitás az a felhasználói interfész, amelyet az Android rendszer megjelenít, és amely lehetővé teszi a felhasználó számára, hogy interakcióba lépjen az alkalmazással. Az aktivitások különböző állapotokon mennek keresztül, például futás közben, szüneteltetett állapotban, vagy éppen leállítva, és minden egyes állapotváltás különböző metódusok hívásával történik.

A leggyakoribb metódusok, mint az onCreate(), onResume(), onPause() és onDestroy(), mindegyike kulcsszerepet játszik az aktivitás életciklusában. Az onCreate() metódus akkor hívódik meg, amikor az aktivitás először létrejön, és itt történik az összes szükséges inicializálás. Az onResume() akkor kerül végrehajtásra, amikor az aktivitás ismét előtérbe kerül, és az onPause() akkor fut le, amikor az aktivitás háttérbe kerül, de még nem áll le teljesen. Az onDestroy() metódus pedig akkor fut le, amikor az aktivitás véglegesen megszűnik.

Egy érdekes megfigyelés, hogy az onDestroy() metódus soha nem hoz létre valós látható változást, mivel az aktivitás már eltávolításra kerül a memóriából. Az isFinishing() metódus használata lehetőséget ad arra, hogy pontosan lássuk, hogy az aktivitás éppen bezárás előtt áll-e, vagy csak szüneteltetésre került. A következő kód például segít ezt ellenőrizni:

java
@Override public void onPause() { super.onPause(); mTextView.append("onPause()\n "); if (isFinishing()){ mTextView.append(" ... finishing"); } }

Fontos megjegyezni, hogy az aktivitások leállítása vagy bezárása nem feltétlenül történik az onPause() vagy onStop() metódusokban, hanem a rendszer a memóriakezelési mechanizmusai alapján bármikor eltávolíthatja őket, ha alacsony a memória vagy más alkalmazások igénylik azt.

Ha egy aktivitást le akarunk állítani, akkor a finish() metódust kell meghívni, ami önállóan végrehajtja az onDestroy() metódust. Ha egy gyermek aktivitásból szeretnénk bezárni egy szülő aktivitást, akkor a finishFromChild(Activity child) metódust használhatjuk. Ezenkívül hasznos lehet tudni, hogy az aktivitás valóban leáll-e vagy csak szüneteltetett állapotban van, amit az isFinishing() metódus segítségével ellenőrizhetünk.

Az Androidban az elrendezés (layout) alapvetően az az struktúra, amely meghatározza, hogyan jelennek meg a felhasználói felület elemei, mint például gombok, szövegdobozok, képek, és így tovább. Az elrendezés leggyakrabban XML fájlban kerül meghatározásra, és az egyik legjobb gyakorlat, ha az elrendezéseket XML-ben deklaráljuk, mivel így elválaszthatjuk a felhasználói felületet a működéstől. Az Android számos beépített elrendezést biztosít, mint például a RelativeLayout, LinearLayout, TableLayout, amelyek mindegyike különböző célokra használható. A ViewGroup a szülő osztályuk, amely lehetővé teszi a különböző nézetek elhelyezését egy elrendezésben.

A RelativeLayout lehetővé teszi, hogy a nézetek egymáshoz és a szülőhöz viszonyítva legyenek elhelyezve. Ez különösen hasznos, ha csökkenteni szeretnénk az egymásba ágyazott elrendezések számát, mivel a túl sok beágyazott elrendezés memória- és teljesítményproblémákat okozhat. A LinearLayout lehetővé teszi a nézetek függőleges vagy vízszintes rendezését, míg a TableLayout táblázatos elrendezést kínál, amely különösen akkor hasznos, ha rácsos elrendezésre van szükség.

A következő kód egy egyszerű példát mutat be a RelativeLayout használatára, ahol a nézetek közvetlenül a szülőhöz vagy egymáshoz viszonyítva helyezkednek el:

xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button 1" android:layout_centerInParent="true" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button 2" android:layout_below="@id/button1" android:layout_centerHorizontal="true"/> </RelativeLayout>

Ez az elrendezés két gombot helyez el, ahol az egyik gomb a szülő nézet közepén helyezkedik el, a második gomb pedig az első alatt jelenik meg.

Ha az elrendezést dinamikusan szeretnénk változtatni, akkor a setContentView() metódust kell használnunk, amely lehetővé teszi, hogy különböző elrendezéseket töltsünk be az aktivitás futásának különböző szakaszaiban. A következő példában két különböző elrendezést váltunk ki egy gombnyomásra:

java
public void onClickLeft(View view) {
setContentView(R.layout.activity_main2); } public void onClickRight(View view) { setContentView(R.layout.activity_main); }

Ez a kód egyszerűsíti az elrendezés kezelését, lehetővé téve a felhasználói felület dinamikus frissítését anélkül, hogy túlságosan bonyolult kódot kellene írni.

A legfontosabb, amit a fejlesztőknek szem előtt kell tartaniuk, hogy az Android elrendezéskezelése erősen összefonódik az alkalmazás teljesítményével. A túl sok beágyazott elrendezés vagy nem optimális elrendezési struktúra jelentős memóriahasználatot és lassulást eredményezhet, különösen alacsony teljesítményű eszközökön. Az elrendezések hatékony kezelése érdekében érdemes a Hierarchy Viewer-t használni, amely segít a felhasználói felület hierarchiájának optimalizálásában.