Palveluiden yleiskatsaus

Service on sovelluskomponentti, joka voi suorittaa pitkäkestoisia toimintoja taustalla. Se ei tarjoa käyttöliittymää. Alkutilanteessa palvelu saattaa jatkua jonkin aikaa, vaikka käyttäjä vaihtaisi toiseen sovellukseen. Lisäksi komponentti voi sitoutua palveluun ollakseen vuorovaikutuksessa sen kanssa ja jopa suorittaa prosessienvälistä viestintää (IPC). Palvelu voi esimerkiksi käsitellä verkkotapahtumia, soittaa musiikkia, suorittaa tiedostojen I/O:ta tai olla vuorovaikutuksessa sisällöntarjoajan kanssa, kaikki taustalla.

Varoitus: Palvelu suoritetaan sen isäntäprosessin pääsäikeessä; palvelu ei luo omaa säiettä eikä toimi erillisessä prosessissa, ellet toisin määritä. Kaikki estävät toiminnot kannattaa suorittaa palvelussa erillisessä säikeessä, jotta vältetään ApplicationNot Responding (ANR) -virheet.

Palvelutyypit

Näissä on kolme erilaista palvelutyyppiä:

Etualalla

Etualalla oleva palvelu suorittaa jonkin toiminnon, joka on käyttäjälle havaittavissa. Esimerkiksi audiosovellus käyttäisi etualapalvelua ääniraidan toistamiseen. Foreground-palveluiden on näytettävä Ilmoitus. Foreground-palvelut jatkavat suorittamista, vaikka käyttäjä ei olisi vuorovaikutuksessa sovelluksen kanssa.

Kun käytät etualalla olevaa palvelua, sinun on näytettävä ilmoitus, jotta käyttäjät ovat aktiivisesti tietoisia siitä, että palvelu on käynnissä. Tätä ilmoitusta ei voi hylätä, ellei palvelua joko pysäytetä tai poisteta etualalla olevasta palvelusta.

Lue lisää siitä, miten etualalla olevat palvelut määritetään sovelluksessasi.

Huomautus: WorkManager API tarjoaa joustavan tavan aikatauluttaa tehtäviä, ja se pystyy tarvittaessa suorittamaan näitä tehtäviä etualalla olevina palveluina. Monissa tapauksissa WorkManagerin käyttäminen on parempi vaihtoehto kuin etualapalveluiden käyttäminen suoraan.

Tausta Taustapalvelu suorittaa toiminnon, jota käyttäjä ei suoraan huomaa. Jos sovellus esimerkiksi käyttäisi palvelua tallennustilan tiivistämiseen, se olisi yleensä taustapalvelu.

Huomautus: Jos sovelluksesi tähtää API-tasolle 26 tai korkeammalle, järjestelmä asettaa rajoituksia taustapalveluiden suorittamiselle silloin, kun itse sovellus ei ole etualalla. Useimmissa tilanteissa esimerkiksi sijaintitietoja ei pitäisi käyttää taustapalvelusta. Aikatauluta sen sijaan tehtäviä WorkManagerin avulla.

Sidottu Palvelu on sidottu, kun sovelluskomponentti sitoutuu siihen kutsumallabindService(). Sidottu palvelu tarjoaa asiakas-palvelinrajapinnan, jonka avulla komponentit voivat olla vuorovaikutuksessa palvelun kanssa, lähettää pyyntöjä, vastaanottaa tuloksia ja jopa tehdä sen prosessien välillä prosessien välisellä kommunikaatiolla (IPC). Sidottu palvelu toimii vain niin kauan kuin toinen sovelluskomponentti on sidottu siihen. Useat komponentit voivat sitoutua palveluun samanaikaisesti, mutta kun kaikki ne irrottautuvat palvelusta, palvelu tuhoutuu.

Vaikka tässä dokumentaatiossa käsitellään käynnistettyjä ja sidottuja palveluita yleensä erikseen, palvelusi voi toimia molemmilla tavoilla – se voi olla käynnistetty (toimia loputtomasti) ja sallia myös sitomisen. Kyse on vain siitä, toteutatko pari callback-metodia: onStartCommand(), jotta komponentit voivat käynnistää sen, ja onBind(), jotta sitominen sallitaan.

