Usługa powiązana to serwer w interfejsie klient-serwer. Dzięki niemu komponenty takie jak działania mogą wiązać się z usługą, wysyłać żądania, odbierać odpowiedzi i wykonywać komunikację międzyprocesową (IPC). Powiązana usługa działa zwykle tylko wtedy, gdy obsługuje inny komponent aplikacji, i nie działa w tle przez cały czas.
W tym dokumencie opisano, jak utworzyć powiązaną usługę, w tym jak utworzyć powiązanie z tą usługą z innych komponentów aplikacji. Więcej ogólnych informacji o usługach, takich jak sposób dostarczania powiadomień z usługi i ustawiania jej działania na pierwszym planie, znajdziesz w omówieniu usług.
Podstawy
Usługa powiązana to implementacja klasy Service
, która umożliwia innym aplikacjom powiązanie z nią i interakcje z nią. Aby udostępnić powiązanie dla usługi, implementujesz metodę wywołania zwrotnego onBind()
. Ta metoda zwraca obiekt IBinder
określający interfejs programowania, za pomocą którego klienty mogą korzystać z usługi.
Powiązanie z uruchomioną usługą
Jak wspomnieliśmy w opisie 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 umożliwia jej działanie bezterminowo. Możesz też pozwolić klientowi na powiązanie z usługą, wywołując bindService()
.
Jeśli zezwolisz na uruchomienie i powiązanie usługi, to po uruchomieniu usługi system nie zniszczy jej po usunięciu powiązania wszystkich klientów.
Zamiast tego musisz wyraźnie zatrzymać usługę, wywołując stopSelf()
lub stopService()
.
Chociaż zwykle implementujesz onBind()
lub onStartCommand()
, czasami konieczne jest zaimplementowanie obu. Na przykład odtwarzacz muzyki może chcieć, aby jego usługa działała
w nieskończoność i mogła też określić powiązania. W ten sposób aktywność może uruchomić usługę i odtworzyć muzykę, która będzie odtwarzana nawet wtedy, gdy użytkownik opuści aplikację. Następnie, gdy użytkownik wróci do aplikacji, aktywność może zostać powiązana 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 łączy się z usługą, wywołując bindService()
. W takich przypadkach musi udostępniać implementację ServiceConnection
, która monitoruje połączenie z usługą. Zwracana wartość 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()
na ServiceConnection
. Metoda onServiceConnected()
zawiera argument IBinder
, którego klient używa następnie do komunikowania się 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
.
Inaczej mówiąc, system wywołuje metodę onBind()
usługi, aby wygenerować IBinder
tylko po powiązaniu pierwszego klienta. System będzie następnie dostarczać ten sam IBinder
do wszystkich dodatkowych klientów, które łączą się z tą samą usługą, bez ponownego wywoływania metody onBind()
.
Gdy ostatni klient usunie powiązanie z usługą, system niszczy usługę, chyba że została uruchomiona przy użyciu startService()
.
Najważniejszym elementem implementacji powiązanej usługi jest zdefiniowanie interfejsu zwracanego przez metodę wywołania zwrotnego onBind()
. W tej sekcji opisujemy kilka sposobów definiowania interfejsu IBinder
usługi.
Utwórz powiązaną usługę
Gdy tworzysz usługę, która udostępnia wiązanie, musisz udostępnić interfejs IBinder
udostępniający interfejs programowania, za pomocą którego klienty mogą wchodzić w interakcje z tą usługą. Interfejs można definiować na 3 sposoby:
- Rozszerz klasę Binder
- Jeśli Twoja usługa jest prywatna dla Twojej aplikacji i działa w tym samym procesie co klient, co jest powszechne, utwórz interfejs, rozszerzając klasę
Binder
i zwracając jej instancję zonBind()
. Klient otrzymujeBinder
i może go użyć, aby uzyskać bezpośredni dostęp do metod publicznych dostępnych w implementacjiBinder
lubService
.Jest to preferowana technika, gdy Twoja usługa jest tylko elementem roboczym Twojej aplikacji. Jedynym przypadkiem użycia, w którym nie jest preferowany sposób tworzenia interfejsu, jest sytuacja, gdy usługa jest używana przez inne aplikacje lub różne procesy.
- Korzystanie z komunikatora
- Jeśli chcesz, aby Twój interfejs działał w różnych procesach, możesz utworzyć interfejs usługi za pomocą
Messenger
. W ten sposób usługa definiuje elementHandler
, który odpowiada na różne typy obiektówMessage
.Handler
jest podstawą obiektuMessenger
, który może następnie współużytkowaćIBinder
z klientem, umożliwiając mu wysyłanie poleceń do usługi za pomocą obiektówMessage
. Dodatkowo klient może zdefiniować własnyMessenger
, aby usługa mogła wysyłać wiadomości z powrotem.Jest to najprostszy sposób komunikacji międzyprocesowej (IPC), ponieważ
Messenger
umieszcza wszystkie żądania w jednym wątku, więc nie musisz projektować usługi w taki sposób, aby była bezpieczna dla wątków. - Użyj AIDL
- Android Interface Definition Language (AIDL) rozkłada obiekty na podstawowe, które system operacyjny może zrozumieć, i łączy je w różne procesy na potrzeby IPC. Poprzednia metoda, wykorzystująca
Messenger
, w rzeczywistości opiera się na AIDL.Jak wspomnieliśmy w poprzedniej sekcji,
Messenger
tworzy kolejkę wszystkich żądań klienta w 1 wątku, więc usługa odbiera żądania pojedynczo. Jeśli jednak chcesz, aby Twoja usługa jednocześnie obsługiwała wiele żądań, możesz użyć AIDL bezpośrednio. W takim przypadku usługa musi być przystosowana do obsługi wątków i umożliwiać wielowątkowość.Aby bezpośrednio korzystać z AIDL, utwórz plik
.aidl
definiujący interfejs programowania. Narzędzia pakietu Android SDK używają tego pliku do wygenerowania klasy abstrakcyjnej, która implementuje interfejs i obsługuje protokół IPC, którą możesz potem rozszerzyć w swojej usłudze.
Uwaga: w przypadku większości aplikacji AIDL nie jest najlepszym rozwiązaniem do utworzenia usługi powiązanej, ponieważ może wymagać funkcji wielowątkowości i prowadzić do bardziej skomplikowanej implementacji. Dlatego w tym dokumencie nie omawiamy sposobu ich używania w Twojej usłudze. Jeśli masz pewność, że musisz bezpośrednio korzystać z AIDL, zapoznaj się z dokumentem AIDL.
Rozszerz klasę Binder
Jeśli tylko aplikacja lokalna korzysta z Twojej usługi i nie musi pracować między procesami, możesz wdrożyć własną klasę Binder
, która zapewni 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ęstsze. Takie rozwiązanie sprawdza się np. w przypadku aplikacji muzycznej, która musi powiązać działanie z własną usługą, która odtwarza muzykę w tle.
Ustawienia należy skonfigurować w następujący sposób:
- W swojej usłudze utwórz instancję
Binder
, która wykona jedną z tych czynności:- Zawiera metody publiczne, które może wywoływać klient.
- Zwraca bieżącą instancję
Service
, która zawiera metody publiczne, które może wywoływać. - Zwraca instancję innej klasy hostowanej przez usługę z publicznymi metodami, które może wywołać klient.
- Zwróć to wystąpienie
Binder
z metody wywołania zwrotnegoonBind()
. - W kliencie odbierz atrybut
Binder
z metody wywołania zwrotnegoonServiceConnected()
i wykonaj wywołania powiązanej usługi, 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 również muszą być w tym samym procesie, ponieważ ta technika nie łączy żadnych procesów.
Oto przykład usługi zapewniającej klientom dostęp do metod w tej 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 klientom metodę getService()
, która może pobrać bieżącą instancję LocalService
. Dzięki temu klienci mogą wywoływać w usłudze metody publiczne. Na przykład klienci mogą wywołać getRandomNumber()
z usługi.
Oto działanie, które wiąże się z elementem LocalService
i wywołuje po kliknięciu przycisku getRandomNumber()
:
Kotlin
class BindingActivity : Activity() { private lateinit var mService: LocalService 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) { 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) 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 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); 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) { mBound = false; } }; }
Poprzedni przykład pokazuje, jak klient łączy się z usługą za pomocą implementacji ServiceConnection
i wywołania zwrotnego onServiceConnected()
. Więcej informacji o procesie łączenia z usługą znajdziesz w następnej sekcji.
Uwaga: w poprzednim przykładzie metoda onStop()
usuwa powiązanie klienta z usługą.
Usuń powiązanie klientów z usługami 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.
Korzystaj z komunikatora
Jeśli chcesz, aby Twoja usługa komunikowała się z procesami zdalnymi, możesz udostępnić jej interfejs za pomocą Messenger
. Ta technika umożliwia przeprowadzanie komunikacji międzyprocesowej (IPC) bez konieczności korzystania z AIDL.
Użycie interfejsu Messenger
jest łatwiejsze niż używanie AIDL, ponieważ Messenger
umieszcza wszystkie wywołania usługi w kolejce. Interfejs czysty AIDL wysyła jednoczesne żądania do usługi, która musi obsługiwać wielowątkowość.
W przypadku większości aplikacji usługa nie musi obsługiwać wielowątkowości, więc użycie interfejsu Messenger
umożliwia usłudze obsługę pojedynczych wywołań. Jeśli Twoja usługa ma być wielowątkowa, zdefiniuj interfejs za pomocą AIDL.
Oto jak należy korzystać z elementu Messenger
:
- Usługa implementuje element
Handler
, który otrzymuje wywołanie zwrotne o każdym wywołaniu od klienta. - Usługa używa
Handler
do tworzenia obiektuMessenger
(który jest odwołaniem doHandler
). Messenger
tworzyIBinder
, który usługa zwraca klientom zonBind()
.- Klienty używają
IBinder
do tworzenia instancjiMessenger
(która odwołuje się doHandler
usługi), której używa do wysyłania obiektówMessage
do usługi. - Usługa otrzymuje każdy element
Message
w elemencieHandler
, a konkretnie w metodziehandleMessage()
.
Dzięki temu nie ma metod, które klient mógłby wywołać w usłudze. Zamiast tego klient dostarcza wiadomości (obiekty Message
), które usługa odbiera w swoim pakiecie Handler
.
Oto prosta przykładowa usługa, która korzysta z 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(); } }
W metodzie handleMessage()
w Handler
usługa otrzymuje przychodzące Message
i decyduje, co zrobić, na podstawie elementu what
.
Klient musi tylko utworzyć Messenger
na podstawie wartości IBinder
zwróconej przez usługę i wysłać wiadomość za pomocą send()
. Oto przykład działania, które wiąże się z usługą i dostarcza do niej komunikat 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) 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); bound = false; } } }
Ten przykład nie pokazuje, jak usługa może odpowiedzieć klientowi.
Jeśli chcesz, by usługa odpowiadała, musisz też utworzyć w kliencie Messenger
.
Gdy klient otrzyma wywołanie zwrotne onServiceConnected()
, wysyła do usługi parametr Message
, który zawiera Messenger
klienta w parametrze replyTo
metody send()
.
Przykład obsługi dwukierunkowej komunikacji znajdziesz w przykładach
MessengerService.java
(usługa) i
MessengerServiceActivities.java
(klient).
Powiązanie z usługą
Komponenty aplikacji (klienty) mogą powiązać się z usługą, wywołując bindService()
. System Android wywołuje następnie metodę onBind()
usługi, która zwraca IBinder
w celu interakcji z usługą.
Powiązanie jest asynchroniczne, a bindService()
zwraca natychmiast bez zwracania klientowi IBinder
. Aby otrzymać IBinder
, klient musi utworzyć instancję ServiceConnection
i przekazać ją do bindService()
. Element ServiceConnection
zawiera metodę wywołania zwrotnego, którą system wywołuje, aby przesłać IBinder
.
Uwaga: powiązanie z usługą można powiązać tylko z działaniami, usługami i dostawcami treści – nie można powiązać ich z usługą od odbiornika.
Aby utworzyć powiązanie z usługą z poziomu klienta, wykonaj te czynności:
- Wdróż
ServiceConnection
.Twoja implementacja musi zastąpić 2 metody wywołania zwrotnego:
onServiceConnected()
- System wywołuje to działanie, aby dostarczyć element
IBinder
zwrócony przez metodęonBind()
usługi. onServiceDisconnected()
- System Android wywołuje tę czynność, gdy połączenie z usługą zostanie nieoczekiwanie utracone, na przykład gdy usługa ulegnie awarii lub zostanie przerwana. Ta czynność nie jest wywoływana, gdy klient usuwa powiązanie.
- Wywołaj
bindService()
, przekazując implementacjęServiceConnection
.Uwaga: jeśli metoda zwraca wartość false (fałsz), oznacza to, że klient nie ma prawidłowego połączenia z usługą. Wywołaj jednak
unbindService()
w kliencie. W przeciwnym razie klient zapobiega zamykaniu usługi w przypadku bezczynności. - Gdy system wywoła metodę wywołania zwrotnego
onServiceConnected()
, możesz rozpocząć wykonywanie wywołań usługi, korzystając z metod zdefiniowanych w interfejsie. - Aby odłączyć się od usługi, zadzwoń pod numer
unbindService()
.Jeśli klient jest nadal powiązany z usługą w momencie jego zniszczenia przez aplikację, zniszczenie go powoduje usunięcie powiązania. Lepiej jest usuwać powiązanie klienta natychmiast po zakończeniu interakcji z usługą. Spowoduje to wyłączenie nieaktywnej usługi. Więcej informacji o odpowiednich momentach tworzenia powiązania znajdziesz w sekcji Dodatkowe uwagi.
Poniższy przykład łączy klienta z usługą utworzoną wcześniej przez rozszerzenie klasy Binder. Wystarczy więc rzutować zwrócony IBinder
na klasę LocalBinder
i zażądać instancji LocalService
:
Kotlin
var mService: LocalService 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") 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"); mBound = false; } };
Za pomocą tego kodu ServiceConnection
klient może powiązać się z usługą, przekazując ją do interfejsu 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
bindService()
jestIntent
jawnie nazwa usługi, która ma zostać powiązana.Uwaga: jeśli chcesz utworzyć powiązanie z obiektem
Service
, upewnij się, że aplikacja jest bezpieczna, używając intencji wyraźnej. Korzystanie z niejawnej intencji uruchomienia usługi stanowi zagrożenie dla bezpieczeństwa, ponieważ nie można mieć pewności, która usługa odpowiada na intencję, a użytkownik nie może zobaczyć, która usługa się uruchamia. Począwszy od Androida 5.0 (poziom interfejsu API 21) system zgłasza wyjątek, jeśli wywołujeszbindService()
z ukrytą intencją. - Drugi parametr to obiekt
ServiceConnection
. - Trzeci parametr to flaga wskazująca opcje wiązania – zwykle
BIND_AUTO_CREATE
– do utworzenia usługi, jeśli jeszcze nie jest aktywna. Inne możliwe wartości toBIND_DEBUG_UNBIND
,BIND_NOT_FOREGROUND
lub0
(brak).
Uwagi dodatkowe
Oto kilka ważnych uwag na temat powiązania z usługą:
- Zawsze blokujej wyjątki
DeadObjectException
, które są wysyłane po zerwaniu połączenia. Jest to jedyny wyjątek zgłaszany przez metody zdalne. - Obiekty są zliczane w procesach.
- Wiązanie i usuwanie powiązania jest zwykle łączone w czasie wyświetlania i rozciągania w cyklu życia klienta, jak opisano w tych przykładach:
- Jeśli chcesz korzystać z usługi tylko wtedy, gdy Twoja aktywność jest widoczna, utwórz powiązanie w czasie
onStart()
, a usuń powiązanie podczasonStop()
. - Jeśli chcesz, aby Twoja aktywność otrzymywała odpowiedzi nawet wtedy, gdy jest zatrzymana w tle, utwórz powiązanie w trakcie
onCreate()
i usuń powiązanie podczasonDestroy()
. Pamiętaj, że oznacza to, że usługa musi korzystać z usługi przez cały czas jej działania, nawet w tle, więc gdy usługa jest w innym procesie, zwiększasz wagę tego procesu, co zwiększa prawdopodobieństwo jego zakończenia przez system.
Uwaga: nie wiąże się zwykle z powiązaniami ani usuwaniem podczas wywołań zwrotnych
onResume()
ionPause()
aktywności, ponieważ te wywołania zwrotne mają miejsce w każdym kroku cyklu życia. Ogranicz przetwarzanie do minimum.Poza tym, jeśli wiele działań w aplikacji jest powiązanych z tą samą usługą i nastąpi przejście między 2 z nich, usługa może zostać zniszczona i odtworzona jako bieżąca aktywność usuwa powiązanie (w trakcie wstrzymania) przed kolejnym wiązaniem (podczas wznowienia). Ta zmiana dotyczy sposobu, w jaki działania koordynują swoje cykle życia, opisaną w artykule Cykl życia aktywności.
- Jeśli chcesz korzystać z usługi tylko wtedy, gdy Twoja aktywność jest widoczna, utwórz powiązanie w czasie
Więcej przykładowego kodu pokazujący, jak utworzyć powiązanie z usługą, znajdziesz w klasie
RemoteService.java
w ApiDemos.
Zarządzanie cyklem życia powiązanej usługi
Gdy usługa jest niepowiązana ze wszystkimi klientami, system Android ją niszczy (chyba że zaczął korzystać z startService()
). Nie musisz więc zarządzać cyklem życia usługi, jeśli jest ona czysto powiązana. System Android zarządza nim za Ciebie
w zależności od tego, czy dotyczy on klientów.
Jeśli jednak zdecydujesz się na wdrożenie metody wywołania zwrotnego onStartCommand()
, musisz samodzielnie zatrzymać usługę, ponieważ usługa jest teraz uznawana za rozpoczętą. W tym przypadku usługa działa, dopóki nie zatrzyma się samodzielnie przy użyciu stopSelf()
lub innego komponentu wywołała element stopService()
, niezależnie od tego, czy jest powiązana z jakimiś klientami.
Dodatkowo, jeśli usługa jest uruchomiona i akceptuje wiązanie, to gdy system wywoła metodę onUnbind()
, możesz opcjonalnie zwrócić metodę true
, jeśli chcesz odebrać wywołanie onRebind()
, gdy klient następnym razem powiąże się z usługą. onRebind()
zwraca wartość nieważną, ale klient nadal otrzymuje wartość 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 artykule Omówienie usług.