A startActivityForResult() metódus kiváló megoldást kínál, ha egy aktivitásból vissza kell küldenünk egy eredményt. A folyamat nem különbözik túlzottan attól, ahogyan előző példákban meghívtuk az aktivitásokat. Az alapvető lépés, hogy két aktivitással rendelkező projektet hozzunk létre, majd kódot adjunk hozzá az eredmények visszaadásához.

Az első lépés, hogy a MainActivity.java fájlban hozzáadjuk az alábbi konstansot a class-hoz:

java
public static final String REQUEST_RESULT = "REQUEST_RESULT";

Ezután módosítjuk az onClickSwitchActivity() metódust úgy, hogy az várjon egy eredményt:

java
public void onClickSwitchActivity(View view) { EditText editText = (EditText)findViewById(R.id.editTextData); String text = editText.getText().toString();
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra(Intent.EXTRA_TEXT, text); startActivityForResult(intent,
1); }

Most adjuk hozzá a szükséges kódot a MainActivity-ban, hogy megkaphassuk az eredményeket:

java
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { Toast.makeText(this, Integer.toString(data.getIntExtra(REQUEST_RESULT, 0)), Toast.LENGTH_LONG).show(); } }

A SecondActivity.java-ban módosítjuk az onClickClose() metódust, hogy az eredményt visszaadja:

java
public void onClickClose(View view) {
Intent returnIntent = new Intent();
returnIntent.putExtra(MainActivity.REQUEST_RESULT,
42); setResult(RESULT_OK, returnIntent); finish(); }

Ahogyan látható, az eredmények visszaadása viszonylag egyszerű. A startActivityForResult() metódus hívása azt jelzi a rendszer számára, hogy egy eredményt várunk. Az onActivityResult() metódus pedig a visszakapott eredményeket dolgozza fel. A második aktivitásnak vissza kell adnia az eredményt a setResult() metódussal, mielőtt bezárul. Az itt bemutatott példában csak egy statikus értéket adunk vissza, hogy illusztráljuk a fogalmat.

Fontos azonban figyelembe venni, hogy mindig ellenőrizzük az eredmény kódot, hogy megbizonyosodjunk arról, hogy a felhasználó nem törölte a műveletet. A rendszer a RESULT_OK vagy RESULT_CANCEL értékeket használja a művelet eredményének jelzésére. A példában a második aktivitás nem tartalmaz törlés gombot, de mi van akkor, ha a felhasználó a "vissza" gombot nyomja meg? Ilyenkor a rendszer RESULT_CANCEL értéket rendel a kódhoz, ami egy kivételt eredményezhet, ha nem kezeljük megfelelően.

Ezen kívül az onActivityResult() metódusnak van egy kiegészítő paramétere, a "Request Code", amely azonosítja a kérés forrását. Ez különösen hasznos lehet olyan alkalmazásoknál, ahol több aktivitás is eredményeket küldhet vissza. Ha a startActivityForResult() negatív kéréskóddal van hívva, akkor az nem fog visszaadni eredményt, és a rendszer úgy viselkedik, mintha startActivity() hívást végeznénk.

A Toast objektum használata praktikus eszközként szolgál, amely egyszerű üzenetet jelenít meg a felhasználónak anélkül, hogy zavaró lenne. A Toast hasznos a hibakereséshez is, mivel nem igényel különleges elrendezést vagy képernyőteret.

Az alkalmazások élettartama alatt az aktivitások állapotai folyamatosan változnak. A mobil környezet dinamikus, és a felhasználó gyakran váltogatja az alkalmazásokat. A rendszer szükség esetén leállíthatja az alkalmazásunkat, hogy több erőforrást biztosítson más folyamatok számára. Mindez része a mobil eszközök működésének, amely miatt elengedhetetlen, hogy alkalmazásunk képes legyen kezelni az ilyen típusú megszakításokat.