Riippumatta siitä, onko palvelusi käynnistetty, sidottu vai molempia, mikä tahansa sovelluskomponentti voi käyttää palvelua (jopa erillisestä sovelluksesta) samalla tavalla kuin mikä tahansa komponentti voi käyttää aktiviteettia käynnistämällä sen Intent:llä. Voit kuitenkin ilmoittaa palvelun yksityiseksi manifestitiedostossa ja estää sen käytön muilta sovelluksilta.Tätä käsitellään tarkemmin luvussa Palvelun ilmoittaminen manifestissa.

Valinta palvelun ja säikeen välillä

Palvelu on yksinkertaisesti komponentti, joka voi toimia taustalla, vaikka käyttäjä ei olisikaan vuorovaikutuksessa sovelluksesi kanssa, joten palvelu kannattaa luoda vain, jos sitä tarvitaan.

Jos sinun on suoritettava työtä pääsäikeen ulkopuolella, mutta vain silloin, kun käyttäjä on vuorovaikutuksessa sovelluksesi kanssa, sinun pitäisi sen sijaan luoda uusi säie toisen sovelluskomponentin yhteyteen. Jos esimerkiksi haluat soittaa musiikkia, mutta vain toimintosi ollessa käynnissä,voit luoda säikeen onCreate():ssä,aloittaa sen suorittamisen onStart():ssä ja pysäyttää sen onStop():ssä.Harkitse myös säiepoolien ja toteuttajien käyttämistä java.util.concurrent-paketista tai Kotlin-korutiineja perinteisenThread-luokan sijasta. Katso lisätietoja suorituksen siirtämisestä taustasäikeisiinThreading on Android -dokumentista.

Muista, että jos käytät palvelua, se suoritetaan silti oletusarvoisesti sovelluksen pääsäikeessä, joten sinun pitäisi silti luoda uusi säie palvelun sisälle, jos se suorittaa intensiivisiä tai estäviä operaatioita.

Perusasiat

Luodaksesi palvelun sinun on luotava Service:n aliluokka tai sinun on käytettävä jotakin Service:n olemassa olevista aliluokista. Toteutuksessasi sinun on ylikirjoitettava joitakin takaisinkutsumenetelmiä, jotka käsittelevät palvelun elinkaaren keskeisiä osa-alueita, ja tarjottava mekanismi, jonka avulla komponentit voivat tarvittaessa sitoutua palveluun. Nämä ovat tärkeimmät callback-metodit, jotka sinun tulisi ohittaa:

onStartCommand()Järjestelmä kutsuu tätä metodia kutsumallastartService(), kun jokin toinen komponentti (esimerkiksi aktiviteetti) pyytää, että palvelu käynnistetään.Kun tämä metodi suoritetaan, palvelu käynnistetään ja se voi toimia taustalla loputtomiin. Jos toteutat tämän metodin, on sinun vastuullasi pysäyttää palvelu, kun sen työ on valmis kutsumalla

stopSelf()taistopService(). Jos haluat vain tarjota sitomista, sinun ei tarvitse toteuttaa tätä metodia.onBind()Järjestelmä kutsuu tätä metodia kutsumallabindService(), kun toinen komponentti haluaa sitoutua palveluun (esimerkiksi suorittaa RPC:n).Tämän metodin toteutuksessa sinun on tarjottava rajapinta, jota asiakkaat käyttävät kommunikoidakseen palvelun kanssa palauttamallaIBinder. Sinun on aina toteutettava tämä metodi; jos et kuitenkaan halua sallia sitoutumista, sinun on palautettavanull.onCreate()Järjestelmä kutsuu tätä metodia suorittaakseen kertaluonteiset asetustoimenpiteet, kun palvelu luodaan ensimmäisen kerran (ennen kuin se kutsuu jokoonStartCommand()taionBind()). Jos palvelu on jo käynnissä, tätä metodia ei kutsuta.onDestroy()Järjestelmä kutsuu tätä metodia, kun palvelua ei enää käytetä ja se tuhotaan.Palvelun tulisi toteuttaa tämä metodi, jotta kaikki resurssit, kuten säikeet, rekisteröidyt kuuntelijat tai vastaanottimet, voidaan siivota. Tämä on viimeinen kutsu, jonka palvelu saa.

