Powiązana usługa to serwer w interfejsie klient-serwer. Umożliwia komponentom, takim jak aktywności, powiązanie z usługą, wysyłanie żądań, odbieranie odpowiedzi i komunikację między procesami (IPC). Powiązana usługa zwykle działa tylko wtedy, gdy obsługuje inny komponent aplikacji, i nie działa w tle bez ograniczeń czasowych.
W tym dokumencie opisano, jak utworzyć powiązaną usługę, w tym jak powiązać ją z innymi komponentami aplikacji. Dodatkowe informacje o usługach, np. o tym, jak dostarczać powiadomienia z usługi i ustawiać jej działanie na pierwszym planie, znajdziesz w omówieniu usług.
Podstawy
Powiązana usługa to implementacja klasy Service, która umożliwia innym aplikacjom powiązanie się z nią i interakcję. Aby zapewnić powiązanie z usługą, zaimplementuj metodę wywołania zwrotnego onBind(). Ta metoda zwraca obiekt IBinder, który definiuje interfejs programistyczny, którego klienci mogą używać do interakcji z usługą.
Tworzenie powiązania z uruchomioną usługą
Jak wspomniano w omówieniu usług, możesz utworzyć usługę, która jest zarówno uruchomiona, jak i powiązana. Oznacza to, że możesz uruchomić usługę, wywołując startService(), co pozwoli jej działać bez ograniczeń czasowych. Możesz też zezwolić klientowi na powiązanie z usługą przez wywołanie bindService().
Jeśli zezwolisz na uruchomienie i powiązanie usługi, system nie zniszczy jej, gdy wszyscy klienci zostaną od niej odłączeni.
Zamiast tego musisz wyraźnie zatrzymać usługę, wywołując stopSelf() lub stopService().
Zwykle implementuje się onBind()
lub onStartCommand(), ale czasami konieczne jest zaimplementowanie obu tych metod. Na przykład odtwarzacz muzyki może potrzebować usługi, która działa bezterminowo i zapewnia powiązanie. Dzięki temu aktywność może uruchomić usługę, aby odtworzyć muzykę, która będzie odtwarzana nawet wtedy, gdy użytkownik opuści aplikację. Gdy użytkownik wróci do aplikacji, aktywność może powiązać się z usługą, aby odzyskać kontrolę nad odtwarzaniem.
Więcej informacji o cyklu życia usługi podczas dodawania powiązania do uruchomionej usługi znajdziesz w sekcji Zarządzanie cyklem życia powiązanej usługi.
Klient wiąże się z usługą, wywołując metodę bindService(). W takim przypadku musi udostępniać implementację interfejsu ServiceConnection, który monitoruje połączenie z usługą. Wartość zwracana przez bindService() wskazuje, czy żądana usługa istnieje i czy klient ma do niej dostęp.
Gdy system Android tworzy połączenie między klientem a usługą, wywołuje onServiceConnected() w ServiceConnection. Metoda onServiceConnected() zawiera argument IBinder, którego klient używa do komunikacji z powiązaną usługą.
Z usługą możesz połączyć wielu klientów jednocześnie. System zapisuje jednak w pamięci podręcznej kanał komunikacji usługi IBinder.
Innymi słowy, system wywołuje metodę onBind() usługi, aby wygenerować IBinder tylko wtedy, gdy pierwszy klient się powiąże. Następnie system dostarcza ten sam obiekt IBinder do wszystkich dodatkowych klientów, którzy powiążą się z tą samą usługą, bez ponownego wywoływania metody onBind().
Gdy ostatni klient odłączy się od usługi, system ją zniszczy, chyba że została uruchomiona za pomocą startService().
Najważniejszą częścią implementacji powiązanej usługi jest zdefiniowanie interfejsu, który zwraca metoda wywołania zwrotnego onBind(). W następnej sekcji omówiono kilka sposobów definiowania interfejsu IBinder usługi.
Tworzenie powiązanej usługi
Podczas tworzenia usługi, która umożliwia powiązanie, musisz podać IBinder, który udostępnia interfejs programowania, za pomocą którego klienci mogą wchodzić w interakcję z usługą. Interfejs można zdefiniować na 3 sposoby:
- Rozszerzanie klasy Binder
- Jeśli usługa jest prywatna dla Twojej aplikacji i działa w tym samym procesie co klient (co jest częstym przypadkiem), utwórz interfejs, rozszerzając klasę
Binderi zwracając jej instancję zonBind(). Klient otrzymujeBinderi może go używać do bezpośredniego dostępu do metod publicznych dostępnych w implementacjiBinderlubService.Jest to preferowana technika, gdy usługa jest tylko procesem roboczym w tle dla Twojej aplikacji. Jedynym przypadkiem, w którym nie jest to preferowany sposób tworzenia interfejsu, jest sytuacja, gdy usługa jest używana przez inne aplikacje lub w osobnych procesach.
- Korzystanie z komunikatora
- Jeśli interfejs ma działać w różnych procesach, możesz utworzyć interfejs dla usługi za pomocą
Messenger. W ten sposób usługa definiujeHandler, który odpowiada na różne typy obiektówMessage.Ten
Handlerjest podstawą dlaMessenger, który może następnie udostępnićIBinderklientowi, umożliwiając mu wysyłanie poleceń do usługi za pomocą obiektówMessage. Klient może też zdefiniować własny obiektMessenger, aby usługa mogła wysyłać wiadomości z powrotem.Jest to najprostszy sposób komunikacji między procesami (IPC), ponieważ
Messengerumieszcza wszystkie żądania w jednym wątku, dzięki czemu nie musisz projektować usługi pod kątem bezpieczeństwa wątków. - Używanie AIDL
- Język definiowania interfejsu Androida (AIDL) rozkłada obiekty na elementy podstawowe, które system operacyjny może zrozumieć, i porządkuje je w procesach, aby umożliwić komunikację między procesami. Poprzednia technika, wykorzystująca
Messenger, jest oparta na AIDL jako strukturze bazowej.Jak wspomnieliśmy w poprzedniej sekcji,
Messengertworzy kolejkę wszystkich żądań klienta w jednym wątku, dzięki czemu usługa otrzymuje żądania pojedynczo. Jeśli jednak chcesz, aby usługa obsługiwała wiele żądań jednocześnie, możesz użyć bezpośrednio AIDL. W takim przypadku usługa musi być wątkowo bezpieczna i obsługiwać wielowątkowość.Aby używać AIDL bezpośrednio, utwórz plik
.aidl, który definiuje interfejs programowania. Narzędzia Android SDK używają tego pliku do wygenerowania klasy abstrakcyjnej, która implementuje interfejs i obsługuje IPC, którą możesz następnie rozszerzyć w ramach usługi.
Uwaga: w przypadku większości aplikacji AIDL nie jest najlepszym wyborem do tworzenia powiązanej usługi, ponieważ może wymagać wielowątkowości i skomplikowanej implementacji. Dlatego w tym dokumencie nie omawiamy, jak używać go w usłudze. Jeśli masz pewność, że musisz używać AIDL bezpośrednio, zapoznaj się z dokumentem AIDL.
Rozszerzanie klasy Binder
Jeśli tylko aplikacja lokalna korzysta z Twojej usługi i nie musi ona działać w różnych procesach, możesz zaimplementować własną klasę Binder, która zapewnia klientowi bezpośredni dostęp do metod publicznych w usłudze.
Uwaga: działa to tylko wtedy, gdy klient i usługa znajdują się w tej samej aplikacji i procesie, co jest najczęstszym przypadkiem. Sprawdza się to na przykład w przypadku aplikacji muzycznej, która musi powiązać aktywność z własną usługą odtwarzającą muzykę w tle.
Ustawienia należy skonfigurować w następujący sposób:
- W usłudze utwórz instancję
Binder, która wykonuje jedną z tych czynności:- Zawiera metody publiczne, które klient może wywołać.
- Zwraca bieżącą instancję
Service, która ma metody publiczne, które klient może wywołać. - Zwraca instancję innej klasy hostowanej przez usługę z metodami publicznymi, które klient może wywołać.
- Zwróć tę instancję
Binderz metody wywołania zwrotnegoonBind(). - W kliencie odbierz
Binderz metody wywołania zwrotnegoonServiceConnected()i wykonaj połączenia z powiązaną usługą, korzystając z podanych metod.
Uwaga: usługa i klient muszą znajdować się w tej samej aplikacji, aby klient mógł rzutować zwrócony obiekt i prawidłowo wywoływać jego interfejsy API. Usługa i klient muszą też znajdować się w tym samym procesie, ponieważ ta technika nie wykonuje żadnego marshalingu między procesami.
Oto przykład usługi, która zapewnia klientom dostęp do metod w usłudze za pomocą implementacji Binder:
Kotlin
class LocalService : Service() { // Binder given to clients. private val binder = LocalBinder() // Random number generator. private val mGenerator = Random() /** Method for clients. */ val randomNumber: Int get() = mGenerator.nextInt(100) /** * Class used for the client Binder. Because we know this service always * runs in the same process as its clients, we don't need to deal with IPC. */ inner class LocalBinder : Binder() { // Return this instance of LocalService so clients can call public methods. fun getService(): LocalService = this@LocalService } override fun onBind(intent: Intent): IBinder { return binder } }
Java
public class LocalService extends Service { // Binder given to clients. private final IBinder binder = new LocalBinder(); // Random number generator. private final Random mGenerator = new Random(); /** * Class used for the client Binder. Because we know this service always * runs in the same process as its clients, we don't need to deal with IPC. */ public class LocalBinder extends Binder { LocalService getService() { // Return this instance of LocalService so clients can call public methods. return LocalService.this; } } @Override public IBinder onBind(Intent intent) { return binder; } /** Method for clients. */ public int getRandomNumber() { return mGenerator.nextInt(100); } }
LocalBinder udostępnia metodę getService(), która umożliwia klientom pobieranie bieżącej instancji LocalService. Dzięki temu klienci mogą wywoływać metody publiczne w usłudze. Klienci mogą na przykład dzwonić do getRandomNumber() z usługi.
Oto aktywność, która wiąże się z LocalService i wywołuje getRandomNumber() po kliknięciu przycisku:
Kotlin
class BindingActivity : Activity() { private var mService: LocalService? = null private var mBound: Boolean = false /** Defines callbacks for service binding, passed to bindService(). */ private val connection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { // We've bound to LocalService, cast the IBinder and get LocalService instance. val binder = service as LocalService.LocalBinder mService = binder.getService() mBound = true } override fun onServiceDisconnected(arg0: ComponentName) { mService = null mBound = false } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) } override fun onStart() { super.onStart() // Bind to LocalService. Intent(this, LocalService::class.java).also { intent -> bindService(intent, connection, Context.BIND_AUTO_CREATE) } } override fun onStop() { super.onStop() unbindService(connection) // Release the reference so the service can be garbage-collected once it's // destroyed. onServiceDisconnected() isn't called when the client unbinds. mService = null mBound = false } /** Called when a button is clicked (the button in the layout file attaches to * this method with the android:onClick attribute). */ fun onButtonClick(v: View) { if (mBound) { // Call a method from the LocalService. // However, if this call is something that might hang, then put this request // in a separate thread to avoid slowing down the activity performance. val num: Int = mService?.randomNumber ?: return Toast.makeText(this, "number: $num", Toast.LENGTH_SHORT).show() } } }
Java
public class BindingActivity extends Activity { LocalService mService; boolean mBound = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected void onStart() { super.onStart(); // Bind to LocalService. Intent intent = new Intent(this, LocalService.class); bindService(intent, connection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); unbindService(connection); // Release the reference so the service can be garbage-collected once it's // destroyed. onServiceDisconnected() isn't called when the client unbinds. mService = null; mBound = false; } /** Called when a button is clicked (the button in the layout file attaches to * this method with the android:onClick attribute). */ public void onButtonClick(View v) { if (mBound) { // Call a method from the LocalService. // However, if this call is something that might hang, then put this request // in a separate thread to avoid slowing down the activity performance. int num = mService.getRandomNumber(); Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); } } /** Defines callbacks for service binding, passed to bindService(). */ private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { // We've bound to LocalService, cast the IBinder and get LocalService instance. LocalBinder binder = (LocalBinder) service; mService = binder.getService(); mBound = true; } @Override public void onServiceDisconnected(ComponentName arg0) { mService = null; mBound = false; } }; }
Powyższy przykład pokazuje, jak klient wiąże się z usługą za pomocą implementacji ServiceConnection i wywołania zwrotnego onServiceConnected(). W następnej sekcji znajdziesz więcej informacji o procesie wiązania z usługą.
Uwaga: w poprzednim przykładzie metoda onStop() odłącza klienta od usługi i zwalnia jego odwołanie do niej.
Odłączaj klientów od usług w odpowiednich momentach, zgodnie z opisem w sekcji Dodatkowe uwagi.
Więcej przykładowego kodu znajdziesz w klasach
LocalService.java i
LocalServiceActivities.java w ApiDemos.
Korzystanie z Messengera
Jeśli usługa musi komunikować się z procesami zdalnymi, możesz użyć Messenger, aby udostępnić interfejs usługi. Ta technika umożliwia komunikację między procesami (IPC) bez konieczności używania AIDL.
Używanie Messenger w interfejsie jest prostsze niż używanie AIDL, ponieważ Messenger kolejkuje wszystkie wywołania usługi. Czysty interfejs AIDL wysyła do usługi jednoczesne żądania, które muszą być następnie obsługiwane przez wielowątkowość.
W przypadku większości aplikacji usługa nie musi wykonywać wielowątkowości, więc użycie Messenger pozwala usłudze obsługiwać jedno wywołanie naraz. Jeśli ważne jest, aby usługa była wielowątkowa, użyj AIDL do zdefiniowania interfejsu.
Oto podsumowanie sposobu korzystania z Messenger:
- Usługa implementuje interfejs
Handler, który otrzymuje wywołanie zwrotne dla każdego wywołania od klienta. - Usługa używa
Handlerdo utworzenia obiektuMessenger(który jest odwołaniem doHandler). MessengertworzyIBinder, który usługa zwraca klientom zonBind().- Klienci używają
IBinderdo utworzenia instancjiMessenger(która odwołuje się doHandlerusługi), której klient używa do wysyłania obiektówMessagedo usługi. - Usługa otrzymuje każdy obiekt
Messagew obiekcieHandler, a dokładnie w metodziehandleMessage().
W ten sposób klient nie ma żadnych metod, które mógłby wywołać w usłudze. Zamiast tego klient dostarcza wiadomości (obiekty Message), które usługa odbiera w Handler.
Oto przykładowa usługa, która używa interfejsu Messenger:
Kotlin
/** Command to the service to display a message. */ private const val MSG_SAY_HELLO = 1 class MessengerService : Service() { /** * Target we publish for clients to send messages to IncomingHandler. */ private lateinit var mMessenger: Messenger /** * Handler of incoming messages from clients. */ internal class IncomingHandler( context: Context, private val applicationContext: Context = context.applicationContext ) : Handler() { override fun handleMessage(msg: Message) { when (msg.what) { MSG_SAY_HELLO -> Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show() else -> super.handleMessage(msg) } } } /** * When binding to the service, we return an interface to our messenger * for sending messages to the service. */ override fun onBind(intent: Intent): IBinder? { Toast.makeText(applicationContext, "binding", Toast.LENGTH_SHORT).show() mMessenger = Messenger(IncomingHandler(this)) return mMessenger.binder } }
Java
public class MessengerService extends Service { /** * Command to the service to display a message. */ static final int MSG_SAY_HELLO = 1; /** * Handler of incoming messages from clients. */ static class IncomingHandler extends Handler { private Context applicationContext; IncomingHandler(Context context) { applicationContext = context.getApplicationContext(); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show(); break; default: super.handleMessage(msg); } } } /** * Target we publish for clients to send messages to IncomingHandler. */ Messenger mMessenger; /** * When binding to the service, we return an interface to our messenger * for sending messages to the service. */ @Override public IBinder onBind(Intent intent) { Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); mMessenger = new Messenger(new IncomingHandler(this)); return mMessenger.getBinder(); } }
Metoda handleMessage() w Handler to miejsce, w którym usługa otrzymuje przychodzące żądanie Message i decyduje, co zrobić, na podstawie elementu what.
Klient musi tylko utworzyć Messenger na podstawie IBinder zwróconego przez usługę i wysłać wiadomość za pomocą send(). Oto przykład aktywności, która wiąże się z usługą i dostarcza do niej wiadomość MSG_SAY_HELLO:
Kotlin
class ActivityMessenger : Activity() { /** Messenger for communicating with the service. */ private var mService: Messenger? = null /** Flag indicating whether we have called bind on the service. */ private var bound: Boolean = false /** * Class for interacting with the main interface of the service. */ private val mConnection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { // This is called when the connection with the service has been // established, giving us the object we can use to // interact with the service. We are communicating with the // service using a Messenger, so here we get a client-side // representation of that from the raw IBinder object. mService = Messenger(service) bound = true } override fun onServiceDisconnected(className: ComponentName) { // This is called when the connection with the service has been // unexpectedly disconnected—that is, its process crashed. mService = null bound = false } } fun sayHello(v: View) { if (!bound) return // Create and send a message to the service, using a supported 'what' value. val msg: Message = Message.obtain(null, MSG_SAY_HELLO, 0, 0) try { mService?.send(msg) } catch (e: RemoteException) { e.printStackTrace() } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) } override fun onStart() { super.onStart() // Bind to the service. Intent(this, MessengerService::class.java).also { intent -> bindService(intent, mConnection, Context.BIND_AUTO_CREATE) } } override fun onStop() { super.onStop() // Unbind from the service. if (bound) { unbindService(mConnection) mService = null bound = false } } }
Java
public class ActivityMessenger extends Activity { /** Messenger for communicating with the service. */ Messenger mService = null; /** Flag indicating whether we have called bind on the service. */ boolean bound; /** * Class for interacting with the main interface of the service. */ private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service has been // established, giving us the object we can use to // interact with the service. We are communicating with the // service using a Messenger, so here we get a client-side // representation of that from the raw IBinder object. mService = new Messenger(service); bound = true; } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected—that is, its process crashed. mService = null; bound = false; } }; public void sayHello(View v) { if (!bound) return; // Create and send a message to the service, using a supported 'what' value. Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); try { mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected void onStart() { super.onStart(); // Bind to the service. bindService(new Intent(this, MessengerService.class), mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // Unbind from the service. if (bound) { unbindService(mConnection); mService = null; bound = false; } } }
Ten przykład nie pokazuje, jak usługa może odpowiedzieć klientowi.
Jeśli chcesz, aby usługa odpowiadała, musisz też utworzyć Messenger w kliencie.
Gdy klient otrzyma wywołanie zwrotne onServiceConnected(), wysyła do usługi żądanie Message, które zawiera Messenger klienta w parametrze replyTo metody send().
Przykład tego, jak zapewnić dwukierunkową komunikację, znajdziesz w przykładach
MessengerService.java (usługa) i
MessengerServiceActivities.java (klient).
Tworzenie powiązania z usługą
Komponenty aplikacji (klienty) mogą wiązać się z usługą, wywołując bindService(). Następnie system Android wywołuje metodę onBind() usługi, która zwraca obiekt IBinder do interakcji z usługą.
Wiązanie jest asynchroniczne, a funkcja bindService() natychmiast zwraca wynik bez zwracania IBinder do klienta. Aby otrzymać IBinder, klient musi utworzyć instancję ServiceConnection i przekazać ją do bindService(). ServiceConnection zawiera metodę wywołania zwrotnego, którą system wywołuje, aby dostarczyć IBinder.
Uwaga: z usługą mogą się wiązać tylko aktywności, usługi i mechanizmy dostarczania treści. Nie można powiązać usługi z odbiornikiem komunikatów.
Aby powiązać usługę z klientem, wykonaj te czynności:
- Wdrożenie
ServiceConnection.Implementacja musi zastąpić 2 metody wywołania zwrotnego:
onServiceConnected()- System wywołuje tę metodę, aby dostarczyć obiekt
IBinderzwracany przez metodęonBind()usługi. onServiceDisconnected()- System Android wywołuje tę metodę, gdy połączenie z usługą zostanie nieoczekiwanie utracone, np. gdy usługa ulegnie awarii lub zostanie zamknięta. Ta metoda nie jest wywoływana, gdy klient rozłącza się z usługą.
- Wywołaj
bindService(), przekazując implementacjęServiceConnection.Uwaga: jeśli metoda zwraca wartość false, klient nie ma prawidłowego połączenia z usługą. Jednak w kliencie wywołaj
unbindService(). W przeciwnym razie klient uniemożliwi wyłączenie usługi, gdy będzie ona bezczynna. - Gdy system wywoła metodę wywołania zwrotnego
onServiceConnected(), możesz zacząć wykonywać wywołania do usługi, używając metod zdefiniowanych przez interfejs. - Aby rozłączyć się z usługą, zadzwoń pod numer
unbindService().Jeśli klient jest nadal powiązany z usługą, gdy aplikacja go niszczy, zniszczenie powoduje odłączenie klienta. Zalecamy odłączenie klienta, gdy tylko zakończy on interakcję z usługą. Dzięki temu nieaktywna usługa zostanie wyłączona. Więcej informacji o odpowiednich momentach na powiązanie i odwiązanie znajdziesz w sekcji Dodatkowe uwagi.
W poniższym przykładzie klient łączy się z usługą utworzoną wcześniej przez rozszerzenie klasy Binder, więc wystarczy, że rzutuje zwrócony obiekt IBinder na klasę LocalBinder i zażąda instancji LocalService:
Kotlin
var mService: LocalService? = null val mConnection = object : ServiceConnection { // Called when the connection with the service is established. override fun onServiceConnected(className: ComponentName, service: IBinder) { // Because we have bound to an explicit // service that is running in our own process, we can // cast its IBinder to a concrete class and directly access it. val binder = service as LocalService.LocalBinder mService = binder.getService() mBound = true } // Called when the connection with the service disconnects unexpectedly. override fun onServiceDisconnected(className: ComponentName) { Log.e(TAG, "onServiceDisconnected") mService = null mBound = false } }
Java
LocalService mService; private ServiceConnection mConnection = new ServiceConnection() { // Called when the connection with the service is established. public void onServiceConnected(ComponentName className, IBinder service) { // Because we have bound to an explicit // service that is running in our own process, we can // cast its IBinder to a concrete class and directly access it. LocalBinder binder = (LocalBinder) service; mService = binder.getService(); mBound = true; } // Called when the connection with the service disconnects unexpectedly. public void onServiceDisconnected(ComponentName className) { Log.e(TAG, "onServiceDisconnected"); mService = null; mBound = false; } };
Za pomocą tego ServiceConnection klient może powiązać się z usługą, przekazując go do bindService(), jak w tym przykładzie:
Kotlin
Intent(this, LocalService::class.java).also { intent -> bindService(intent, connection, Context.BIND_AUTO_CREATE) }
Java
Intent intent = new Intent(this, LocalService.class); bindService(intent, connection, Context.BIND_AUTO_CREATE);
- Pierwszym parametrem metody
bindService()jestIntent, który jednoznacznie określa usługę do powiązania.Uwaga: jeśli używasz intencji do powiązania z
Service, upewnij się, że Twoja aplikacja jest bezpieczna, używając intencji jawnej. Użycie intencji niejawnej do uruchomienia usługi stanowi zagrożenie dla bezpieczeństwa, ponieważ nie można mieć pewności, która usługa odpowie na intencję, a użytkownik nie widzi, która usługa się uruchamia. Począwszy od Androida 5.0 (poziom interfejsu API 21), system zgłasza wyjątek, jeśli wywołasz metodębindService()z zamiarem niejawnym. - Drugi parametr to obiekt
ServiceConnection. - Trzeci parametr to flaga wskazująca opcje powiązania – zwykle
BIND_AUTO_CREATE, aby utworzyć usługę, jeśli jeszcze nie działa. Inne możliwe wartości toBIND_DEBUG_UNBIND,BIND_NOT_FOREGROUNDlub0(brak).
Uwagi dodatkowe
Oto kilka ważnych informacji o powiązaniu z usługą:
- Zawsze przechwytuj wyjątki
DeadObjectException, które są zgłaszane, gdy połączenie zostanie przerwane. To jedyny wyjątek zgłaszany przez metody zdalne. - Obiekty są zliczane w procesach.
- Zwykle powiązanie i odwiązanie są wykonywane w odpowiednich momentach cyklu życia klienta, jak opisano w tych przykładach:
- Jeśli chcesz korzystać z usługi tylko wtedy, gdy aktywność jest widoczna, powiąż ją podczas
onStart()i odłącz podczasonStop(). - Jeśli chcesz, aby aktywność otrzymywała odpowiedzi nawet wtedy, gdy jest zatrzymana w tle, powiąż ją podczas
onCreate()i odłącz podczasonDestroy(). Pamiętaj, że oznacza to, że aktywność musi korzystać z usługi przez cały czas jej działania, nawet w tle. Gdy usługa jest w innym procesie, zwiększasz wagę procesu i jest bardziej prawdopodobne, że zostanie on zamknięty przez system.
Uwaga: zwykle nie wiąże się i nie rozwiązuje aktywności podczas wywołań zwrotnych
onResume()ionPause(), ponieważ te wywołania zwrotne występują przy każdym przejściu cyklu życia. Ogranicz do minimum przetwarzanie, które odbywa się podczas tych przejść.Jeśli wiele aktywności w aplikacji jest powiązanych z tą samą usługą i następuje przejście między 2 z nich, usługa może zostać zniszczona i utworzona ponownie, gdy bieżąca aktywność zostanie odłączona (podczas wstrzymania), zanim zostanie powiązana następna (podczas wznowienia). Przejście między działaniami, które opisuje, jak działania koordynują swoje cykle życia, jest opisane w sekcji Cykl życia działania.
- Jeśli chcesz korzystać z usługi tylko wtedy, gdy aktywność jest widoczna, powiąż ją podczas
Więcej przykładowego kodu pokazującego, jak powiązać usługę, znajdziesz w klasie
RemoteService.java w ApiDemos.
Zarządzanie cyklem życia powiązanej usługi
Gdy usługa zostanie odłączona od wszystkich klientów, system Android ją niszczy (chyba że została uruchomiona za pomocą metody startService()). Nie musisz więc zarządzać cyklem życia usługi, jeśli jest to wyłącznie powiązana usługa. System Android zarządza nią na podstawie tego, czy jest powiązana z jakimiś klientami.
Jeśli jednak zdecydujesz się wdrożyć metodę wywołania zwrotnego onStartCommand(), musisz wyraźnie zatrzymać usługę, ponieważ jest ona teraz uznawana za uruchomioną. W takim przypadku usługa działa, dopóki nie zatrzyma się sama za pomocą metody stopSelf() lub dopóki inny komponent nie wywoła metody stopService(), niezależnie od tego, czy jest powiązana z jakimiś klientami.
Dodatkowo, jeśli usługa jest uruchomiona i akceptuje powiązanie, to gdy system wywołuje metodę onUnbind(), możesz opcjonalnie zwrócić true, jeśli chcesz otrzymać wywołanie metody onRebind(), gdy klient następnym razem powiąże się z usługą. Metoda onRebind() zwraca wartość void, ale klient nadal otrzymuje obiekt IBinder w wywołaniu zwrotnym onServiceConnected().
Na rysunku poniżej przedstawiono logikę tego rodzaju cyklu życia.
Więcej informacji o cyklu życia uruchomionej usługi znajdziesz w omówieniu usług.