Dịch vụ ràng buộc là máy chủ trong giao diện máy khách-máy chủ. API này cho phép các thành phần chẳng hạn như các hoạt động liên kết với dịch vụ, gửi yêu cầu, nhận phản hồi và thực hiện giao tiếp liên quy trình (IPC). Một dịch vụ ràng buộc thường chỉ hoạt động trong khi phân phát một dịch vụ khác và không chạy trong nền vô thời hạn.
Tài liệu này mô tả cách tạo dịch vụ ràng buộc, bao gồm cả cách liên kết vào dịch vụ từ các thành phần khác của ứng dụng. Để biết thêm thông tin về các dịch vụ trong chung, chẳng hạn như cách gửi thông báo từ một dịch vụ và thiết lập để dịch vụ đó chạy ở nền trước, hãy tham khảo Tổng quan về dịch vụ.
Thông tin cơ bản
Dịch vụ ràng buộc là cách triển khai lớp Service
cho phép
các ứng dụng khác liên kết với nó và tương tác với nó. Để cung cấp liên kết cho một
bạn hãy triển khai phương thức gọi lại onBind()
. Chiến dịch này
phương thức trả về một đối tượng IBinder
xác định giao diện lập trình
mà khách hàng có thể sử dụng để tương tác với dịch vụ.
Liên kết với dịch vụ đã bắt đầu
Như đã thảo luận trong bài viết Tổng quan về dịch vụ,
bạn có thể tạo một dịch vụ vừa bắt đầu vừa bị ràng buộc. Tức là bạn có thể bắt đầu một
bằng cách gọi startService()
, cho phép
dịch vụ sẽ chạy vô thời hạn. Bạn cũng có thể cho phép ứng dụng liên kết với dịch vụ bằng cách
đang gọi bindService()
.
Nếu bạn cho phép bắt đầu và ràng buộc dịch vụ, thì khi dịch vụ bắt đầu,
hệ thống không huỷ liên kết dịch vụ khi tất cả ứng dụng huỷ liên kết.
Thay vào đó, bạn phải
dừng dịch vụ một cách rõ ràng bằng cách gọi stopSelf()
hoặc stopService()
.
Mặc dù bạn thường triển khai onBind()
hoặc onStartCommand()
, đôi khi
cần thiết để
triển khai cả hai. Ví dụ: một trình phát nhạc có thể thấy hữu ích khi cho phép dịch vụ của mình chạy
vô thời hạn mà còn đem lại sự ràng buộc. Bằng cách này, một hoạt động có thể khởi động dịch vụ để phát một số
nhạc và nhạc sẽ tiếp tục phát ngay cả khi người dùng rời khỏi ứng dụng. Sau đó, khi người dùng
quay lại ứng dụng, hoạt động có thể liên kết với dịch vụ để lấy lại quyền kiểm soát
video.
Để biết thêm thông tin về vòng đời của dịch vụ khi thêm liên kết vào một dịch vụ đã bắt đầu, hãy xem phần Quản lý vòng đời của dịch vụ ràng buộc.
Ứng dụng liên kết với dịch vụ bằng cách gọi
bindService()
. Khi đó, mã phải
cung cấp phương thức triển khai ServiceConnection
,
giám sát kết nối với dịch vụ. Giá trị trả về của
bindService()
cho biết liệu
dịch vụ được yêu cầu tồn tại và liệu ứng dụng có được phép truy cập vào dịch vụ đó hay không.
Thời gian
hệ thống Android tạo kết nối giữa ứng dụng và dịch vụ, nó
gọi onServiceConnected()
vào ngày ServiceConnection
. Chiến lược phát hành đĩa đơn
Phương thức onServiceConnected()
bao gồm IBinder
đối số mà sau đó ứng dụng dùng để giao tiếp với dịch vụ ràng buộc.
Bạn có thể kết nối nhiều ứng dụng với một dịch vụ cùng lúc. Tuy nhiên,
hệ thống sẽ lưu kênh giao tiếp của dịch vụ IBinder
vào bộ nhớ đệm.
Nói cách khác, hệ thống gọi onBind()
của dịch vụ
để tạo IBinder
chỉ khi lần đầu tiên
liên kết ứng dụng khách. Sau đó, hệ thống phân phối chính IBinder
đó đến
tất cả ứng dụng khác liên kết với cùng một dịch vụ đó mà không cần gọi
onBind()
nữa.
Khi ứng dụng khách cuối cùng huỷ liên kết với dịch vụ, hệ thống sẽ huỷ liên kết dịch vụ, trừ khi
dịch vụ đã được bắt đầu bằng startService()
.
Phần quan trọng nhất trong quá trình triển khai dịch vụ ràng buộc là xác định giao diện
mà phương thức gọi lại onBind()
trả về. Nội dung sau đây
thảo luận một số cách giúp bạn xác định
Giao diện IBinder
.
Tạo một dịch vụ ràng buộc
Khi tạo một dịch vụ cung cấp liên kết, bạn phải cung cấp IBinder
cung cấp giao diện lập trình mà ứng dụng có thể sử dụng để tương tác với dịch vụ. Có
là 3 cách bạn có thể xác định giao diện:
- Mở rộng lớp Binder
- Nếu dịch vụ dành riêng cho ứng dụng của bạn và chạy trong cùng một quy trình
thường là ứng dụng khách tạo giao diện bằng cách mở rộng
Binder
lớp và trả về một thực thể của hàm từonBind()
. Ứng dụng nhận đượcBinder
và có thể sử dụng nó để truy cập trực tiếp vào các phương thức công khai có trongBinder
hoặcService
.Đây là kỹ thuật ưu tiên khi dịch vụ của bạn chỉ là một trình chạy ở chế độ nền của riêng bạn . Trường hợp sử dụng duy nhất khi đây không phải là cách ưa thích để tạo giao diện là nếu dịch vụ của bạn được các ứng dụng khác hoặc trong các quy trình riêng biệt sử dụng.
- Sử dụng Messenger
- Nếu cần giao diện của bạn hoạt động trong nhiều quy trình, bạn có thể tạo
giao diện cho dịch vụ có
Messenger
. Bằng cách này, dịch vụ xác định mộtHandler
phản hồi nhiều loại đối tượngMessage
.Handler
này là cơ sở cho mộtMessenger
mà sau đó có thể chia sẻIBinder
với máy khách, cho phép máy khách gửi lệnh đến dịch vụ bằng cách sử dụng các đối tượngMessage
. Ngoài ra, ứng dụng có thể xác địnhMessenger
của của riêng mình để dịch vụ có thể gửi lại tin nhắn.Đây là cách đơn giản nhất để thực hiện giao tiếp liên quy trình (IPC), vì
Messenger
xếp hàng tất cả các yêu cầu vào một luồng duy nhất để bạn không phải thiết kế để dịch vụ của bạn an toàn theo luồng. - Sử dụng AIDL
- Ngôn ngữ định nghĩa giao diện Android (AIDL) phân tách các đối tượng thành
những dữ liệu gốc mà hệ điều hành có thể hiểu và tổng hợp chúng trong các quy trình để thực hiện
IPC. Kỹ thuật trước đây (sử dụng
Messenger
) thực ra dựa trên AIDL cấu trúc cơ bản của nó.Như đã đề cập trong phần trước,
Messenger
sẽ tạo một hàng đợi gồm tất cả yêu cầu của máy khách trong một luồng, nên dịch vụ sẽ nhận lần lượt từng yêu cầu. Nếu: tuy nhiên, nếu muốn dịch vụ của mình xử lý nhiều yêu cầu cùng lúc, thì bạn có thể sử dụng AIDL trực tiếp. Trong trường hợp này, dịch vụ của bạn phải an toàn cho luồng và có khả năng đa luồng.Để sử dụng trực tiếp AIDL, tạo một tệp
.aidl
xác định giao diện lập trình. Bộ công cụ SDK Android sử dụng tệp này để tạo một lớp trừu tượng triển khai giao diện và xử lý IPC mà bạn thì có thể mở rộng trong dịch vụ của bạn.
Lưu ý: Đối với hầu hết ứng dụng, AIDL không phải là lựa chọn phù hợp nhất để tạo một dịch vụ ràng buộc vì dịch vụ này có thể đòi hỏi khả năng đa luồng và có thể khiến việc triển khai phức tạp hơn. Do đó, tài liệu này không thảo luận cách để sử dụng cho dịch vụ của bạn. Nếu bạn chắc chắn rằng mình cần để sử dụng trực tiếp AIDL, hãy xem AIDL tài liệu.
Mở rộng lớp Binder
Nếu chỉ ứng dụng cục bộ sử dụng dịch vụ của bạn và không cần
xử lý nhiều quy trình,
thì bạn có thể triển khai lớp Binder
của riêng mình để cung cấp trực tiếp cho ứng dụng của bạn
truy cập vào các phương thức công khai trong dịch vụ.
Lưu ý: Tính năng này chỉ hoạt động nếu ứng dụng và dịch vụ giống nhau quy trình và ứng dụng phổ biến nhất. Ví dụ: đối tượng này phù hợp với một bản nhạc ứng dụng cần liên kết một hoạt động với dịch vụ riêng đang phát nhạc trong nền.
Dưới đây là cách thiết lập:
- Trong dịch vụ của bạn, hãy tạo một thực thể của
Binder
có chức năng này một trong các lệnh sau:- Chứa các phương thức công khai mà ứng dụng có thể gọi.
- Trả về thực thể
Service
hiện tại, trong đó có các phương thức công khai mà khách hàng có thể gọi. - Trả về một thực thể của một lớp khác được dịch vụ lưu trữ bằng các phương thức công khai khách hàng có thể gọi.
- Trả về thực thể này của
Binder
từ phương thức gọi lạionBind()
. - Trong ứng dụng, nhận
Binder
từ phương thức gọi lạionServiceConnected()
và thực hiện lệnh gọi đến dịch vụ ràng buộc bằng các phương thức đã cho.
Lưu ý: Dịch vụ và ứng dụng phải giống nhau để ứng dụng khách có thể truyền đối tượng được trả về và gọi API của đối tượng đó một cách chính xác. Dịch vụ và khách hàng cũng phải trong cùng một quy trình vì kỹ thuật này không thực hiện bất kỳ tổng hợp toàn bộ các quy trình.
Ví dụ: đây là một dịch vụ cung cấp cho khách hàng quyền truy cập vào các phương thức trong dịch vụ đó thông qua
cách triển khai 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
cung cấp phương thức getService()
để ứng dụng truy xuất
thực thể hiện tại của LocalService
. Điều này cho phép ứng dụng gọi các phương thức công khai trong
. Ví dụ: ứng dụng có thể gọi getRandomNumber()
từ dịch vụ.
Dưới đây là một hoạt động liên kết với LocalService
và gọi getRandomNumber()
khi người dùng nhấp vào một nút:
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; } }; }
Mẫu trước đó cho thấy cách ứng dụng liên kết với dịch vụ bằng cách triển khai
ServiceConnection
và lệnh gọi lại onServiceConnected()
. Phần tiếp theo
cung cấp thêm thông tin về quá trình liên kết này với dịch vụ.
Lưu ý: Trong ví dụ trước, thuộc tính
Phương thức onStop()
huỷ liên kết ứng dụng với dịch vụ.
Huỷ liên kết khách hàng khỏi các dịch vụ vào những thời điểm thích hợp, như được thảo luận trong
Ghi chú bổ sung.
Để biết thêm mã mẫu, hãy xem
Lớp LocalService.java
và
LocalServiceActivities.java
trong Apidemos.
Sử dụng Messenger
Nếu cần dịch vụ của mình giao tiếp với các quy trình từ xa, bạn có thể sử dụng
Messenger
để cung cấp giao diện cho dịch vụ của bạn. Kỹ thuật này cho phép
bạn thực hiện giao tiếp liên quy trình (IPC) mà không cần sử dụng AIDL.
Việc sử dụng Messenger
cho giao diện là
đơn giản hơn so với sử dụng AIDL vì Messenger
hàng đợi
tất cả các lệnh gọi đến dịch vụ. Giao diện AIDL thuần tuý gửi các yêu cầu đồng thời đến
để xử lý đa luồng.
Đối với hầu hết ứng dụng, dịch vụ này không cần thực hiện đa luồng, vì vậy, việc sử dụng Messenger
cho phép dịch vụ xử lý một lệnh gọi mỗi lần. Nếu quan trọng
để đảm bảo dịch vụ của bạn đa luồng, hãy sử dụng AIDL để xác định giao diện của bạn.
Dưới đây là phần tóm tắt về cách sử dụng Messenger
:
- Dịch vụ triển khai một
Handler
nhận lệnh gọi lại cho mỗi cuộc gọi từ một khách hàng. - Dịch vụ này sử dụng
Handler
để tạoMessenger
đối tượng (tham chiếu đếnHandler
). Messenger
tạo mộtIBinder
mà dịch vụ trả lại cho khách hàng từonBind()
.- Ứng dụng sử dụng
IBinder
để tạoMessenger
(tham chiếu đếnHandler
của dịch vụ) mà ứng dụng dùng để gửiMessage
đối với dịch vụ. - Dịch vụ nhận từng
Message
trongHandler
, cụ thể là trong phương thứchandleMessage()
.
Theo đó, sẽ không có phương thức nào để ứng dụng khách gọi dịch vụ. Thay vào đó,
ứng dụng gửi thông báo (Message
đối tượng) mà dịch vụ
nhận trong
Handler
.
Dưới đây là một dịch vụ mẫu đơn giản sử dụng giao diện 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(); } }
Phương thức handleMessage()
trong phần
Handler
là nơi dịch vụ nhận Message
đến
và quyết định việc cần làm, dựa trên thành viên what
.
Ứng dụng chỉ cần tạo Messenger
dựa trên IBinder
do dịch vụ trả về rồi gửi tin nhắn bằng send()
. Ví dụ: đây là một hoạt động liên kết với
và gửi thông báo MSG_SAY_HELLO
đến dịch vụ:
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; } } }
Ví dụ này không cho thấy cách dịch vụ có thể phản hồi với ứng dụng.
Nếu bạn muốn
để phản hồi, bạn cũng cần tạo một Messenger
trong ứng dụng khách.
Khi nhận được lệnh gọi lại onServiceConnected()
, ứng dụng sẽ gửi một Message
đến dịch vụ có chứa
Messenger
của ứng dụng trong tham số replyTo
của phương thức send()
.
Bạn có thể xem ví dụ về cách cung cấp thông báo hai chiều trong phần
MessengerService.java
(dịch vụ) và
MessengerServiceActivities.java
(ứng dụng).
Liên kết với một dịch vụ
Các thành phần ứng dụng (máy khách) có thể liên kết với một dịch vụ bằng cách gọi
bindService()
. Hệ điều hành Android
sau đó hệ thống sẽ gọi phương thức onBind()
của dịch vụ. Phương thức này trả về IBinder
để tương tác với
dịch vụ.
Liên kết không đồng bộ và bindService()
trả về ngay lập tức mà không trả về IBinder
cho
khách hàng. Để nhận IBinder
, ứng dụng phải tạo một
thực thể của ServiceConnection
rồi truyền nó đến bindService()
. ServiceConnection
bao gồm một phương thức gọi lại
các lệnh gọi hệ thống để cung cấp IBinder
.
Lưu ý: Chỉ các nhà cung cấp nội dung, dịch vụ và hoạt động mới có thể liên kết với một dịch vụ – bạn không thể liên kết với dịch vụ từ broadcast receiver.
Để liên kết với một dịch vụ từ ứng dụng của bạn, hãy làm theo các bước sau:
- Triển khai
ServiceConnection
.Quá trình triển khai của bạn phải ghi đè 2 phương thức gọi lại:
onServiceConnected()
- Hệ thống gọi hàm này để phân phối
IBinder
do phương thứconBind()
của dịch vụ. onServiceDisconnected()
- Hệ thống Android gọi lệnh này khi kết nối đến dịch vụ bất ngờ bị mất, chẳng hạn như khi dịch vụ gặp sự cố hoặc bị dừng. Đây không phải được gọi khi ứng dụng khách hủy liên kết.
- Gọi
bindService()
, truyền phần triển khaiServiceConnection
.Lưu ý: Nếu phương thức này trả về false, thì ứng dụng khách không có kết nối hợp lệ với dịch vụ. Tuy nhiên, hãy gọi
unbindService()
trong ứng dụng khách của bạn. Nếu không, khách hàng của bạn sẽ giữ dịch vụ tắt khi ở trạng thái rảnh. - Khi hệ thống gọi phương thức gọi lại
onServiceConnected()
, bạn có thể bắt đầu gọi dịch vụ bằng cách sử dụng các phương thức do giao diện xác định. - Để ngắt kết nối khỏi dịch vụ, hãy gọi
unbindService()
.Nếu ứng dụng của bạn vẫn liên kết với một dịch vụ khi ứng dụng huỷ bỏ, thì ứng dụng bị huỷ bỏ khiến ứng dụng huỷ liên kết. Tốt hơn là bạn nên huỷ liên kết với ứng dụng ngay khi hoàn tất tương tác với dịch vụ. Thao tác này sẽ tắt dịch vụ ở trạng thái rảnh. Thông tin khác về thời điểm thích hợp để liên kết và huỷ liên kết, hãy xem phần Ghi chú bổ sung.
Ví dụ sau đây kết nối ứng dụng với dịch vụ đã được tạo trước đó bởi
mở rộng lớp Binder, nên bạn chỉ cần truyền lớp được trả về
IBinder
vào lớp LocalBinder
và yêu cầu thực thể 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; } };
Với ServiceConnection
này, ứng dụng có thể liên kết với một dịch vụ
bằng cách truyền
thành bindService()
, như trong ví dụ sau:
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);
- Tham số đầu tiên của
bindService()
làIntent
đặt tên rõ ràng cho dịch vụ cần liên kết.Thận trọng: Nếu bạn sử dụng một ý định để liên kết với một
Service
, hãy đảm bảo ứng dụng của bạn được bảo mật bằng cách sử dụng một quy tắc rõ ràng ý định. Việc sử dụng ý định ngầm ẩn để bắt đầu một dịch vụ mối nguy hiểm về bảo mật vì bạn không thể chắc chắn dịch vụ nào phản hồi ý định, và người dùng không thể thấy dịch vụ nào bắt đầu. Kể từ Android 5.0 (API cấp 21), hệ thống ném một trường hợp ngoại lệ nếu bạn gọibindService()
có ý định ngầm ẩn. - Tham số thứ hai là đối tượng
ServiceConnection
. - Tham số thứ ba là một cờ chỉ ra các tuỳ chọn cho liên kết (thường là
BIND_AUTO_CREATE
) để tạo dịch vụ nếu chưa có sống động. Có thể sử dụng các giá trịBIND_DEBUG_UNBIND
,BIND_NOT_FOREGROUND
hoặc0
nếu không có.
Ghi chú khác
Dưới đây là một số lưu ý quan trọng về việc liên kết với một dịch vụ:
- Luôn bẫy các ngoại lệ
DeadObjectException
, được gửi khi bị ngắt kết nối. Đây là trường hợp ngoại lệ duy nhất được gửi bởi các phương thức từ xa. - Các đối tượng được tính trong các quy trình.
- Bạn thường ghép nối và huỷ liên kết trong
so khớp những khoảnh khắc đưa ra và tận hưởng trong vòng đời của khách hàng, như được mô tả trong
các ví dụ sau:
- Nếu bạn chỉ cần tương tác với dịch vụ khi hoạt động của bạn đang hiển thị, hãy liên kết trong
onStart()
và huỷ liên kết trongonStop()
. - Nếu bạn muốn hoạt động của mình nhận được phản hồi ngay cả khi đã dừng hoạt động trong
chạy ở chế độ nền, liên kết trong
onCreate()
rồi huỷ liên kết trongonDestroy()
. Xin lưu ý rằng điều này ngụ ý rằng cần phải sử dụng dịch vụ trong toàn bộ thời gian nó chạy, ngay cả trong nền, vì vậy, khi khi dịch vụ đang trong một quá trình khác, khi đó bạn tăng trọng số của quá trình và có nhiều khả năng bị hệ thống loại bỏ.
Lưu ý: Bạn thường không phải liên kết và huỷ liên kết trong lệnh gọi lại
onResume()
vàonPause()
của hoạt động, vì các lệnh gọi lại này xảy ra tại mỗi chuyển đổi vòng đời. Hãy giữ cho quá trình xử lý diễn ra ở những điểm chuyển đổi này ở mức tối thiểu.Ngoài ra, nếu nhiều hoạt động trong ứng dụng của bạn sẽ liên kết với cùng một dịch vụ và có chuyển đổi giữa hai trong số các hoạt động đó, dịch vụ có thể bị huỷ và tạo lại như huỷ liên kết hoạt động (trong thời gian tạm dừng) trước khi liên kết tiếp theo (trong quá trình tiếp tục). Chuyển đổi hoạt động này để biết cách các hoạt động điều phối vòng đời được mô tả trong bài viết Vòng đời hoạt động.
- Nếu bạn chỉ cần tương tác với dịch vụ khi hoạt động của bạn đang hiển thị, hãy liên kết trong
Để biết thêm mã mẫu cho thấy cách liên kết với một dịch vụ, hãy xem
RemoteService.java
trong Apidemos.
Quản lý vòng đời của một dịch vụ ràng buộc
Khi một dịch vụ không được liên kết khỏi tất cả ứng dụng, hệ thống Android sẽ huỷ bỏ dịch vụ đó
(trừ phi bắt đầu sử dụng
startService()
).
Vì vậy, bạn không phải quản lý vòng đời của dịch vụ nếu đó là
chỉ đơn thuần là một dịch vụ ràng buộc. Hệ thống Android quản lý tài khoản đó cho bạn dựa trên
cho dù nó có bị ràng buộc với bất kỳ ứng dụng khách nào hay không.
Tuy nhiên, nếu chọn triển khai phương thức gọi lại onStartCommand()
, bạn phải dừng dịch vụ một cách rõ ràng vì
dịch vụ hiện được coi là đã bắt đầu. Trong trường hợp này, dịch vụ sẽ chạy cho đến khi dịch vụ
tự dừng bằng stopSelf()
hoặc một thành phần khác gọi stopService()
, bất kể thành phần đó có được liên kết với thành phần nào
khách hàng.
Ngoài ra, nếu dịch vụ của bạn được bắt đầu và chấp nhận liên kết, thì khi hệ thống gọi
phương thức onUnbind()
, bạn có thể trả về (không bắt buộc)
true
nếu bạn muốn nhận lệnh gọi đến onRebind()
vào lần tiếp theo ứng dụng liên kết với dịch vụ. onRebind()
trả về giá trị trống, nhưng ứng dụng khách vẫn nhận được IBinder
trong
Gọi lại onServiceConnected()
.
Hình dưới đây minh hoạ logic cho loại vòng đời này.
Để biết thêm thông tin về vòng đời của một dịch vụ đã bắt đầu, hãy xem bài viết Tổng quan về dịch vụ.