Jos komponentti käynnistää palvelun kutsumalla startService() (joka johtaa kutsuun onStartCommand()), palvelu jatkaa suoritustaan, kunnes se pysäyttää itsensä stopSelf():llä tai jokin toinen komponentti pysäyttää sen kutsumalla stopService().

Jos komponentti kutsuubindService() luodakseen palvelun ja onStartCommand():tä ei kutsuta, palvelu pyöriisonly niin kauan kuin komponentti on sidottu siihen. Kun palvelu on irrotettu kaikista asiakkaistaan,järjestelmä tuhoaa sen.

Android-järjestelmä pysäyttää palvelun vain silloin, kun muistin määrä on vähissä ja sen on palautettava järjestelmäresursseja toiminnolle, johon käyttäjä keskittyy. Jos palvelu on sidottu aktiviteettiin, jolla on käyttäjän fokus, on epätodennäköisempää, että se lopetetaan; jos palvelu on ilmoitettu suoritettavaksi etualalla, se lopetetaan harvoin.Jos palvelu käynnistetään ja se toimii pitkään, järjestelmä laskee sen asemaa taustatehtävien luettelossa ajan mittaan, ja palvelusta tulee erittäin altis lopetuksille – jos palvelusi käynnistetään, sinun on suunniteltava se siten, että se pystyy käsittelemään järjestelmän tekemät uudelleenkäynnistykset joustavasti. Jos järjestelmä tappaa palvelusi, se käynnistää sen uudelleen heti, kun resursseja on käytettävissä, mutta tämä riippuu myös onStartCommand():n palauttamasta arvosta. Lisätietoja siitä, milloin järjestelmä saattaa tuhota palvelun, on Processes and Threadingdocumentissa.

Seuraavissa kappaleissa näet, miten voit luoda startService()– ja bindService()-palvelumetodit sekä miten niitä käytetään muista sovelluskomponenteista.

Palvelun ilmoittaminen manifestissa

Kaikki palvelut on ilmoitettava sovelluksen manifestitiedostossa, aivan kuten aktiviteettien ja muiden komponenttien kohdalla.

Palvelun ilmoittamiseksi lisää <service>-elementti <application>elementin lapseksi. Tässä on esimerkki:

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

Katso <service>-elementinviittauksesta lisätietoja palvelun ilmoittamisesta manifestissa.

On muitakin attribuutteja, joita voit sisällyttää <service>-elementtiin määrittelemään ominaisuuksia, kuten palvelun käynnistämiseen vaadittavat käyttöoikeudet ja prosessin, jossa palvelun tulisi toimia. android:name-attribuutti on ainoa pakollinen attribuutti – se määrittää palvelun luokan nimen. Kun olet julkaissut sovelluksesi, jätä tämä nimi muuttumattomana, jotta vältät koodin rikkoutumisen riskin, joka johtuu riippuvuudesta nimenomaisista aikomuksista käynnistää tai sitoa palvelu (lue blogikirjoitus ThingsThat Cannot Change).

Caution: Varmistaaksesi, että sovelluksesi on turvallinen, käytä aina eksplisiittistä aikomusta, kun käynnistät Service, äläkä ilmoita aikomussuodattimia palveluillesi. Epäsuoran aikomuksen käyttäminen palvelun käynnistämiseen on turvallisuusriski, koska et voi olla varma palvelusta, joka vastaa aikomukseen, eikä käyttäjä näe, mikä palvelu käynnistyy. Android 5.0:sta alkaen (API-taso 21) järjestelmä heittää poikkeuksen, jos kutsutbindService() implisiittisellä aikomuksella.

Voit varmistaa, että palvelusi on vain sovelluksesi käytettävissä sisällyttämällä android:exportedattribuutin ja asettamalla sen arvoksi false. Tämä estää tehokkaasti muita sovelluksia käynnistämästä palveluasi, vaikka käyttäisitkin eksplisiittistä aikomusta.

