Szolgáltatások áttekintése

A Service egy olyan alkalmazáskomponens, amely hosszú ideig futó műveleteket végezhet a háttérben. Nem biztosít felhasználói felületet. Előfordulhat, hogy egy szolgáltatás egy ideig tovább fut, még akkor is, ha a felhasználó átvált egy másikalkalmazásra. Ezenkívül egy komponens kapcsolódhat egy szolgáltatáshoz, hogy kölcsönhatásba lépjen vele, és akár folyamatközi kommunikációt (IPC) is végezhet. Egy szolgáltatás például hálózati tranzakciókat kezelhet, zenét játszhat le, fájl I/O-t végezhet, vagy kölcsönhatásba léphet egy tartalomszolgáltatóval, mindezt a háttérből.

Vigyázat: A szolgáltatás a hosztingfolyamat főszálában fut; a szolgáltatás nem hoz létre saját szálat, és nem fut külön folyamatban, hacsak másképp nem adja meg. Az ApplicationNot Responding (ANR) hibák elkerülése érdekében minden blokkoló műveletet a szolgáltatáson belül külön szálon kell futtatni.

A szolgáltatások típusai

Ez a három különböző típusú szolgáltatás:

Foreground

A foreground szolgáltatás valamilyen, a felhasználó számára észrevehető műveletet végez. Például egy audioalkalmazás egy előtérben lévő szolgáltatást használna egy hangsáv lejátszására. Az előtérben lévő szolgáltatásoknak egy Értesítést kell megjeleníteniük. Az előtérben lévő szolgáltatások akkor is tovább futnak, amikor a felhasználó nem lép interakcióba az alkalmazással.

Az előtérben lévő szolgáltatás használatakor értesítést kell megjelenítenie, hogy a felhasználók aktívan tudjanak arról, hogy a szolgáltatás fut. Ez az értesítés csak akkor utasítható el, ha a szolgáltatást leállítják vagy eltávolítják az előtérből.

Tudjon meg többet arról, hogyan konfigurálhatja az előtérben lévő szolgáltatásokat az alkalmazásban.

Megjegyzés: A WorkManager API rugalmas módot kínál a feladatok ütemezésére, és szükség esetén képes ezeket a feladatokat előtérben lévő szolgáltatásként futtatni. Sok esetben a WorkManager használata előnyösebb, mint az előtérbe helyezett szolgáltatások közvetlen használata.

Háttér A háttérszolgáltatás olyan műveletet végez, amelyet a felhasználó közvetlenül nem vesz észre. Ha például egy alkalmazás egy szolgáltatást használna a tárhelyének tömörítésére, az általában háttérszolgáltatás lenne.

Megjegyzés: Ha az alkalmazás a 26-os vagy magasabb API-szintet célozza meg, a rendszer korlátozza a háttérszolgáltatások futtatását, amikor maga az alkalmazás nem az előtérben van. A legtöbb helyzetben például nem szabad a háttérből hozzáférni a helyadatokhoz. Ehelyett ütemezze a feladatokat a WorkManager segítségével.

Kötés Egy szolgáltatás akkor kötődik, ha egy alkalmazáskomponens abindService()hívásával kötődik hozzá. A kötött szolgáltatás olyan ügyfél-kiszolgáló interfészt kínál, amely lehetővé teszi a komponensek számára, hogy kölcsönhatásba lépjenek a szolgáltatással, kéréseket küldjenek, eredményeket fogadjanak, és mindezt akár folyamatok között, folyamatközi kommunikációval (IPC) tegyék. Egy kötött szolgáltatás csak addig fut, amíg egy másik alkalmazáskomponens hozzá van kötve. Egyszerre több komponens is kapcsolódhat a szolgáltatáshoz, de amikor mindannyian feloldják a kötést, a szolgáltatás megsemmisül.

Bár ez a dokumentáció általában külön tárgyalja az indított és a kötött szolgáltatásokat, a szolgáltatás mindkét módon működhet – lehet indított (határozatlan ideig futó) és megengedheti a kötést is. Ez egyszerűen annak a kérdése, hogy implementálsz-e néhány visszahívási metódust: onStartCommand() a komponensek indításának engedélyezéséhez és onBind() a kötés engedélyezéséhez.

Függetlenül attól, hogy a szolgáltatásod indított, kötött vagy mindkettő, bármelyik alkalmazáskomponens használhatja a szolgáltatást (akár egy külön alkalmazásból is) ugyanúgy, ahogy bármelyik komponens használhat egy tevékenységet – egy Intent indításával. A szolgáltatást azonban privátnak is nyilváníthatja a manifeszt fájlban, és blokkolhatja a hozzáférést más alkalmazások számára.Ezt bővebben a szolgáltatás deklarálása a manifesztben című részben tárgyaljuk.