A legjobb felhasználói élmény érdekében érdemes úgy tervezni, hogy a felhasználó könnyedén folytathassa ott, ahol abbahagyta. Az Android operációs rendszer biztosítja a szükséges visszahívásokat, amelyek értesítést küldenek az alkalmazásunknak az állapotváltozásokról. A onSaveInstanceState() és onRestoreInstanceState() metódusok segítségével könnyen elmenthetjük és visszaállíthatjuk az alkalmazás állapotát.

A következő lépések segítenek az állapot kezelésében:

  1. Adjuk hozzá a szükséges globális változókat és kulcsokat a MainActivity.java fájlhoz:

java
static final String KEY_COUNTER = "COUNTER";
private int mCounter = 0;
  1. Módosítsuk a gombnyomás eseményt, hogy növeljük a számlálót, és frissítsük a TextView-t:

java
public void onClickCounter(View view) { mCounter++; ((TextView)findViewById(R.id.textViewCounter)).setText("Counter: " + Integer.toString(mCounter)); }
  1. Készítsük el az onSaveInstanceState() és onRestoreInstanceState() metódusokat, hogy az alkalmazás állapotát megőrizzük:

java
@Override
protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(KEY_COUNTER, mCounter); } @Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState); mCounter = savedInstanceState.getInt(KEY_COUNTER); }

Ezek a metódusok lehetővé teszik, hogy az alkalmazás megőrizze az adatokat akkor is, ha a felhasználó elhagyja az alkalmazást vagy az eszközt elforgatja.

Fontos megérteni, hogy a mobil környezetben az alkalmazások gyakran megállíthatók, és az erőforrások újraelosztása gyakori. Emiatt az alkalmazásoknak mindenképpen rendelkezniük kell olyan mechanizmusokkal, amelyek lehetővé teszik a folytatást anélkül, hogy a felhasználó munkája elveszne.

Hogyan kezeljük az ébresztőórákat és a rendszerindítást Android alkalmazásokban

Az Android alkalmazásokban az ébresztőórák kezelése és a rendszerindítási események kezelése kiemelt fontosságú lehet azok számára, akik háttérben futó alkalmazásokat vagy események kezelését kívánják megvalósítani. A következő leírás bemutatja, hogyan hozhatunk létre ébresztőórákat és hogyan érhetjük el az eszköz indítási eseményeit, biztosítva ezzel, hogy alkalmazásunk mindig megfelelően működjön a készülék újraindítása után.

Első lépésként hozzunk létre egy új projektet Android Studio-ban "Alarms" néven. Az alapértelmezett "Phone & Tablet" opciót válasszuk, és az "Empty Activity" lehetőséget válasszuk az Activity típusánál. Az alkalmazásunk felülete egyszerű lesz: egy gomb, amely beállítja az ébresztőórát.

A fő kihívás az, hogy egy Pending Intent-et kell létrehoznunk, amelyet az Android rendszer küld, amikor az ébresztőóra aktiválódik. A Pending Intent-nek köszönhetően a rendszer meghívja a kívánt műveletet, például egy értesítést, amikor az ébresztőóra elérkezik. Ehhez először létre kell hoznunk egy BroadcastReceiver-t, amely figyeli az ébresztőóra eseményét. Ez biztosítja, hogy a rendszer értesítést küldhessen, amikor az ébresztőóra elérkezik.

A következő lépés az Android Manifest fájl módosítása. Az alkalmazásunkban hozzá kell adnunk a megfelelő engedélyeket és a BroadcastReceiver deklarációját. Ezután a layout fájlban, az activity_main.xml-ben, kicseréljük az alapértelmezett TextView-t egy gombra, amely az ébresztőóra beállítását indítja el.

Ezután egy új Java osztályt kell létrehoznunk, amely a BroadcastReceiver-t valósítja meg. Az osztály feladata, hogy kezelje az ébresztőóra eseményt és értesítést küldjön a felhasználónak, például egy Toast üzenetet. A kód egy egyszerű példát tartalmaz, amely az ébresztőóra eseményt az ACTION_ALARM konstans értékére alapozza.