Huomaa: Käyttäjät näkevät, mitä palveluita heidän laitteessaan on käynnissä. Jos he näkevät palvelun, jota he eivät tunnista tai johon he eivät luota, he voivat pysäyttää palvelun. Jotta vältät sen, että käyttäjät pysäyttävät palvelusi vahingossa, sinun on lisättävä android:description-attribuutti sovelluksen manifestin <service>-elementtiin. Kirjoita kuvaukseen lyhyt lause, jossa selität, mitä palvelu tekee ja mitä etuja se tarjoaa.

Käynnistetyn palvelun luominen

Käynnistetty palvelu on palvelu, jonka jokin toinen komponentti käynnistää kutsumalla startService(), mikä johtaa kutsuun palvelunonStartCommand()-metodiin.

Kun palvelu käynnistetään, sillä on elinkaari, joka on riippumaton sen käynnistäneestä komponentista. Palvelu voi toimia taustalla loputtomiin, vaikka sen käynnistänyt komponentti tuhoutuisi. Näin ollen palvelun tulisi pysäyttää itsensä, kun sen tehtävä on valmis kutsumalla stopSelf(), tai jokin toinen komponentti voi pysäyttää sen kutsumalla stopService().

Sovelluskomponentti, kuten aktiviteetti, voi käynnistää palvelun kutsumalla startService() ja välittämällä Intent, joka määrittelee palvelun ja sisältää mahdolliset tiedot, joita palvelu voi käyttää. Palvelu vastaanottaa tämän Intent metodissa onStartCommand().

Esitetään esimerkiksi, että aktiviteetin on tallennettava joitakin tietoja verkkotietokantaan. Aktiivisuusvoi käynnistää liitännäispalvelun ja toimittaa sille tallennettavat tiedot välittämällä aikomuksen startService(). Palvelu vastaanottaa aikomuksen onStartCommand(), muodostaa yhteyden Internetiin ja suorittaa tietokantatapahtuman. Kun tapahtuma on valmis, palvelu pysähtyy ja tuhoutuu.

Varoitus: Palvelu suoritetaan oletusarvoisesti samassa prosessissa kuin sovellus, jossa se on ilmoitettu, ja kyseisen sovelluksen pääsäikeessä. Jos palvelu suorittaa intensiivisiä tai estäviä toimintoja, kun käyttäjä on vuorovaikutuksessa saman sovelluksen toiminnon kanssa, palvelu hidastaa toiminnon suorituskykyä. Välttääksesi vaikutuksen sovelluksen suorituskykyyn, käynnistä uusi säie palvelun sisällä.

Luokka Service on kaikkien palveluiden perusluokka. Kun laajennat tätä luokkaa, on tärkeää luoda uusi säie, jossa palvelu voi suorittaa kaiken työnsä; palvelu käyttää oletusarvoisesti sovelluksesi pääsäiettä, mikä voi hidastaa minkä tahansa sovelluksesi suorittaman aktiviteetin suorituskykyä.

Android-kehys tarjoaa myös Service:n IntentService-alaluokan, joka käyttääworker-säiettä käsittelemään kaikki käynnistyspyynnöt, yhden kerrallaan. Tämän luokan käyttäminen ei olesuositeltua uusille sovelluksille, sillä se ei toimi hyvin Android 8 Oreosta alkaen, johtuen taustan suoritusrajoitusten käyttöönotosta.Lisäksi se on vanhentunut Android 11:stä alkaen.Voit käyttää JobIntentService-luokkaa IntentService:n korvikkeena, joka on yhteensopiva uudempien Android-versioiden kanssa.

Seuraavissa osioissa kuvataan, miten voit toteuttaa oman mukautetun palvelusi, mutta useimmissa käyttötapauksissa kannattaa kuitenkin harkita sen sijaan WorkManagerin käyttöä. Tutustu Androidin taustakäsittelyn oppaaseen nähdäksesi, löytyykö tarpeisiisi sopiva ratkaisu.