Választás egy szolgáltatás és egy szál között

A szolgáltatás egyszerűen egy olyan komponens, amely a háttérben futhat, még akkor is, ha a felhasználó nem lép kapcsolatba az alkalmazással, ezért csak akkor hozzon létre szolgáltatást, ha erre van szükség.

Ha a főszálon kívül kell munkát végeznie, de csak akkor, amikor a felhasználó interakcióban van az alkalmazással, akkor inkább hozzon létre egy új szálat egy másik alkalmazáskomponens kontextusában. Ha például zenét akarsz lejátszani, de csak a tevékenységed futása közben,létrehozhatsz egy szálat a onCreate()-ban,elindíthatod a onStart()-ben,és leállíthatod a onStop()-ben.Fontolóra veheted a java.util.concurrent csomagból származó szálkészletek és végrehajtók vagy a Kotlin coroutines használatát is a hagyományosThread osztály helyett. A végrehajtás háttérszálakba való áthelyezéséről bővebben aThreading on Android dokumentumban olvashat.

Ne feledje, hogy ha szolgáltatást használ, az alapértelmezés szerint továbbra is az alkalmazás főszálában fut, ezért a szolgáltatáson belül új szálat kell létrehoznia, ha az intenzív vagy blokkoló műveleteket hajt végre.

Az alapok

A szolgáltatás létrehozásához a Service alosztályát kell létrehoznia, vagy valamelyik meglévő alosztályát kell használnia. Az implementációdban felül kell írnod néhány visszahívási metódust, amelyek a szolgáltatás életciklusának kulcsfontosságú aspektusait kezelik, és olyan mechanizmust kell biztosítanod, amely lehetővé teszi a komponensek számára, hogy adott esetben kapcsolódjanak a szolgáltatáshoz. Ezek a legfontosabb visszahívási metódusok, amelyeket felül kell írnia:

onStartCommand()A rendszer ezt a metódust hívja meg astartService()hívásával, amikor egy másik komponens (például egy tevékenység) kéri a szolgáltatás elindítását.Amikor ez a metódus végrehajtódik, a szolgáltatás elindul és a háttérben határozatlan ideig futhat. Ha ezt implementálod, a te felelősséged, hogy leállítsd a szolgáltatást, amikor a munkája befejeződött astopSelf()vagy astopService()hívásával. Ha csak kötést akarsz biztosítani, akkor nem kell implementálnod ezt a metódust.onBind()A rendszer ezt a metódust abindService()hívásával hívja meg, amikor egy másik komponens kötődni akar a szolgáltatáshoz (például RPC végrehajtásához).Ennek a metódusnak az implementációjában egyIBindervisszaadásával egy olyan interfészt kell biztosítania, amelyet a kliensek használnak a szolgáltatással való kommunikációhoz. Ezt a metódust mindig meg kell valósítania; ha azonban nem akarja engedélyezni a kötődést, akkor returnnull.onCreate()A rendszer ezt a metódust hívja meg, hogy egyszeri beállítási műveleteket hajtson végre a szolgáltatás kezdeti létrehozásakor (mielőtt meghívná aonStartCommand()vagy aonBind()metódust). Ha a szolgáltatás már fut, ezt a metódust nem hívja meg.onDestroy()A rendszer akkor hívja meg ezt a metódust, amikor a szolgáltatást már nem használják és megsemmisítik.A szolgáltatásnak ezt kell implementálnia, hogy megtisztítsa az erőforrásokat, például a szálakat, regisztrált hallgatókat vagy vevőket. Ez az utolsó hívás, amit a szolgáltatás kap.

Ha egy komponens elindítja a szolgáltatást a startService() hívásával (ami a onStartCommand() hívását eredményezi), a szolgáltatás addig fut, amíg le nem állítja magát a stopSelf() segítségével, vagy egy másik komponens le nem állítja a stopService() hívásával.

Ha egy komponens hívja abindService()-t a szolgáltatás létrehozásához, és a onStartCommand() nem kerül meghívásra, a szolgáltatás csak addig fut, amíg a komponens hozzá van kötve. Miután a szolgáltatás minden kliensétől megszabadul,a rendszer megsemmisíti.