A gomb megnyomásakor az alkalmazásunk elindít egy új Intent-et, amely az AlarmBroadcastReceiver-t célozza meg. Az ébresztőóra időpontját a rendszer aktuális időpontjához képest 30 perccel késleltetve állítjuk be, és a Pending Intent-et beállítjuk az AlarmManager használatával. Az AlarmManager segítségével pontosan meghatározhatjuk, hogy mikor aktiválódjon az ébresztőóra.

Ha az ébresztőórát egyszerre többször próbáljuk beállítani, az operációs rendszer csak az utolsó beállítást veszi figyelembe, mivel mindegyik ugyanazt a Pending Intent-et használja. Amennyiben több különböző ébresztőórát szeretnénk, minden egyes ébresztőórához külön Pending Intent-et kell létrehoznunk, különböző akciókkal.

Ha szükség van az ébresztőóra törlésére, a cancel() metódust kell használni, amelyet ugyanazzal a Pending Intent-tel kell meghívni, amelyet az ébresztőóra létrehozásakor használtunk.

A rendszerindítási események kezelése szintén kulcsfontosságú lehet, különösen olyan alkalmazásoknál, amelyek folyamatos háttérfeladatokat végeznek. Az Android rendszer minden indításkor küld egy BOOT_COMPLETED eseményt, amelyet figyelni kell, hogy az alkalmazás újraindulás után is megfelelően működjön. Ehhez szükséges a RECEIVE_BOOT_COMPLETED engedély, és a BroadcastReceiver megfelelő beállítása. Amikor az eszköz elindul, a rendszer automatikusan elküldi a BOOT_COMPLETED akciót, amelyet a BroadcastReceiver figyelni tud.

Az indítási események kezelésére ugyanazt a BroadcastReceiver osztályt használhatjuk, amelyet az ébresztőóráknál alkalmaztunk, csupán az események típusát kell módosítani. Az alábbi példa bemutatja, hogyan kezelhetjük a BOOT_COMPLETED eseményt, és hogyan értesíthetjük a felhasználót, hogy az eszköz újraindult. A kód hasonlóan működik, mint az ébresztőóra kezelése, csak most a rendszerindításhoz szükséges eseményt figyeljük.

A rendszerindítást követően az alkalmazás újraindíthatja az ébresztőórákat, vagy bármilyen más háttérmunkát végezhet, amelyet az alkalmazásunk igényel. Fontos megjegyezni, hogy minden egyes akció kezeléséhez különböző kódot kell alkalmaznunk, így nem szükséges külön osztályokat létrehozni minden egyes eseményhez.

A következő lépés a folytatáshoz: ha alkalmazásunknak többször kell kezelnie különböző típusú háttérfeladatokat, fontos, hogy minden egyes eseményhez külön-külön kezeljük a Pending Intent-eket, és ne hagyjuk figyelmen kívül az operációs rendszer erőforrás-gazdálkodási irányelveit, különösen Android 4.4-es és újabb verziók esetében.

Miért fontos elkerülni a fő szál blokkolását az Android alkalmazásokban?

Az Android alkalmazások fő szála kulcsszerepet játszik a felhasználói élményben: ezen futnak az összes UI művelet, a válaszok és a felhasználói interakciók. Azonban a hosszú ideig tartó műveletek végrehajtása ezen a szálon jelentős problémákat okozhat. Ha egy alkalmazás nem reagál körülbelül 5 másodpercig, a rendszer hajlamos megjeleníteni az „Alkalmazás nem válaszol” (ANR) párbeszédablakot, amely lehetőséget ad a program leállítására. Az ilyen helyzetek elkerülése kritikus fontosságú, mivel az alkalmazás eltávolítása a felhasználói élmény romlásához vezethet.

Az Android alkalmazások egy szálú modellt használnak, amely két egyszerű szabályra épül. Az első szabály: ne blokkoljuk a fő szálat, a második pedig: minden UI műveletet a fő szálon kell végrehajtani. A fő szálat automatikusan létrehozza az Android rendszer, amikor az alkalmazást elindítjuk. Minden felhasználói felületet érintő műveletet ezen a szálon kell végezni. Ha hosszú ideig tartó vagy potenciálisan blokkoló műveletet kell végrehajtanunk, akkor azt másik szálon kell futtatni.