Palvelu-luokan laajentaminen

Voit laajentaa Service-luokkaa käsittelemään kutakin saapuvaa aikomusta. Näin voisi näyttää perustoteutus:

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(); }}

Esimerkkikoodi käsittelee kaikki saapuvat kutsut onStartCommand():ssä ja lähettää työn Handler:lle, joka toimii taustasäikeessä. Se toimii aivan kuten IntentService ja käsittelee kaikki pyynnöt peräkkäin sarjallisesti.Voit muuttaa koodia siten, että työ suoritetaan esimerkiksi säiepooliin, jos haluat suorittaa useita pyyntöjä samanaikaisesti.

Huomaa, että onStartCommand()-metodin on palautettava kokonaisluku. Kokonaisluku on arvo, joka kuvaa, miten järjestelmän tulisi jatkaa palvelua siinä tapauksessa, että järjestelmä tappaa sen. onStartCommand():n paluuarvon on oltava jokin seuraavista vakioista:

START_NOT_STICKYJos järjestelmä lopettaa palvelunonStartCommand():n paluuarvon jälkeen, älä luo palvelua uudelleen, ellei palvelussa ole odottamattomiaintenttejä toimitettavaksi. Tämä on turvallisin vaihtoehto välttää palvelun suorittaminen silloin, kun se ei ole tarpeen, ja kun sovelluksesi voi yksinkertaisesti käynnistää uudelleen kaikki keskeneräiset työt.START_STICKYJos järjestelmä lopettaa palvelun sen jälkeen, kunonStartCommand()palaa, luo palvelu uudelleen ja kutsuonStartCommand(), mutta älä toimita viimeistä aikomusta uudelleen.Sen sijaan järjestelmä kutsuuonStartCommand():tä tyhjällä aikomuksella, ellei palvelun käynnistämistä odottavia aikomuksia ole. Siinä tapauksessa nämä aikomukset toimitetaan. Tämä sopii mediasoittimille (tai vastaaville palveluille), jotka eivät suorita komentoja, vaan ovat käynnissä loputtomiin ja odottavat työtä.START_REDELIVER_INTENTJos järjestelmä lopettaa palvelun sen jälkeen, kunonStartCommand()palaa, luo palvelu uudelleen ja kutsuonStartCommand()viimeisellä palveluun toimitetulla aikomuksella. Kaikki odottavat aikomukset toimitetaan vuorollaan. Tämä soveltuu palveluille, jotka suorittavat aktiivisesti työtä, jota pitäisi jatkaa välittömästi, kuten tiedoston lataamista.

Lisätietoja näistä paluuarvoista on kunkin vakion linkitetyssä referenssidokumentaatiossa.

Palvelun käynnistäminen

Voit käynnistää palvelun aktiviteetista tai muusta sovelluskomponentista välittämällä Intent:n startService() tai startForegroundService():lle. Android-järjestelmä kutsuu palvelun onStartCommand()-metodia ja välittää sille Intent, joka määrittää käynnistettävän palvelun.

Huomautus: Jos sovelluksesi tähtää API-tasolle 26 tai korkeammalle, järjestelmä asettaa rajoituksia taustapalveluiden käytölle tai luomiselle, ellei sovellus itse ole etualalla. Jos sovelluksen on luotava etualapalvelu, sovelluksen on kutsuttava startForegroundService(). Tämä menetelmä luo taustapalvelun, mutta menetelmä ilmoittaa järjestelmälle, että palvelu siirtyy etualalle. Kun palvelu on luotu, sen on kutsuttava startForeground()-metodiaan viiden sekunnin kuluessa.

Toiminto voi esimerkiksi käynnistää edellisen kappaleen esimerkkipalvelun (HelloService) käyttämällä eksplisiittistä aikomusta startService(), kuten tässä näytetään:

Kotlin

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

Java

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

Metodin startService() paluu tapahtuu välittömästi, jaAndroid-järjestelmä kutsuu palvelun onStartCommand()-metodia. Jos palvelu ei ole jo käynnissä, järjestelmä kutsuu ensin onCreate() ja sen jälkeen onStartCommand().

