الخدمة المرتبطة هي الخادم الموجود في واجهة خادم العميل. فهو يتيح لمكونات مثل الأنشطة، مثل الالتزام بالخدمة، وإرسال الطلبات، وتلقي الردود، وإجراء الاتصالات بين العمليات (IPC). لا تعمل الخدمة المرتبطة عادةً إلا عندما تخدم مكون تطبيق آخر ولا تعمل في الخلفية إلى أجل غير مسمى.
يشرح هذا المستند طريقة إنشاء خدمة مرتبطة، بما في ذلك كيفية الربط بالخدمة من مكوّنات التطبيق الأخرى. للحصول على معلومات إضافية حول الخدمات بشكل عام، مثل كيفية تسليم الإشعارات من خدمة وإعداد الخدمة لتشغيلها في المقدّمة، يمكنك الرجوع إلى نظرة عامة على الخدمات.
الأساسيّات
الخدمة المرتبطة هي تنفيذ لفئة Service
يتيح للتطبيقات الأخرى الربط بها والتفاعل معها. لتوفير الربط لخدمة، عليك تنفيذ طريقة معاودة الاتصال onBind()
. تعرِض هذه الطريقة عنصر IBinder
يحدّد واجهة البرمجة التي يمكن للعملاء استخدامها للتفاعل مع الخدمة.
الربط بخدمة تم بدؤها
وفقًا لما نوقش في النظرة العامة على الخدمات، يمكنك إنشاء خدمة في مرحلة التشغيل والربط في الوقت نفسه. وهذا يعني أنّه يمكنك بدء خدمة من خلال الاتصال بـ startService()
، ما يتيح تشغيل الخدمة إلى أجل غير مسمى. يمكنك أيضًا السماح للعميل بالربط بالخدمة من خلال
الاتصال بـ bindService()
.
إذا سمحت ببدء الخدمة وربطها، فعند بدء الخدمة،
لا يقوم النظام بتدمير الخدمة عند إلغاء ربط جميع العملاء.
بدلاً من ذلك، يجب عليك إيقاف الخدمة بشكل صريح من خلال الاتصال بـ stopSelf()
أو stopService()
.
على الرغم من أنّك تتّبع عادةً السمة onBind()
أو onStartCommand()
، من الضروري
أحيانًا تنفيذهما. على سبيل المثال، قد يجد مشغّل الموسيقى أنّه من المفيد السماح بتشغيل خدمته إلى أجل غير مسمى، ويوفّر أيضًا الربط. وبهذه الطريقة، يمكن أن يؤدي النشاط إلى تشغيل الخدمة لتشغيل بعض الموسيقى،
ويستمر تشغيل الموسيقى حتى إذا غادر المستخدم التطبيق. بعد ذلك، عند عودة المستخدم إلى التطبيق، يمكن ربط النشاط بالخدمة لاستعادة إمكانية التحكّم
في التشغيل.
لمزيد من المعلومات عن دورة حياة الخدمة عند إضافة ربط إلى خدمة تم بدؤها، راجِع القسم إدارة دورة حياة الخدمة المرتبطة.
يرتبط العميل بالخدمة من خلال طلب الرقم
bindService()
. عند تفعيل هذه السمة، يجب أن توفّر السياسة
السمة ServiceConnection
التي تراقب الاتصال بالخدمة. وتوضح القيمة المعروضة bindService()
ما إذا كانت الخدمة المطلوبة متوفّرة وما إذا كان يُسمح للعميل بالوصول إليها.
عندما ينشئ نظام Android اتصالاً بين العميل والخدمة، فإنه يتصل بـ onServiceConnected()
على ServiceConnection
. تتضمّن طريقة onServiceConnected()
وسيطة IBinder
، يستخدمها العميل بعد ذلك للتواصل مع الخدمة المرتبطة.
يمكنك ربط عدة عملاء بخدمة في الوقت نفسه. ومع ذلك، يخزّن النظام قناة اتصال خدمة IBinder
في ذاكرة التخزين المؤقت.
بمعنى آخر، يستدعي النظام الطريقة onBind()
الخاصة بالخدمة لإنشاء IBinder
فقط عند ربط البرنامج الأول. بعد ذلك، يُسلِّم النظام نفس علامة IBinder
إلى
جميع العملاء الإضافيين المرتبطين بتلك الخدمة نفسها، بدون الاتّصال بـ
onBind()
مرة أخرى.
عند إلغاء ربط العميل الأخير بالخدمة، يمحو النظام الخدمة، ما لم تكن الخدمة قد بدأت باستخدام startService()
.
إنّ أهم جزء في تنفيذ خدمة الربط هو تحديد الواجهة التي تعرضها طريقة معاودة الاتصال onBind()
. يناقش القسم التالي عدة طرق يمكنك من خلالها تحديد واجهة IBinder
لخدمتك.
إنشاء خدمة مرتبطة
عند إنشاء خدمة توفر الربط، يجب توفير IBinder
توفّر واجهة برمجة يمكن للعملاء استخدامها للتفاعل مع الخدمة. هناك ثلاث طرق يمكنك من خلالها تحديد الواجهة:
- توسيع فئة الصنف Binder
- إذا كانت خدمتك خاصة بتطبيقك الخاص وتعمل في العملية نفسها التي يعمل بها العميل، وهو أمر شائع، يمكنك إنشاء واجهة من خلال توسيع الفئة
Binder
وعرض مثيل منها منonBind()
. يتلقّى العميلBinder
ويمكنه استخدامها للوصول مباشرةً إلى الطرق العلنية المتاحة في تنفيذBinder
أوService
.وهذا هو الأسلوب المفضّل عندما تكون خدمتك مجرد عامل في الخلفية لتطبيقك. إنّ حالة الاستخدام الوحيدة عندما لا تكون هذه الطريقة هي الطريقة المفضّلة لإنشاء الواجهة هي إذا كانت الخدمة مستخدَمة بواسطة تطبيقات أخرى أو عبر عمليات منفصلة.
- استخدام تطبيق Messenger
- إذا كنت بحاجة إلى واجهتك لكي تعمل ضمن عمليات مختلفة، يمكنك إنشاء واجهة للخدمة باستخدام
Messenger
. بهذه الطريقة، تحدّد الخدمةHandler
يستجيب لأنواع مختلفة من كائناتMessage
.يمثّل
Handler
هذا أساسMessenger
الذي يمكنه بعد ذلك مشاركةIBinder
مع العميل، ما يسمح للعميل بإرسال الأوامر إلى الخدمة باستخدام عناصرMessage
. بالإضافة إلى ذلك، يمكن للعميل تحديدMessenger
خاص به، حتى تتمكّن الخدمة من إرسال الرسائل مرة أخرى.وهذه هي أبسط طريقة لإجراء الاتصال البيني للعمليات (IPC)، لأنّ
Messenger
يضع جميع الطلبات في قائمة انتظار في سلسلة محادثات واحدة حتى لا تضطر إلى تصميم خدمتك بحيث تكون آمنة وخاضعة لسلسلة المحادثات. - استخدام AIDL
- تحلّل لغة تعريف واجهة Android (AIDL) العناصر إلى
مواد أولية يمكن لنظام التشغيل فهمها وتنظيمها في مختلف العمليات لتنفيذ
IPC. يعتمد الأسلوب السابق، المتمثل في استخدام
Messenger
، على لغة تعريف الارتباط (AIDL) كهيكلها الأساسي.كما هو مذكور في القسم السابق، تنشئ
Messenger
قائمة انتظار تتضمّن جميع طلبات العميل في سلسلة محادثات واحدة، وبالتالي تتلقّى الخدمة الطلبات واحدًا تلو الآخر. على الرغم من ذلك، إذا أردت أن تتعامل خدمتك مع طلبات متعددة في الوقت نفسه، يمكنك استخدام لغة تعريف واجهة برمجة التطبيقات (AIDL) مباشرةً. في هذه الحالة، يجب أن تكون الخدمة آمنة وقادرة على إنشاء سلاسل محادثات متعددة.لاستخدام AIDL مباشرةً، أنشِئ ملف
.aidl
يحدّد واجهة البرمجة. تستخدم أدوات حزمة تطوير البرامج (SDK) لنظام التشغيل Android هذا الملف لإنشاء فئة مجردة تنفّذ الواجهة وتتعامل مع IPC، ويمكنك بعد ذلك توسيع نطاقها ضمن خدمتك.
ملاحظة: بالنسبة إلى معظم التطبيقات، لا تعتبر تقنية AIDL الخيار الأفضل لإنشاء خدمة مرتبطة، لأنّها قد تتطلّب إمكانات تعدد سلاسل التعليمات وقد تؤدي إلى عملية تنفيذ أكثر تعقيدًا. لذلك، لا يناقش هذا المستند كيفية استخدامه لخدمتك. إذا كنت متأكّدًا من أنّك بحاجة إلى استخدام لغة تعريف واجهة برمجة التطبيقات (AIDL) مباشرةً، يمكنك الاطّلاع على مستند AIDL.
توسيع فئة Binder
إذا كان التطبيق المحلي فقط يستخدم خدمتك ولم يكن بحاجة إلى تنفيذ إجراءات على مستوى جميع العمليات، يمكنك استخدام فئة Binder
الخاصة بك التي توفّر للعميل إمكانية الوصول المباشر إلى الطرق العامة في الخدمة.
ملاحظة: لا يعمل هذا الإجراء إلا إذا كان العميل والخدمة في التطبيق والعملية نفسيهما، الأكثر شيوعًا. على سبيل المثال، يعمل هذا بشكل جيد مع تطبيق موسيقي يحتاج إلى ربط نشاط بخدمته الخاصة التي تشغِّل الموسيقى في الخلفية.
لإعداد الأداة، يجب:
- في خدمتك، أنشِئ مثيل
Binder
يؤدي أيًا مما يلي:- يحتوي على طرق عامة يمكن للعميل استدعاءها.
- تعرض مثيل
Service
الحالي، والذي يتضمّن طرقًا عامة يمكن للعميل استدعاءها. - تعرض مثيلاً لفئة أخرى تستضيفها الخدمة بالطرق العامة التي يمكن للعميل استدعاءها.
- يتم عرض هذا المثيل من النوع
Binder
من خلال طريقة معاودة الاتصالonBind()
. - في العميل، يمكن تلقّي
Binder
من طريقة معاودة الاتصالonServiceConnected()
وإجراء مكالمات مع الخدمة المرتبطة باستخدام الطرق المتوفّرة.
ملاحظة: يجب أن تكون الخدمة والعميل في التطبيق نفسه حتى يتمكّن العميل من بث العنصر المعروض واستدعاء واجهات برمجة التطبيقات الخاصة به بشكل صحيح. يجب أن تكون الخدمة والعميل أيضًا في نفس العملية، لأن هذه التقنية لا تقوم بأي تنظيم عبر العمليات.
على سبيل المثال، في ما يلي خدمة توفّر للعملاء إمكانية الوصول إلى طرق في الخدمة من خلال تنفيذ 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
الإجراء getService()
للعملاء لاسترداد المثيل الحالي من LocalService
. يتيح ذلك للعملاء استدعاء الطرق العامة في الخدمة. على سبيل المثال، يمكن للعملاء الاتصال برقم getRandomNumber()
من الخدمة.
في ما يلي نشاط يرتبط بـ LocalService
ويطلب 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; } }; }
يوضِّح النموذج السابق كيفية ربط العميل بالخدمة باستخدام تطبيق
ServiceConnection
وردّ الاتصال onServiceConnected()
. يقدّم القسم التالي المزيد من المعلومات حول عملية الربط بالخدمة.
ملاحظة: في المثال السابق، تؤدي الطريقة
onStop()
إلى إلغاء ربط العميل بالخدمة.
عليك إلغاء ربط العملاء من الخدمات في الأوقات المناسبة، كما تمت مناقشته في قسم
ملاحظات إضافية.
لمزيد من نماذج الرموز، يُرجى الاطّلاع على الصف
LocalService.java
والصف
LocalServiceActivities.java
في ApiDemos.
استخدام مراسلة
إذا كنت بحاجة إلى خدمتك للاتصال بالعمليات عن بُعد، يمكنك استخدام Messenger
لتوفير الواجهة لخدمتك. تتيح لك هذه التقنية إجراء اتصال بين العمليات (IPC) بدون الحاجة إلى استخدام AIDL.
إنّ استخدام Messenger
للواجهة هو أبسط من استخدام AIDL لأنّ Messenger
يضع جميع المكالمات الواردة إلى الخدمة في قائمة انتظار. ترسل واجهة AIDL الخالصة طلبات متزامنة إلى
الخدمة، التي يجب أن تعالج بعد ذلك سلاسل التعليمات المتعددة.
بالنسبة إلى معظم التطبيقات، لا تحتاج الخدمة إلى تنفيذ سلاسل محادثات متعددة، لذا يتيح استخدام Messenger
للخدمة معالجة مكالمة واحدة في كل مرة. إذا كان من المهم أن تكون الخدمة متعددة السلاسل، استخدِم AIDL لتحديد الواجهة.
في ما يلي ملخّص لطريقة استخدام Messenger
:
- تنفِّذ الخدمة عنصر
Handler
يتلقّى معاودة اتصال لكل مكالمة من عميل. - تستخدم الخدمة السمة
Handler
لإنشاء عنصرMessenger
(وهو مرجع إلىHandler
). - تنشئ
Messenger
عنصرIBinder
بإرجاعه الخدمة إلى العملاء منonBind()
. - يستخدم العملاء
IBinder
لإنشاء مثيل لـMessenger
(الذي يشير إلىHandler
للخدمة)، والذي يستخدمه العميل لإرسال عناصرMessage
إلى الخدمة. - تتلقّى الخدمة كل
Message
فيHandler
، وعلى وجه التحديد في طريقةhandleMessage()
.
بهذه الطريقة، لا توجد طرق يمكن للعميل من خلالها الاتصال بالخدمة. بدلاً من ذلك، يُسلِّم العميل الرسائل (عناصر Message
) التي تتلقّاها الخدمة في Handler
.
في ما يلي مثال بسيط على خدمة تستخدم واجهة 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(); } }
الطريقة handleMessage()
في
Handler
هي المكان الذي تتلقّى فيه الخدمة Message
الوارد
وتحدِّد ما يجب فعله استنادًا إلى المستخدم في what
.
كل ما يحتاج إليه العميل هو إنشاء Messenger
استنادًا إلى IBinder
الذي تعرضه الخدمة وإرسال رسالة باستخدام send()
. على سبيل المثال، في ما يلي نشاط يرتبط بالخدمة ويقدم رسالة 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; } } }
لا يوضح هذا المثال كيف يمكن للخدمة أن تستجيب للعميل.
إذا أردت أن تستجيب الخدمة، عليك أيضًا إنشاء Messenger
في البرنامج.
عندما يتلقّى العميل معاودة الاتصال onServiceConnected()
، يرسل Message
إلى الخدمة التي تتضمّن Messenger
لدى العميل في المعلَمة replyTo
لطريقة send()
.
يمكنك الاطّلاع على مثال على كيفية توفير رسائل ثنائية في نموذجَي
MessengerService.java
(الخدمة) و
MessengerServiceActivities.java
(العميل).
الربط بخدمة
يمكن لمكوّنات التطبيق (البرامج) الربط بخدمة عن طريق طلب الرمز
bindService()
. بعد ذلك، يستدعي نظام Android طريقة onBind()
الخاصة بالخدمة، وتعرض هذه الطريقة IBinder
للتفاعل مع الخدمة.
ويكون الربط غير متزامن، وتعرض bindService()
على الفور بدون عرض IBinder
إلى البرنامج. لاستلام IBinder
، على العميل إنشاء مثيل ServiceConnection
وتمريره إلى bindService()
. يتضمّن ServiceConnection
طريقة معاودة الاتصال يطلبها
النظام لتقديم IBinder
.
ملاحظة: يمكن فقط للأنشطة والخدمات وموفّري المحتوى الربط بخدمة، ولا يمكنك الربط بخدمة من جهاز استقبال البث.
للربط بخدمة من البرنامج، اتبع الخطوات التالية:
- نفِّذ
ServiceConnection
.يجب أن تلغي عملية التنفيذ طريقتين لمعاودة الاتصال:
onServiceConnected()
- يستدعي النظام هذا الإجراء لتسليم
IBinder
التي تم إرجاعها من خلال طريقةonBind()
الخاصة بالخدمة. onServiceDisconnected()
- يستدعي نظام Android هذا الإجراء عند فقدان الاتصال بالخدمة بشكل غير متوقّع، مثلاً عند تعطُّل الخدمة أو إنهائها. لا يتم استدعاء هذا عند إلغاء الربط.
- يمكنك الاتصال بـ
bindService()
، واجتياز تنفيذServiceConnection
.ملاحظة: إذا عرضت الطريقة "خطأ"، لن يتوفّر لدى العميل اتصال صالح بالخدمة. ومع ذلك، يمكنك طلب الرمز
unbindService()
في عميلك. وبخلاف ذلك، يمنع العميل إيقاف الخدمة عندما يكون في وضع عدم النشاط. - عندما يستدعي النظام طريقة معاودة الاتصال في
onServiceConnected()
، يمكنك البدء في إجراء اتصالات بالخدمة باستخدام الطرق التي تحدّدها الواجهة. - لقطع اتصالك بالخدمة، يُرجى الاتصال بـ
unbindService()
.إذا كان العميل لا يزال مرتبطًا بالخدمة عندما يدمر التطبيق العميل، يؤدي التدمير إلى إلغاء ربط العميل. من أفضل الممارسات إلغاء ربط العميل بمجرد الانتهاء من التفاعل مع الخدمة. يؤدي ذلك إلى السماح بإيقاف الخدمة في وضع عدم النشاط. ولمزيد من المعلومات عن الأوقات المناسبة للربط وإلغاء الربط، راجِع قسم ملاحظات إضافية.
يربط المثال التالي العميل بالخدمة التي تم إنشاؤها سابقًا من خلال تمديد فئة الصنف Binder، لذلك كل ما عليك فعله هو إرسال IBinder
المعروض إلى الفئة LocalBinder
وطلب المثيل 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; } };
باستخدام ServiceConnection
، يمكن للعميل الربط بالخدمة من خلال تمريرها إلى bindService()
، كما هو موضّح في المثال التالي:
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);
- المعلَمة الأولى في السمة
bindService()
هيIntent
التي تسمي الخدمة لربطها بشكل صريح.تنبيه: إذا كنت تستخدم غرضًا للربط بـ
Service
، تأكّد من أنّ تطبيقك آمن باستخدام هدف صريح. يمثل استخدام نية ضمنية لبدء الخدمة خطرًا أمنيًا، لأنك لا تستطيع التأكد من الخدمة التي تستجيب للقصد، ولا يمكن للمستخدم معرفة الخدمة التي تبدأ. بدءًا من Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات)، يطرح النظام استثناءً في حال طلب البياناتbindService()
بنيّة ضمنية. - المعلمة الثانية هي الكائن
ServiceConnection
. - والمَعلمة الثالثة هي علامة تشير إلى خيارات الربط، وعادةً ما تكون
BIND_AUTO_CREATE
، لإنشاء الخدمة إذا لم تكن نشطة. والقيم الأخرى المحتملة هيBIND_DEBUG_UNBIND
أوBIND_NOT_FOREGROUND
أو0
لعدم وجود أي قيمة.
ملاحظات إضافية
في ما يلي بعض الملاحظات المهمة حول الربط بخدمة:
- حصر استثناءات
DeadObjectException
دائمًا، التي يتم طرحها عند انقطاع الاتصال. هذا هو الاستثناء الوحيد الذي تُطرحه الطرق عن بُعد. - العناصر هي مرجع يتم عدّه في كل العمليات.
- يتم عادةً إقران عملية الربط وإلغاء الربط خلال لحظات
الإزالة والإزالة خلال مراحل نشاط العميل، كما هو موضّح في
الأمثلة التالية:
- إذا كنت تحتاج إلى التفاعل مع الخدمة فقط عندما يكون نشاطك مرئيًا، يمكنك الربط خلال
onStart()
وإلغاء الربط خلالonStop()
. - إذا أردت أن يتلقّى نشاطك ردودًا حتى عندما يكون غير مفعَّل في الخلفية، عليك الربط أثناء
onCreate()
وإلغاء الربط خلالonDestroy()
. احذر أن هذا يعني أن نشاطك يحتاج إلى استخدام الخدمة طوال مدة تشغيلها، حتى في الخلفية، لذلك عندما تكون الخدمة في عملية أخرى، ستزيد من حجم العملية ومن المرجح أن يؤدي النظام إلى إنهائها.
ملاحظة: لا تحتاج عادةً إلى الربط وإلغاء الربط أثناء عمليّتَي استدعاء
onResume()
وonPause()
لنشاطك التجاري، لأنّ عمليات استدعاء الاستدعاء هذه تحدث في كل عملية انتقال في مراحل النشاط. احرص على الحد الأدنى من المعالجة التي تحدث خلال عمليات النقل هذه.بالإضافة إلى ذلك، إذا كانت هناك عدة أنشطة في تطبيقك ترتبط بالخدمة نفسها وكان هناك انتقال بين اثنين من هذه الأنشطة، قد يتم إتلاف الخدمة وإعادة إنشائها عندما يتم إلغاء ربط النشاط الحالي (أثناء الإيقاف المؤقت) قبل الربط التالي (أثناء السيرة الذاتية). يتم وصف انتقال النشاط هذا في كيفية تنسيق الأنشطة لمراحل نشاطها في دورة حياة النشاط.
- إذا كنت تحتاج إلى التفاعل مع الخدمة فقط عندما يكون نشاطك مرئيًا، يمكنك الربط خلال
للحصول على مزيد من نماذج الرموز التي توضّح كيفية الربط بخدمة، يُرجى الاطّلاع على الفئة
RemoteService.java
في ApiDemos.
إدارة دورة حياة الخدمة المرتبطة
عند إلغاء ربط إحدى الخدمات لدى جميع العملاء، يلحق بها نظام Android تدميرًا لها
(ما لم تكن قد بدأت في استخدام
startService()
).
وبالتالي، لن تضطر إلى إدارة مراحل نشاط خدمتك إذا كانت مجرّد خدمة مرتبطة. ويديرها نظام Android نيابةً عنك استنادًا
إلى ما إذا كان مرتبطًا بأي عملاء أم لا.
أما إذا اخترت استخدام طريقة معاودة الاتصال بـ "onStartCommand()
"، فيجب إيقاف الخدمة صراحةً، لأن الخدمة أصبحت الآن بدأت. في هذه الحالة، تعمل الخدمة إلى أن يتم إيقاف الخدمة تلقائيًا باستخدام stopSelf()
أو مكوّن آخر يستدعي stopService()
، بغض النظر عمّا إذا كانت الخدمة مرتبطة بأيّ عملاء أم لا.
بالإضافة إلى ذلك، إذا بدأت الخدمة ووافقت على الربط، عند استدعاء النظام
لطريقة onUnbind()
، يمكنك اختياريًا الرجوع إلى
true
إذا كنت تريد تلقّي مكالمة على الرقم onRebind()
في المرة التالية التي يربط فيها أحد البرامج بالخدمة. يعرض الخطأ onRebind()
قيمة فارغة، ولكن مع ذلك، يتلقّى العميل IBinder
عند معاودة الاتصال بخدمة onServiceConnected()
.
يوضح الشكل التالي منطق هذا النوع من دورة الحياة.
![](https://developer.android.com/static/images/fundamentals/service_binding_tree_lifecycle.png?authuser=6&hl=ar)
الشكل 1. دورة حياة الخدمة التي بدأت وتسمح أيضًا بالربط.
لمزيد من المعلومات حول دورة حياة خدمة تم بدؤها، يُرجى الاطِّلاع على نظرة عامة على الخدمات.