Dịch vụ ràng buộc là máy chủ trong giao diện máy khách – máy chủ. Thư viện này cho phép các thành phần như 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 hoạt động giao tiếp liên quy trình (IPC). Dịch vụ ràng buộc thường chỉ tồn tại trong khi phân phát một thành phần khác của ứng dụng và không chạy ở chế độ 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ới 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ụ nói chung, chẳng hạn như cách gửi thông báo của một dịch vụ và thiết lập để dịch vụ đó chạy ở nền trước, hãy tham khảo nội dung 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 lớp đó và tương tác với lớp đó. Để cung cấp liên kết cho một dịch vụ, bạn triển khai phương thức gọi lại onBind()
. Phương thức này trả về một đối tượng IBinder
xác định 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ụ.
Liên kết với dịch vụ đã bắt đầu
Như đã thảo luận trong phần Tổng quan về dịch vụ, bạn có thể tạo một dịch vụ vừa được bắt đầu vừa được ràng buộc. Tức là bạn có thể bắt đầu một dịch vụ bằng cách gọi startService()
để cho phép dịch vụ 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 gọi bindService()
.
Nếu bạn cho phép dịch vụ bắt đầu và liên kết, thì khi dịch vụ bắt đầu, hệ thống sẽ không huỷ liên kết dịch vụ khi mọi ứng dụng khách đều 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()
, nhưng đôi khi, bạn cần triển khai cả hai. Ví dụ: trình phát nhạc có thể thấy hữu ích khi cho phép dịch vụ chạy vô thời hạn và có thể cung cấp tính năng liên kết. Bằng cách này, một hoạt động có thể bắt đầu dịch vụ để phát một số bản 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 quá trình phát.
Để 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 một dịch vụ bằng cách gọi bindService()
. Khi thực hiện, dịch vụ này phải cung cấp phương thức triển khai của ServiceConnection
, giúp 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 có tồn tại hay không và ứng dụng có được phép truy cập vào dịch vụ đó hay không.
Khi tạo kết nối giữa ứng dụng và dịch vụ, hệ thống Android sẽ gọi onServiceConnected()
trên ServiceConnection
. Phương thức onServiceConnected()
bao gồm một đối số IBinder
mà sau đó ứng dụng sẽ dùng để giao tiếp với dịch vụ ràng buộc.
Bạn có thể kết nối đồng thời nhiều khách hàng với một dịch vụ. Tuy nhiên, hệ thống sẽ lưu kênh liên lạc dịch vụ IBinder
vào bộ nhớ đệm.
Nói cách khác, hệ thống sẽ gọi phương thức onBind()
của dịch vụ để chỉ tạo IBinder
khi ứng dụng đầu tiên liên kết. Sau đó, hệ thống sẽ phân phối chính IBinder
đó cho tất cả ứng dụng bổ sung liên kết với cùng dịch vụ đó mà không cần gọi lại onBind()
.
Khi ứng dụng gần đây nhất huỷ liên kết với dịch vụ, hệ thống sẽ huỷ dịch vụ, trừ phi dịch vụ này 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ề. Phần sau đây thảo luận một số cách để xác định giao diện IBinder
của dịch vụ.
Tạo 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 một 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ó 3 cách để xác định giao diện:
- Mở rộng lớp Binder
- Nếu dịch vụ của bạn dành riêng cho ứng dụng của bạn và chạy trong cùng một quy trình như ứng dụng khách (điều này phổ biến), hãy tạo giao diện bằng cách mở rộng lớp
Binder
và trả về một thực thể của lớp đó từonBind()
. Ứng dụng nhận đượcBinder
và có thể sử dụng đối tượng này để truy cập trực tiếp vào các phương thức công khai có trong quá trình triển khaiBinder
hoặcService
.Đây là kỹ thuật được ưu tiên khi dịch vụ của bạn chỉ là một trình chạy nền cho ứng dụng 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 ưu tiên để tạo giao diện là khi các ứng dụng khác dùng dịch vụ của bạn hoặc trên các quy trình riêng biệt.
- Sử dụng Messenger
- Nếu cần giao diện để hoạt động trên nhiều quy trình, bạn có thể tạo giao diện cho dịch vụ bằng
Messenger
. Bằng cách này, dịch vụ sẽ 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 ứng dụng, cho phép ứng dụng 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 định mộtMessenger
của riêng mình để dịch vụ có thể gửi lại thông báo.Đây là cách đơn giản nhất để thực hiện giao tiếp liên quy trình (IPC) vì
Messenger
sẽ xếp tất 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ụ sao cho an toàn với 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 các dữ liệu nguyên gốc mà hệ điều hành có thể hiểu và sắp xếp các đối tượng đó trên các quy trình để thực hiện IPC. Kỹ thuật trước đó sử dụng
Messenger
thực sự dựa trên AIDL làm cấu trúc cơ bả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 ứng dụng trong một luồng duy nhất. Do đó, dịch vụ sẽ lần lượt nhận từng yêu cầu một. Tuy nhiên, nếu muốn dịch vụ xử lý nhiều yêu cầu cùng lúc, thì bạn có thể trực tiếp sử dụng AIDL. Trong trường hợp này, dịch vụ của bạn phải an toàn về luồng và có khả năng đa luồng.Để sử dụng trực tiếp AIDL, hãy 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 giúp triển khai giao diện và xử lý IPC mà sau đó bạn có thể mở rộng trong dịch vụ của mình.
Lưu ý: Đối với hầu hết ứng dụng, AIDL không phải là lựa chọn tốt nhất để tạo dịch vụ ràng buộc, vì AIDL này có thể yêu cầu khả năng đa luồng và có thể dẫn đế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 về cách sử dụng cho dịch vụ của bạn. Nếu bạn chắc chắn cần sử dụng trực tiếp AIDL, hãy xem tài liệu về AIDL.
Mở rộng lớp Binder
Nếu chỉ ứng dụng cục bộ sử dụng dịch vụ và không cần phải thực hiện giữa các quy trình, thì bạn có thể triển khai lớp Binder
của riêng mình để cung cấp cho ứng dụng quyền truy cập trực tiếp vào các phương thức công khai trong dịch vụ.
Lưu ý: Thao tác này chỉ hoạt động nếu ứng dụng và dịch vụ nằm trong cùng một ứng dụng và quy trình (trường hợp phổ biến nhất). Ví dụ: cách này phù hợp với một ứng dụng nhạc cần liên kết một hoạt động với dịch vụ của chính ứng dụ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
thực hiện một trong những thao tác 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 có các phương thức công khai mà ứng dụng có thể gọi. - Trả về bản sao của một lớp khác do dịch vụ lưu trữ bằng các phương thức công khai mà ứng dụng có thể gọi.
- Trả về phiên bản thể hiện này của
Binder
từ phương thức gọi lạionBind()
. - Trong ứng dụng khách, 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 được cung cấp.
Lưu ý: Dịch vụ và ứng dụng phải nằm trong cùng một ứng dụng thì mới có thể truyền đối tượng được trả về và gọi đúng các API của đối tượng đó. Dịch vụ và ứng dụng khách 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ỳ thao tác phối hợp nào giữa các quy trình.
Ví dụ: dưới đây là một dịch vụ cung cấp cho ứng dụng quyền truy cập vào các phương thức trong dịch vụ thông qua việc 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 phiên bản 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 dịch vụ. 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 một nút được nhấp:
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ên 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 sẽ cung cấp thêm thông tin về quy trình liên kết với dịch vụ này.
Lưu ý: Trong ví dụ trước, phương thức onStop()
huỷ liên kết ứng dụng với dịch vụ.
Huỷ liên kết ứng dụng với các dịch vụ vào thời điểm thích hợp, như thảo luận trong phần Ghi chú bổ sung.
Để biết thêm mã mẫu, hãy xem lớp
LocalService.java
và lớp
LocalServiceActivities.java
trong Apidemos.
Sử dụng Messenger
Nếu cần dịch vụ 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 mình. 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 của bạn sẽ đơn giản hơn so với sử dụng AIDL vì Messenger
xếp mọi lệnh gọi đến dịch vụ vào hàng đợi. Một giao diện AIDL thuần tuý sẽ gửi các yêu cầu đồng thời đến dịch vụ mà sau đó phải xử lý đa luồng.
Đối với hầu hết ứng dụng, dịch vụ 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ý từng lệnh gọi một. Nếu quan trọng là dịch vụ của bạn phải đa luồng, hãy sử dụng AIDL để xác định giao diện.
Sau đâ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 lệnh gọi từ ứng dụng. - Dịch vụ sử dụng
Handler
để tạo đối tượngMessenger
(là đối tượng tham chiếu đếnHandler
). Messenger
tạo mộtIBinder
để dịch vụ trả về ứng dụng từonBind()
.- Ứng dụng sử dụng
IBinder
để tạo thực thểMessenger
(tham chiếu đếnHandler
của dịch vụ) mà ứng dụng sử dụng để gửi đối tượngMessage
đến dịch vụ. - Dịch vụ nhận từng
Message
trongHandler
, cụ thể là trong phương thứchandleMessage()
.
Theo đó, không có phương thức nào để ứng dụng gọi dịch vụ. Thay vào đó, ứng dụng sẽ phân phối thông báo (đối tượng Message
) mà dịch vụ nhận được trong Handler
của mình.
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 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 phần what
.
Ứng dụng chỉ cần tạo một Messenger
dựa trên IBinder
do dịch vụ trả về và gửi tin nhắn bằng send()
. Ví dụ: dưới đây là một hoạt động liên kết với dịch vụ 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 ứng dụng.
Nếu muốn dịch vụ phản hồi, bạn cũng cần tạo một Messenger
trong ứng dụng.
Khi nhận được lệnh gọi lại onServiceConnected()
, ứng dụng sẽ gửi Message
đến dịch vụ bao gồm 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 mẫu
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 (ứng dụng) có thể liên kết với một dịch vụ bằng cách gọi bindService()
. Sau đó, hệ thống Android sẽ gọi phương thức onBind()
của dịch vụ. Phương thức này trả về một IBinder
để tương tác với dịch vụ.
Liên kết này không đồng bộ và bindService()
sẽ trả về ngay lập tức mà không trả về IBinder
cho ứng dụng. Để nhận IBinder
, ứng dụng phải tạo một thực thể của ServiceConnection
và truyền phiên bản đó đến bindService()
. ServiceConnection
bao gồm một phương thức gọi lại mà hệ thống gọi để phân phối IBinder
.
Lưu ý: Chỉ các hoạt động, dịch vụ và nhà cung cấp nội dung 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 một dịch vụ của broadcast receiver.
Để liên kết với một dịch vụ từ ứng dụng khách 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 đè hai 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ụ trả về. onServiceDisconnected()
- Hệ thống Android gọi lệnh này khi kết nối với dịch vụ bị mất đột ngột, chẳng hạn như khi dịch vụ gặp sự cố hoặc bị dừng. Lệnh này không được gọi khi ứng dụng huỷ liên kết.
- Gọi
bindService()
, truyền phương thức triển khaiServiceConnection
.Lưu ý: Nếu phương thức này trả về giá trị "false", tức là ứng dụng khách của bạn 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 của bạn. Nếu không, ứng dụng khách của bạn sẽ ngăn 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 thực hiện lệnh gọi đến dịch vụ bằ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 bị liên kết với một dịch vụ khi ứng dụng của bạn huỷ bỏ ứng dụng đó, thì việc huỷ liên kết sẽ khiến ứng dụng đó bị huỷ liên kết. Phương pháp hay hơn là huỷ liên kết ứng dụng ngay sau khi tương tác xong với dịch vụ. Làm như vậy sẽ cho phép dịch vụ ở trạng thái rảnh sẽ tắt. Để biết thêm thông tin 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ụ đã tạo trước đó bằng cách mở rộng lớp Binder, vì vậy, bạn chỉ cần truyền IBinder
được trả về đến 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 dịch vụ đó vào 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
nêu rõ tên 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 dùng một ý định rõ ràng. Việc sử dụng ý định ngầm ẩn để bắt đầu một dịch vụ sẽ gây ra 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 sẽ phản hồi ý định và người dùng không thể biết dịch vụ nào sẽ bắt đầu. Kể từ Android 5.0 (API cấp 21), hệ thống sẽ gửi một ngoại lệ nếu bạn gọibindService()
với ý định ngầm ẩn. - Tham số thứ hai là đối tượng
ServiceConnection
. - Tham số thứ ba là một cờ cho biết các tuỳ chọn để liên kết (thường là
BIND_AUTO_CREATE
) để tạo dịch vụ nếu dịch vụ này chưa hoạt động. Các giá trị khác có thể là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 chặn các ngoại lệ
DeadObjectException
được gửi khi kết nối bị ngắt. Đây là ngoại lệ duy nhất được gửi bởi các phương thức từ xa. - Các đối tượng là tệp đối chiếu được tính trong các quy trình.
- Bạn thường ghép nối mối liên kết và huỷ liên kết trong các khoảnh khắc hiển thị và phân tích phù hợp trong vòng đời của ứng dụng, như 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 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 phản hồi ngay cả khi bị dừng ở chế độ nền, hãy liên kết trong
onCreate()
và huỷ liên kết trongonDestroy()
. Xin lưu ý rằng điều này có nghĩa là hoạt động cần sử dụng dịch vụ trong toàn bộ thời gian nó chạy, ngay cả ở chế độ nền. Vì vậy, khi dịch vụ đang ở một quy trình khác, bạn sẽ tăng trọng số của quá trình và hệ thống có nhiều khả năng sẽ tắt dịch vụ đó.
Lưu ý: Thường thì bạn không liên kết và huỷ liên kết trong các 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 quá trình chuyển đổi vòng đời. Hãy giữ cho quá trình xử lý xảy ra tại các quá trình chuyển đổi này luôn ở mức tối thiểu.Ngoài ra, nếu nhiều hoạt động trong ứng dụng liên kết với cùng một dịch vụ và có sự chuyển đổi giữa hai trong số các hoạt động đó, thì dịch vụ có thể bị huỷ và tạo lại khi hoạt động hiện tại huỷ liên kết (trong khi tạm dừng) trước khi hoạt động tiếp theo liên kết (trong quá trình tiếp tục). Việc chuyển đổi hoạt động này về cách các hoạt động điều phối vòng đời được mô tả trong phần 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 hiển thị, hãy liên kết trong
Để xem thêm mã mẫu cho biết cách liên kết với một dịch vụ, hãy xem lớp
RemoteService.java
trong Apidemos.
Quản lý vòng đời của dịch vụ ràng buộc
Khi một dịch vụ không được liên kết từ tất cả các ứng dụng, hệ thống Android sẽ huỷ bỏ dịch vụ đó (trừ phi dịch vụ đó bắt đầu sử dụng startService()
). Do đó, bạn không phải quản lý vòng đời của dịch vụ nếu đó chỉ là một dịch vụ ràng buộc. Hệ thống Android sẽ quản lý việc này cho bạn dựa trên việc ứng dụng có được liên kết với bất kỳ ứng dụng nào hay không.
Tuy nhiên, nếu chọn triển khai phương thức gọi lại onStartCommand()
, thì 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ể dịch vụ đó có được liên kết với bất kỳ ứng dụng nào hay không.
Ngoài ra, nếu dịch vụ đã khởi động 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ể tuỳ ý trả về true
nếu muốn nhận lệnh gọi đến onRebind()
vào lần tiếp theo ứng dụng khách liên kết với dịch vụ. onRebind()
trả về giá trị rỗng, nhưng ứng dụng vẫn nhận được IBinder
trong lệnh gọi lại onServiceConnected()
.
Hình dưới đây minh hoạ logic cho loại vòng đời này.
![](https://developer.android.com/static/images/fundamentals/service_binding_tree_lifecycle.png?authuser=6&hl=vi)
Hình 1. Vòng đời của một dịch vụ bắt đầu và cũng cho phép liên kết.
Để biết thêm thông tin về vòng đời của một dịch vụ đã bắt đầu, hãy xem phần Tổng quan về dịch vụ.