Jos palvelu ei tarjoa myös sidontaa, startService():n mukana toimitettu intent on ainoa kommunikointitapa sovelluskomponentin ja palvelun välillä. Jos kuitenkin halutaan, että palvelu lähettää tuloksen takaisin,palvelun käynnistävä asiakas voi luoda PendingIntent lähetystä varten (getBroadcast():llä) ja toimittaa sen palvelulle Intent:ssä, joka käynnistää palvelun. Palvelu voi sitten käyttää lähetystä tuloksen lähettämiseen.

Moninkertaiset pyynnöt palvelun käynnistämiseksi johtavat useisiin vastaaviin kutsuihin palvelun onStartCommand():lle. Palvelun pysäyttämiseen tarvitaan kuitenkin vain yksi pysäytyspyyntö (stopSelf():lla tai stopService():lla).

Palvelun pysäyttäminen

Käynnistetyn palvelun on hallittava oma elinkaarensa. Toisin sanoen järjestelmä ei pysäytä tai tuhoa palvelua, ellei sen tarvitse palauttaa järjestelmämuistia, ja palvelua jatketaan sen jälkeen, kun onStartCommand() palaa. Palvelun on pysäytettävä itsensä kutsumalla stopSelf(), tai jokin muu komponentti voi pysäyttää sen kutsumalla stopService().

Kun palvelua pyydetään pysäyttämään stopSelf():lla tai stopService():lla, järjestelmä tuhoaa palvelun niin pian kuin mahdollista.

Jos palvelusi käsittelee useita pyyntöjä osoitteeseen onStartCommand() samanaikaisesti, sinun ei pitäisi pysäyttää palvelua, kun olet lopettanut aloituspyynnön käsittelyn, koska olet saattanut saada uuden aloituspyynnön (pysäyttäminen ensimmäisen pyynnön päättyessä lopettaisi toisen pyynnön). Tämän ongelman välttämiseksi voit käyttää stopSelf(int) varmistaaksesi, että palvelun pysäyttämispyyntö perustuu aina viimeisimpään aloituspyyntöön. Toisin sanoen, kun kutsut stopSelf(int), annat sen aloituspyynnön ID:n (onStartCommand():lle toimitettu startId), johon pysäytyspyyntösi vastaa. Sitten, jos palvelu saa uuden käynnistyspyynnön ennen kuin ehdit kutsua stopSelf(int), ID ei täsmää ja palvelu ei pysähdy.

Varoitus: Välttääksesi järjestelmän resurssien tuhlaamisen ja akkuvirran kulutuksen, varmista, että sovelluksesi pysäyttää palvelunsa, kun se on lopettanut työskentelynsä.Tarvittaessa muut komponentit voivat pysäyttää palvelun kutsumalla stopService(). Vaikka ottaisitkin palvelun sitomisen käyttöön,sinun on aina itse pysäytettävä palvelu, jos se joskus saa kutsun onStartCommand().

Lisätietoa palvelun elinkaaresta saat alla olevasta kohdasta Palvelun elinkaaren hallinta.

Sidotun palvelun luominen

Sidottu palvelu on sellainen, jonka avulla sovelluskomponentit voivat sitoutua siihen kutsumalla bindService():aa luodakseen pitkäaikaisen yhteyden.Se ei yleensä salli komponenttien käynnistää sitä kutsumalla startService().

Luo sidottu palvelu, kun haluat olla vuorovaikutuksessa palvelun kanssa aktiviteeteista ja muista sovelluksen komponenteista tai paljastaa osan sovelluksen toiminnallisuudesta toisille sovelluksille prosessienvälisen kommunikaation (IPC) kautta.

Luoaksesi sidotun palvelun toteuta onBind() takaisinsoittomenetelmä palauttamaan IBinder, joka määrittelee rajapinnan kommunikaatiota varten palvelun kanssa. Muut sovelluskomponentit voivat sen jälkeen kutsuabindService() hakeakseen rajapinnan ja aloittaakseen palvelun metodien kutsumisen. Palvelu elää vain palvelemaan siihen sidottua sovelluskomponenttia, joten kun palveluun ei ole sidottuja komponentteja, järjestelmä tuhoaa sen.Sidottua palvelua ei tarvitse pysäyttää samalla tavalla kuin silloin, kun palvelu käynnistetään onStartCommand():n avulla.