Az Android rendszer csak akkor állítja le a szolgáltatást, ha a memória kevés, és vissza kell állítania a rendszer erőforrásait a felhasználó fókuszában lévő tevékenységhez. Ha a szolgáltatás olyan tevékenységhez van kötve, amely felhasználói fókusszal rendelkezik, akkor kisebb a valószínűsége, hogy megölik; ha a szolgáltatás úgy van deklarálva, hogy előtérben fut, akkor ritkán ölik meg.Ha a szolgáltatás elindul és hosszú ideig fut, a rendszer idővel csökkenti a pozícióját a háttérfeladatok listájában, és a szolgáltatás nagyon érzékeny lesz a leállításra – ha a szolgáltatása elindul, akkor úgy kell megterveznie, hogy méltóságteljesen kezelje a rendszer általi újraindításokat. Ha a rendszer megöli a szolgáltatást, újraindítja azt, amint az erőforrások elérhetővé válnak, de ez attól is függ, hogy milyen értéket ad vissza a onStartCommand(). További információt arról, hogy a rendszer mikor semmisíthet meg egy szolgáltatást, a Processes and Threadingdocumentben talál.

A következő szakaszokban megnézzük, hogyan hozhatjuk létre astartService() és abindService() szolgáltatás metódusokat, valamint hogyan használhatjuk őket más alkalmazáskomponensekből.

Szolgáltatás deklarálása a manifesztben

Minden szolgáltatást az alkalmazás manifeszt fájljában kell deklarálnia, ahogyan a tevékenységek és más komponensek esetében is.

A szolgáltatás deklarálásához adjon hozzá egy <service> elemet a <application>elem gyermekeként. Íme egy példa:

<manifest ... > ... <application ... > <service android:name=".ExampleService" /> ... </application></manifest>

A <service> elemre való hivatkozásban további információkat talál a szolgáltatás manifesztben történő deklarálásáról.

A <service> elemben egyéb attribútumokat is megadhat olyan tulajdonságok meghatározására, mint például a szolgáltatás indításához szükséges engedélyek és a folyamat, amelyben a szolgáltatásnak futnia kell. A android:nameattribútum az egyetlen kötelező attribútum – ez adja meg a szolgáltatás osztálynevét. Miután közzétette az alkalmazást, hagyja változatlanul ezt a nevet, hogy elkerülje a töréskód kockázatát a szolgáltatás indítására vagy kötésére vonatkozó explicit szándékoktól való függés miatt (olvassa el a ThingsThat Cannot Change című blogbejegyzést).

Vigyázat: Service indításakor mindig használjon explicit szándékot, és ne deklaráljon szándékszűrőket a szolgáltatásaihoz. Az implicit szándék használata egy szolgáltatás indításához biztonsági kockázatot jelent, mivel nem lehetünk biztosak abban, hogy melyik szolgáltatás válaszol a szándékra, és a felhasználó nem láthatja, hogy melyik szolgáltatás indul el. Az Android 5.0-tól kezdve (21. API-szint) a rendszer kivételt dob, ha implicit szándékkal hívja meg abindService()-t.

A android:exportedattribútum beillesztésével és a false beállításával biztosíthatja, hogy a szolgáltatása csak az alkalmazása számára legyen elérhető. Ez hatékonyan megakadályozza, hogy más alkalmazások elindítsák a szolgáltatásodat, még akkor is, ha explicit szándékot használsz.

Megjegyzés: A felhasználók láthatják, hogy milyen szolgáltatások futnak az eszközükön. Ha olyan szolgáltatást látnak, amelyet nem ismernek fel vagy nem bíznak meg benne, leállíthatják a szolgáltatást. Annak elkerülése érdekében, hogy a felhasználók véletlenül leállítsák a szolgáltatást, az alkalmazás manifesztjének <service> eleméhez hozzá kell adnia a android:description attribútumot. A leírásban adjon meg egy rövid mondatot, amelyben elmagyarázza, hogy mit csinál a szolgáltatás és milyen előnyöket nyújt.

Elindított szolgáltatás létrehozása

Az indított szolgáltatást egy másik komponens indítja el a startService() hívásával, ami a szolgáltatásonStartCommand() metódusának meghívását eredményezi.

A szolgáltatás indításakor a szolgáltatás életciklusa független az indító komponenstől. A szolgáltatás korlátlan ideig futhat a háttérben, még akkor is, ha az azt elindító komponens megsemmisül. Mint ilyen, a szolgáltatásnak le kell állítania magát, amikor a munkája befejeződik a stopSelf() hívásával, vagy egy másik komponens leállíthatja a stopService() hívásával.