A háttérben történő műveletek elvégzésére számos Android-keretrendszert kínál, köztük az AsyncTask osztályt. Az AsyncTask célja, hogy egyszerű módot biztosítson hosszú ideig futó műveletek kezelésére anélkül, hogy közvetlenül blokkolnánk a fő szálat. Az AsyncTask három alapvető paramétert használ, amelyek lehetővé teszik a háttérmunkát, a haladás közvetítését, és az eredmények visszaküldését a felhasználói felületre. A szálkezelésben alkalmazott ilyen típusú absztrakciók elengedhetetlenek az Android fejlesztésében.

A fő szál blokkolása számos problémát okozhat. Az alkalmazás lassúvá válik, és ha egy hosszú művelet végrehajtása alatt nem kapunk választ, akkor a felhasználóval való interakció egy idő után lehetetlen lesz. Ez különösen igaz az olyan hálózati műveletekre, amelyek jelentős késleltetést okozhatnak. Ha nem használunk háttérszálat, a felhasználó számára úgy tűnik, hogy az alkalmazás nem válaszol, ami zűrzavart okozhat.

Még az AsyncTask használatakor is fontos figyelembe venni az Activity életciklusát. Az Activity például akkor is újraindulhat, ha a felhasználó elfordítja a készüléket, vagy más okból az alkalmazás újraindul. Az AsyncTask, mivel az Activity életciklusa alatt működik, könnyen problémákat okozhat, ha az Activity példány már megszűnt a háttérben futó szál visszaérkezése után. Ebben az esetben NullPointerException-t válthatunk ki, ha az AsyncTask megpróbál válaszolni az elpusztult Activity-re. A Fragmentek, mivel nem pusztulnak el képernyő forgatáskor, jobb választás lehet ilyen helyzetekben.

Az AsyncTask beépített előnye, hogy egyszerűsíti a hosszú műveletek kezelését anélkül, hogy azokat explicit háttérszálakon kellene kezelni. A doInBackground metódusban történik a háttérmunkák futtatása, míg az onPostExecute biztosítja, hogy a UI szálon végrehajtsuk a szükséges változtatásokat, mint például a gombok engedélyezése. Fontos, hogy mindig ügyeljünk arra, hogy az AsyncTask egyetlen példánya egyszerre csak egyszer futhat, így minden új végrehajtásnál új objektumot kell létrehozni.

A paraméterek, amelyeket az AsyncTask használ, különböző típusok, amelyeket figyelembe kell venni a fejlesztés során. A Params típus a doInBackground számára biztosít paramétereket, a Progress típus az előrehaladás közvetítésére, míg a Result típus az eredmény visszaadására szolgál. Ez a típusú paraméterezés lehetővé teszi, hogy rugalmasan és hatékonyan kezeljük a háttérben futó műveleteket, miközben biztosítjuk, hogy az UI szál megfelelően kezelje az eredményeket.

Fontos tisztában lenni az AsyncTask törlésének mechanizmusával is. Ha egy műveletet törölni akarunk, akkor az AsyncTask.cancel(true) metódust hívhatjuk meg. Ekkor a háttérszál megszakad, és a doInBackground-ban az isCancelled() metódus értéke igazra változik, amely lehetővé teszi a futó művelet leállítását. Ha a feladatot sikeresen töröljük, akkor az onCancelled() metódus kerül meghívásra, ahelyett, hogy az onPostExecute futna.

Ez a megközelítés biztosítja a felhasználói élmény stabilitását, miközben lehetővé teszi a fejlesztők számára, hogy egyszerűen és hatékonyan kezeljék a hosszú ideig futó műveleteket az Android alkalmazásaikban. Az AsyncTask tehát egy egyszerű, de erőteljes eszköz, amely segíthet abban, hogy alkalmazásunk gyors és responzív maradjon.