Sidotun palvelun luomiseksi on määriteltävä rajapinta, joka määrittelee, miten asiakas voi olla yhteydessä palveluun. Tämän palvelun ja asiakkaan välisen rajapinnan on oltava IBinder:n toteutus, ja se on se, mitä palvelusi on palautettava onBind():n callback-metodista. Kun asiakas vastaanottaa IBinder, se voi aloittaa vuorovaikutuksen palvelun kanssa kyseisen rajapinnan kautta.

Monet asiakkaat voivat sitoutua palveluun samanaikaisesti. Kun asiakas on lopettanut vuorovaikutuksen palvelun kanssa, se kutsuu unbindService() irrottaakseen sitoutumisen.Kun palveluun ei ole sitoutuneita asiakkaita, järjestelmä tuhoaa palvelun.

Sitoutuneen palvelun voi toteuttaa monella eri tavalla, ja toteutus on monimutkaisempi kuin käynnistetyn palvelun. Näistä syistä sidottuja palveluita käsitellään erillisessä dokumentissa, joka käsittelee sidottuja palveluita.

Ilmoitusten lähettäminen käyttäjälle

Kun palvelu on käynnissä, se voi ilmoittaa käyttäjälle tapahtumista Toast-ilmoituksilla tai tilapalkki-ilmoituksilla.

Toast-ilmoitus on viesti, joka ilmestyy nykyisen ikkunan pintaan vain hetkeksi ennen kuin se katoaa. Tilapalkki-ilmoitus tarjoaa tilapalkissa kuvakkeen, jossa on viesti, jonka käyttäjä voi valita ryhtyäkseen johonkin toimenpiteeseen (kuten jonkin toiminnon aloittamiseen).

Tilapalkki-ilmoitus on yleensä paras tekniikka silloin, kun taustatyö, kuten tiedostojen lataaminen, on päättynyt ja käyttäjä voi nyt toimia sen mukaan. Kun käyttäjävalitsee ilmoituksen laajennetusta näkymästä, ilmoitus voi käynnistää toiminnon(esimerkiksi näyttää ladatun tiedoston).

Lisätietoa saat Toast-ilmoituksista tai tilapalkki-ilmoituksistakehittäjän oppaista.

Palvelun elinkaaren hallinta

Palvelun elinkaari on paljon yksinkertaisempi kuin toiminnon. On kuitenkin sitäkin tärkeämpää, että kiinnität tarkkaa huomiota siihen, miten palvelu luodaan ja tuhotaan, koska palvelu voi toimia taustalla käyttäjän tietämättä.

Palvelun elinkaari – sen luomisesta tuhoutumiseen – voi noudattaa jompaakumpaa näistä kahdesta polusta:

  • Käynnistetty palvelu

    Palvelu luodaan, kun toinen komponentti kutsuu startService(). Tämän jälkeen palvelu toimii loputtomiin ja sen on lopetettava itsensä kutsumalla stopSelf(). Toinen komponentti voi myös pysäyttää palvelun kutsumalla stopService(). Kun palvelu pysäytetään, järjestelmä tuhoaa sen.

  • Sidottu palvelu

    Palvelu luodaan, kun toinen komponentti (asiakas) kutsuu bindService(). Tämän jälkeen asiakas kommunikoi palvelun kanssa IBinder rajapinnan kautta. Asiakas voi sulkea yhteyden kutsumallaunbindService(). Useat asiakkaat voivat sitoutua samaan palveluun, ja kun kaikki asiakkaat poistavat sitoutumisensa, järjestelmä tuhoaa palvelun. Palvelun ei tarvitse lopettaa itseään.