Egy alkalmazáskomponens, például egy aktivitás elindíthatja a szolgáltatást a startService() hívásával és egy Intent átadásával, amely meghatározza a szolgáltatást és tartalmazza a szolgáltatás számára felhasználandó adatokat. A szolgáltatás ezt a Intent-t a onStartCommand() metódusban kapja meg.

Tegyük fel például, hogy egy aktivitásnak el kell mentenie néhány adatot egy online adatbázisba. Az aktivitás elindíthat egy társszolgáltatást, és átadhatja neki a mentendő adatokat egy szándék átadásával a startService()-nak. A szolgáltatás megkapja a szándékot a onStartCommand()-ben, csatlakozik az internethez, és végrehajtja az adatbázis tranzakciót. Amikor a tranzakció befejeződött, a szolgáltatás leállítja magát és megsemmisül.

Vigyázat: A szolgáltatás ugyanabban a folyamatban fut, mint az alkalmazás, amelyben deklarálták, és alapértelmezés szerint az alkalmazás fő szálában. Ha a szolgáltatás intenzív vagy blokkoló műveleteket végez, miközben a felhasználó ugyanannak az alkalmazásnak egy tevékenységével lép kapcsolatba, a szolgáltatás lelassítja a tevékenység teljesítményét. Az alkalmazás teljesítményének befolyásolásának elkerülése érdekében indítson új szálat a szolgáltatáson belül.

A Service osztály az összes szolgáltatás alaposztálya. Amikor ezt az osztályt bővíti, fontos, hogy hozzon létre egy új szálat, amelyben a szolgáltatás befejezheti az összes munkáját; a szolgáltatás alapértelmezés szerint az alkalmazás főszálát használja, ami lassíthatja az alkalmazásban futó bármely tevékenység teljesítményét.

Az Android keretrendszer biztosítja a Service IntentServicealosztályát is, amely egymunkásszálat használ az összes indítási kérelem egyenkénti kezelésére. Ennek az osztálynak a használata nemajánlott az új alkalmazások számára, mivel az Android 8 Oreo-tól kezdve nem fog jól működni, a Background végrehajtási korlátozások bevezetése miatt.Továbbá az Android 11-től kezdve elavult.Használhatja a JobIntentService-t a IntentService helyettesítésére, amely kompatibilis az Android újabb verzióival.

A következő szakaszok leírják, hogyan implementálhatja a saját egyéni szolgáltatását, azonban a legtöbb felhasználási esetre a WorkManager használata helyett inkább a WorkManager használatát érdemes megfontolnia. Olvassa el az Androidon történő háttérfeldolgozásról szóló útmutatót, hogy megnézze, van-e olyan megoldás, amely megfelel az igényeinek.

A Service osztály kiterjesztése

A Service osztályt kiterjesztheti az egyes bejövő szándékok kezelésére. Így nézhet ki egy alapvető megvalósítás:

Kotlin