Nämä kaksi polkua eivät ole täysin erillisiä. Voit sitoutua palveluun, joka on jo käynnistetty startService():llä. Voit esimerkiksi käynnistää taustamusiikkipalvelun kutsumalla startService(), jossa on Intent, joka yksilöi toistettavan musiikin. Myöhemmin,mahdollisesti kun käyttäjä haluaa hallita soitinta tai saada tietoa nykyisestä kappaleesta, aktiviteetti voi sitoutua palveluun kutsumalla bindService(). Tällaisissa tapauksissa stopService() tai stopSelf() ei varsinaisesti pysäytä palvelua ennen kuin kaikki asiakkaat irrottavat sitoutumisensa.

Elinkaaren takaisinkutsujen toteuttaminen

Kuten aktiviteetilla, myös palvelulla on elinkaaren takaisinkutsumetodit, jotka voit toteuttaa seurataksesi muutoksia palvelun tilassa ja suorittaaksesi töitä sopivina ajankohtina. Seuraava luurankopalvelu havainnollistaa kutakin elinkaaren metodia:

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 }}

Huomaa: Toisin kuin aktiviteettien elinkaaren takaisinkutsumenetelmissä, näiden takaisinkutsumenetelmien yläluokkatoteutusta ei tarvitse kutsua.

Kuva 2. Palvelun elinkaari. Vasemmanpuoleinen kaavio näyttää elinkaaren, kun palvelu luodaan startService():lla, ja oikeanpuoleinen kaavio näyttää elinkaaren, kun palvelu luodaan bindService():lla.

Kuvassa 2 on havainnollistettu tyypillisiä palvelun takaisinkutsumenetelmiä. Vaikka kuvio erottaa startService():lla luodut palvelut bindService():lla luoduista palveluista, pitää muistaa, että mikä tahansa palvelu, riippumatta siitä, miten se on käynnistetty, voi potentiaalisesti sallia asiakkaiden sitoutua siihen.Palvelu, joka on alun perin käynnistetty onStartCommand():llä (startService():aa kutsuvan asiakkaan toimesta), voi silti vastaanottaa kutsun onBind():llä (asiakkaan kutsuessa bindService():aa).

Toteuttamalla nämä metodit voit valvoa näitä kahta palvelun elinkaaren sisäkkäistä silmukkaa:

  • Palvelun koko elinkaari ajoittuu onCreate() kutsun ja onDestroy() paluun välille. Kuten aktiviteetti, palvelu tekee alkuasetukset onCreate():ssä ja vapauttaa kaikki jäljellä olevat resurssit onDestroy():ssä. Esimerkiksi musiikin toistopalvelu voi luoda säikeen, jossa musiikkia soitetaan onCreate():ssä, ja sitten se voi lopettaa säikeen onDestroy():ssä.

    Huomaa: onCreate()– ja onDestroy()-metodeja kutsutaan kaikista palveluista riippumatta siitä, onko ne luotu startService():llä vai bindService():lla.

  • Palvelun aktiivinen elinkaari alkaa kutsusta joko onStartCommand():lle tai onBind():lle.Kullekin metodille annetaan Intent, joka välitettiin joko startService():lle tai bindService():lle.

    Jos palvelu käynnistetään, aktiivinen elinkaari loppuu samaan aikaan, kun koko elinkaari loppuu (palvelu on edelleen aktiivinen myös sen jälkeen, kun onStartCommand() palaa). Jos palvelu on sidottu, aktiivinen elinikä päättyy, kun onUnbind() palaa.

Huomaa: Vaikka käynnistetty palvelu pysäytetään kutsumalla joko stopSelf() tai stopService(), palvelulle ei ole vastaavaa takaisinkutsua (ei ole onStop() takaisinkutsua). Ellei palvelua ole sidottu asiakkaaseen,järjestelmä tuhoaa sen, kun palvelu pysäytetään – onDestroy() on ainoa vastaanotettu takaisinkutsu.

Lisätietoa sitovaa palvelua tarjoavan palvelun luomisesta on Sidotut palvelut -dokumentissa,jossa on lisätietoa onRebind()takaisinkutsumenetelmästä kappaleessa Sidotun palvelun elinkaaren hallinta.

Vastaa

Sähköpostiosoitettasi ei julkaista.