class HelloService : Service() { private var serviceLooper: Looper? = null private var serviceHandler: ServiceHandler? = null // Handler that receives messages from the thread private inner class ServiceHandler(looper: Looper) : Handler(looper) { override fun handleMessage(msg: Message) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. try { Thread.sleep(5000) } catch (e: InterruptedException) { // Restore interrupt status. Thread.currentThread().interrupt() } // Stop the service using the startId, so that we don't stop // the service in the middle of handling another job stopSelf(msg.arg1) } } override fun onCreate() { // Start up the thread running the service. Note that we create a // separate thread because the service normally runs in the process's // main thread, which we don't want to block. We also make it // background priority so CPU-intensive work will not disrupt our UI. HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply { start() // Get the HandlerThread's Looper and use it for our Handler serviceLooper = looper serviceHandler = ServiceHandler(looper) } } override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show() // For each start request, send a message to start a job and deliver the // start ID so we know which request we're stopping when we finish the job serviceHandler?.obtainMessage()?.also { msg -> msg.arg1 = startId serviceHandler?.sendMessage(msg) } // If we get killed, after returning from here, restart return START_STICKY } override fun onBind(intent: Intent): IBinder? { // We don't provide binding, so return null return null } override fun onDestroy() { Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show() }}

Java

public class HelloService extends Service { private Looper serviceLooper; private ServiceHandler serviceHandler; // Handler that receives messages from the thread private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. try { Thread.sleep(5000); } catch (InterruptedException e) { // Restore interrupt status. Thread.currentThread().interrupt(); } // Stop the service using the startId, so that we don't stop // the service in the middle of handling another job stopSelf(msg.arg1); } } @Override public void onCreate() { // Start up the thread running the service. Note that we create a // separate thread because the service normally runs in the process's // main thread, which we don't want to block. We also make it // background priority so CPU-intensive work doesn't disrupt our UI. HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); // Get the HandlerThread's Looper and use it for our Handler serviceLooper = thread.getLooper(); serviceHandler = new ServiceHandler(serviceLooper); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); // For each start request, send a message to start a job and deliver the // start ID so we know which request we're stopping when we finish the job Message msg = serviceHandler.obtainMessage(); msg.arg1 = startId; serviceHandler.sendMessage(msg); // If we get killed, after returning from here, restart return START_STICKY; } @Override public IBinder onBind(Intent intent) { // We don't provide binding, so return null return null; } @Override public void onDestroy() { Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); }}

A példakód az összes bejövő hívást onStartCommand()ben kezeli, és a munkát egy háttérszálon futó Handler-be teszi át. Úgy működik, mint egy IntentService, és az összes kérést sorban, egymás után dolgozza fel.A kódot megváltoztathatjuk úgy, hogy a munkát például egy szálkészleten futtassuk, ha több kérést szeretnénk egyszerre futtatni.

Megjegyezzük, hogy a onStartCommand() metódusnak egy egész számot kell visszaadnia. Az egész szám egy olyan érték, amely leírja, hogy a rendszer hogyan folytassa a szolgáltatást abban az esetben, ha a rendszer megöli azt. A onStartCommand() visszatérési értékének a következő konstansok valamelyikének kell lennie:

START_NOT_STICKYHa a rendszer aonStartCommand()visszatérése után megöli a szolgáltatást, ne hozza létre újra a szolgáltatást, kivéve, ha vannak függőben lévőintentek, amelyeket kézbesíteni kell. Ez a legbiztonságosabb megoldás, hogy elkerülje a szolgáltatás futtatását, amikor nincs rá szükség, és amikor az alkalmazás egyszerűen újra tudja indítani a befejezetlen feladatokat.START_STICKYHa a rendszer megöli a szolgáltatást, miután aonStartCommand()visszatér, hozza létre újra a szolgáltatást és hívja meg aonStartCommand()-et, de ne adja le újra az utolsó szándékot.Ehelyett a rendszer aonStartCommand()-et egyull szándékkal hívja meg, hacsak nincsenek függőben lévő szándékok a szolgáltatás indítására. Ebben az esetben ezek a szándékok kézbesítésre kerülnek. Ez olyan médialejátszók (vagy hasonló szolgáltatások) számára alkalmas, amelyek nem hajtanak végre parancsokat, hanem határozatlan ideig futnak és várnak egy feladatra.START_REDELIVER_INTENTHa a rendszer aonStartCommand()visszatérése után megöli a szolgáltatást, hozza létre újra a szolgáltatást, és hívja meg aonStartCommand()-et az utolsó olyan szándékkal, amelyet a szolgáltatásnak kézbesítettek. Minden függőben lévő szándékot sorban kézbesít. Ez olyan szolgáltatásokhoz alkalmas, amelyek aktívan végeznek olyan feladatot, amelyet azonnal folytatni kell, például egy fájl letöltése.

A visszatérési értékekkel kapcsolatos további részletekért lásd az egyes konstansok hivatkozott dokumentációját.

Szolgáltatás indítása

Egy szolgáltatást egy tevékenységből vagy más alkalmazáskomponensből indíthat el egy Intent átadásával a startService() vagy startForegroundService() értékhez. Az Android rendszer meghívja a szolgáltatás onStartCommand() metódusát, és átadja neki a Intent-t, amely megadja, hogy melyik szolgáltatást kell elindítani.

Megjegyzés: Ha az alkalmazás a 26-os vagy magasabb API-szintet célozza meg, a rendszer korlátozásokat vezet be a háttérszolgáltatások használatára vagy létrehozására, kivéve, ha maga az alkalmazás van előtérben. Ha egy alkalmazásnak előtérben lévő szolgáltatást kell létrehoznia, akkor az alkalmazásnak a startForegroundService()-t kell hívnia. Ez a módszer egy háttérszolgáltatást hoz létre, de a módszer jelzi a rendszernek, hogy a szolgáltatás előtérbe kerül. A szolgáltatás létrehozása után a szolgáltatásnak öt másodpercen belül meg kell hívnia a startForeground() metódusát.

Egy aktivitás például az előző részben (HelloService) bemutatott példaszolgáltatást a startService() explicit szándékkal indíthatja el, ahogy itt látható:

Kotlin

Intent(this, HelloService::class.java).also { intent -> startService(intent)}

Java

Intent intent = new Intent(this, HelloService.class);startService(intent);

A startService() metódus azonnal visszatér, ésaz Android rendszer meghívja a szolgáltatás onStartCommand() metódusát. Ha a szolgáltatás még nem fut, a rendszer először a onCreate() metódust hívja meg, majd a onStartCommand() metódust.

Ha a szolgáltatás nem biztosít kötést is, a startService() metódussal átadott szándék az egyetlen kommunikációs mód az alkalmazáskomponens és a szolgáltatás között. Ha azonban azt szeretnénk, hogy a szolgáltatás visszaküldjön egy eredményt,a szolgáltatást indító kliens létrehozhat egy PendingIntent-et egy broadcast-hez (getBroadcast()-vel), és átadhatja azt a szolgáltatásnak a Intent-ben, amely elindítja a szolgáltatást. A szolgáltatás ezután használhatja a broadcastot az eredmény kézbesítésére.

A szolgáltatás indítására irányuló több kérés több megfelelő hívást eredményez a szolgáltatásonStartCommand()-hez. A szolgáltatás leállításához azonban csak egy kérés szükséges (a stopSelf() vagy stopService() segítségével).

Szolgáltatás leállítása

Az elindított szolgáltatásnak saját életciklusát kell kezelnie. Ez azt jelenti, hogy a rendszer nem állítja le vagy semmisíti meg a szolgáltatást, kivéve, ha vissza kell állítania a rendszermemóriát, és a szolgáltatás a onStartCommand() visszatérése után is tovább fut. A szolgáltatásnak magát kell leállítania a stopSelf() hívásával, vagy egy másik komponens leállíthatja a stopService() hívásával.

Amikor a stopSelf() vagy stopService() segítségével leállításra kérik, a rendszer a lehető leghamarabb megsemmisíti a szolgáltatást.

Ha a szolgáltatás egyszerre több kérést kezel a onStartCommand()-hez, nem szabad leállítani a szolgáltatást, amikor befejezte egy indítási kérés feldolgozását, mivel lehet, hogy új indítási kérést kapott (az első kérés végén történő leállítás a második kérést is megszakítaná). E probléma elkerülése érdekében a stopSelf(int) használatával biztosíthatja, hogy a szolgáltatás leállítására irányuló kérése mindig a legutóbbi indítási kérésen alapuljon. Ez azt jelenti, hogy a stopSelf(int) hívásakor átadja annak az indítási kérelemnek az azonosítóját (a onStartCommand()-nek átadott startId), amelynek a leállítási kérelem megfelel. Ezután, ha a szolgáltatás új indítási kérést kap, mielőtt Ön képes lenne a stopSelf(int) hívására, az azonosító nem egyezik, és a szolgáltatás nem áll le.

Vigyázat: Ha szükséges, más komponensek leállíthatják a szolgáltatást a stopService() hívásával. Még ha engedélyezi is a szolgáltatás kötését,mindig magának kell leállítania a szolgáltatást, ha valaha is hívást kap a onStartCommand()-re.

A szolgáltatás életciklusával kapcsolatos további információkért lásd a szolgáltatás életciklusának kezelése című részt.

Kötött szolgáltatás létrehozása

A kötött szolgáltatás olyan szolgáltatás, amely lehetővé teszi az alkalmazáskomponensek számára, hogy a bindService() hívásával kötődjenek hozzá egy hosszú távú kapcsolat létrehozásához.Általában nem teszi lehetővé a komponensek számára, hogy a startService() hívásával indítsák el.

Kötött szolgáltatást akkor hozhat létre, ha a szolgáltatással a tevékenységekből és az alkalmazás más komponenseiből szeretne interakcióba lépni, vagy ha az alkalmazás bizonyos funkcióit más alkalmazások számára szeretné felfedni folyamatközi kommunikáció (IPC) révén.

A kötött szolgáltatás létrehozásához implementálja a onBind() visszahívási metódust, hogy visszaadjon egy IBinder-et, amely meghatározza a szolgáltatással való kommunikáció interfészét. Más alkalmazáskomponensek ezután meghívhatjákbindService() az interfész lekérdezésére és a szolgáltatás metódusainak meghívására. A szolgáltatás csak a hozzá kötött alkalmazáskomponens kiszolgálásáig él, így amikor már nincsenek a szolgáltatáshoz kötött komponensek, a rendszer megsemmisíti azt.A kötött szolgáltatást nem kell ugyanúgy leállítani, mint a szolgáltatás onStartCommand() segítségével történő indításakor.

A kötött szolgáltatás létrehozásához meg kell határoznia azt az interfészt, amely meghatározza, hogy egy ügyfél hogyan kommunikálhat a szolgáltatással. Ennek a szolgáltatás és az ügyfél közötti interfésznek a IBinder implementációjának kell lennie, és ez az, amit a szolgáltatásnak vissza kell adnia a onBind() visszahívási metódusból. Miután az ügyfél megkapja a IBinder-t, elkezdhet interakcióba lépni a szolgáltatással ezen az interfészen keresztül.

Multi kliensek egyszerre többszörösen is kapcsolódhatnak a szolgáltatáshoz. Amikor egy kliens befejezte az interakciót a szolgáltatással, a unbindService() hívást hívja, hogy feloldja a kötést.

Ha már nincs a szolgáltatáshoz kötött kliens, a rendszer megsemmisíti a szolgáltatást.

Egy kötött szolgáltatást többféleképpen lehet megvalósítani, és a megvalósítás bonyolultabb, mint egy indított szolgáltatásé. Ezen okok miatt a kötött szolgáltatások tárgyalása a Kötött szolgáltatásokról szóló külön dokumentumban jelenik meg.

Értesítések küldése a felhasználónak

Mikor egy szolgáltatás fut, értesítheti a felhasználót az eseményekről Toast értesítések vagy állapotsori értesítések segítségével.

A toast értesítés olyan üzenet, amely csak egy pillanatra jelenik meg az aktuális ablak felületén, majd eltűnik. Az állapotsor-értesítés egy ikont jelenít meg az állapotsorban egy üzenettel, amelyet a felhasználó kiválaszthat egy művelet elvégzéséhez (például egy tevékenység elindításához).

Az állapotsor-értesítés általában akkor a legjobb technika, ha a háttérben zajló munka, például egy fájl letöltése befejeződött, és a felhasználó most már cselekedhet. Amikor a felhasználókiválasztja az értesítést a kibővített nézetben, az értesítés elindíthat egy tevékenységet(például a letöltött fájl megjelenítését).

Bővebb információért lásd a Toast Notifications vagy Status Bar Notificationsdeveloper guides.

Managing the lifecycle of a service

A szolgáltatás életciklusa sokkal egyszerűbb, mint egy tevékenységé. Azonban még fontosabb, hogy nagy figyelmet fordítson a szolgáltatás létrehozásának és megsemmisítésének módjára, mivel a szolgáltatás a felhasználó tudta nélkül futhat a háttérben.

A szolgáltatás életciklusa – a létrehozásától a megsemmisítéséig – az alábbi két útvonal egyikét követheti:

  • Elindított szolgáltatás

    A szolgáltatás akkor jön létre, amikor egy másik komponens hívja startService(). A szolgáltatás ezután határozatlan ideig fut, és a stopSelf() hívásával kell leállítania magát. Egy másik komponens is leállíthatja a szolgáltatást a stopService() hívásával. A szolgáltatás leállításakor a rendszer megsemmisíti azt.

  • Kötött szolgáltatás

    A szolgáltatás akkor jön létre, amikor egy másik komponens (egy ügyfél) meghívja a bindService()-t. Az ügyfél ezután egy IBinder interfészen keresztül kommunikál a szolgáltatással. Az ügyfél a kapcsolatot aunbindService() hívásával zárhatja le. Több kliens is kapcsolódhat ugyanahhoz a szolgáltatáshoz, és amikor mindegyikük feloldja a kapcsolódást, a rendszer megsemmisíti a szolgáltatást. A szerviznek nem kell magát leállítania.

Ez a két út nem teljesen különálló. A startService() segítségével már elindított szolgáltatáshoz is lehet kötődni. Például elindíthat egy háttérzene szolgáltatást a startService() meghívásával, a lejátszandó zenét azonosító Intent-vel. Később, amikor a felhasználó esetleg a lejátszó felett akar valamilyen irányítást gyakorolni, vagy információt szeretne kapni az aktuális zeneszámról, egy aktivitás a bindService() hívásával kapcsolódhat a szolgáltatáshoz. Ilyen esetekben a stopService() vagy stopSelf() valójában nem állítja le a szolgáltatást, amíg az összes kliens fel nem oldja a kötődést.

Az életciklus-visszahívások megvalósítása

Az aktivitáshoz hasonlóan a szolgáltatásnak is vannak életciklus-visszahívási metódusai, amelyeket implementálhatunk a szolgáltatás állapotában bekövetkező változások megfigyelésére és a megfelelő időpontokban történő munkavégzésre. A következő skeletonservice bemutatja az egyes életciklus-módszereket:

Kotlin

class ExampleService : Service() { private var startMode: Int = 0 // indicates how to behave if the service is killed private var binder: IBinder? = null // interface for clients that bind private var allowRebind: Boolean = false // indicates whether onRebind should be used override fun () { // The service is being created } override fun (intent: Intent?, flags: Int, startId: Int): Int { // The service is starting, due to a call to startService() return startMode } override fun (intent: Intent): IBinder? { // A client is binding to the service with bindService() return binder } override fun (intent: Intent): Boolean { // All clients have unbound with unbindService() return allowRebind } override fun (intent: Intent) { // A client is binding to the service with bindService(), // after onUnbind() has already been called } override fun () { // The service is no longer used and is being destroyed }}

Java

public class ExampleService extends Service { int startMode; // indicates how to behave if the service is killed IBinder binder; // interface for clients that bind boolean allowRebind; // indicates whether onRebind should be used @Override public void () { // The service is being created } @Override public int (Intent intent, int flags, int startId) { // The service is starting, due to a call to  return startMode; } @Override public IBinder (Intent intent) { // A client is binding to the service with  return binder; } @Override public boolean (Intent intent) { // All clients have unbound with  return allowRebind; } @Override public void (Intent intent) { // A client is binding to the service with , // after onUnbind() has already been called } @Override public void () { // The service is no longer used and is being destroyed }}

Figyelem: A tevékenység életciklus-visszahívási metódusaitól eltérően nem szükséges meghívni ezen visszahívási metódusok szuperosztálybeli implementációját.

2. ábra. A szolgáltatás életciklusa. A bal oldali diagram az életciklust mutatja, amikor a szolgáltatást a startService(), a jobb oldali diagram pedig az életciklust, amikor a szolgáltatást a bindService() segítségével hozzuk létre.

A 2. ábra egy szolgáltatás tipikus visszahívási metódusait szemlélteti. Bár az ábra elkülöníti a startService() által létrehozott szolgáltatásokat a bindService() által létrehozottaktól, ne feledjük, hogy bármely szolgáltatás, függetlenül attól, hogy hogyan indították el, potenciálisan lehetővé teheti az ügyfelek számára a hozzá való kötődést.Egy szolgáltatás, amelyet eredetileg a onStartCommand() segítségével indítottak el (egy startService()-ot hívó ügyfél által)még mindig kaphat hívást a onBind()-re (amikor egy ügyfél hívja a bindService()-t).

Ezeknek a metódusoknak az implementálásával a szolgáltatás életciklusának ezt a két egymásba ágyazott ciklusát figyelhetjük:

  • A szolgáltatás teljes élettartama a onCreate() meghívása és a onDestroy() visszatérése között zajlik. Mint egy aktivitás, a szolgáltatás is onCreate()-ban végzi el a kezdeti beállításokat, és onDestroy()-ben szabadítja fel az összes fennmaradó erőforrást. Például egy zenelejátszó szolgáltatás a onCreate()-ban létrehozhatja a szálat, ahol a zene lejátszása történik, majd a onDestroy()-ben leállíthatja a szálat.

    Megjegyzés: A onCreate() és onDestroy() metódusokat minden szolgáltatás hívja meg, függetlenül attól, hogy a startService() vagy a bindService() hozta létre őket.

  • A szolgáltatás aktív élettartama a onStartCommand() vagy a onBind() hívásával kezdődik.Minden metódus megkapja a Intent-t, amelyet vagy a startService()-nak, vagy a bindService()-nak adtunk át.

    Ha a szolgáltatás elindul, az aktív élettartam a teljes élettartam végével egy időben ér véget (a szolgáltatás a onStartCommand() visszatérése után is aktív marad). Ha a szolgáltatás le van kötve, az aktív élettartam akkor ér véget, amikor onUnbind() visszatér.

Megjegyzés: Bár egy indított szolgáltatás a stopSelf() vagy stopService() hívásával áll le, nincs megfelelő visszahívás a szolgáltatáshoz (nincs onStop() visszahívás). Hacsak a szolgáltatás nincs ügyfélhez kötve,a rendszer a szolgáltatás leállításakor megsemmisíti azt – a onDestroy() az egyetlen kapott visszahívás.

A kötést biztosító szolgáltatás létrehozásával kapcsolatos további információkért lásd a Kötött szolgáltatások dokumentumot,amely a onRebind()visszahívás módszerrel kapcsolatos további információkat tartalmaz a Kötött szolgáltatás életciklusának kezelése című részben.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.