نظرة عامة على الخدمات الخاضعة للحظر

الخدمة المرتبطة هي الخادم الموجود في واجهة خادم العميل. فهو يتيح لمكونات مثل الأنشطة، مثل الالتزام بالخدمة، وإرسال الطلبات، وتلقي الردود، وإجراء الاتصالات بين العمليات (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 الخاصة بك التي توفّر للعميل إمكانية الوصول المباشر إلى الطرق العامة في الخدمة.

ملاحظة: لا يعمل هذا الإجراء إلا إذا كان العميل والخدمة في التطبيق والعملية نفسيهما، الأكثر شيوعًا. على سبيل المثال، يعمل هذا بشكل جيد مع تطبيق موسيقي يحتاج إلى ربط نشاط بخدمته الخاصة التي تشغِّل الموسيقى في الخلفية.

لإعداد الأداة، يجب:

  1. في خدمتك، أنشِئ مثيل Binder يؤدي أيًا مما يلي:
    • يحتوي على طرق عامة يمكن للعميل استدعاءها.
    • تعرض مثيل Service الحالي، والذي يتضمّن طرقًا عامة يمكن للعميل استدعاءها.
    • تعرض مثيلاً لفئة أخرى تستضيفها الخدمة بالطرق العامة التي يمكن للعميل استدعاءها.
  2. يتم عرض هذا المثيل من النوع Binder من خلال طريقة معاودة الاتصال onBind().
  3. في العميل، يمكن تلقّي 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:

  1. تنفِّذ الخدمة عنصر Handler يتلقّى معاودة اتصال لكل مكالمة من عميل.
  2. تستخدم الخدمة السمة Handler لإنشاء عنصر Messenger (وهو مرجع إلى Handler).
  3. تنشئ Messenger عنصر IBinder بإرجاعه الخدمة إلى العملاء من onBind().
  4. يستخدم العملاء IBinder لإنشاء مثيل لـ Messenger (الذي يشير إلى Handler للخدمة)، والذي يستخدمه العميل لإرسال عناصر Message إلى الخدمة.
  5. تتلقّى الخدمة كل 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.

ملاحظة: يمكن فقط للأنشطة والخدمات وموفّري المحتوى الربط بخدمة، ولا يمكنك الربط بخدمة من جهاز استقبال البث.

للربط بخدمة من البرنامج، اتبع الخطوات التالية:

  1. نفِّذ ServiceConnection.

    يجب أن تلغي عملية التنفيذ طريقتين لمعاودة الاتصال:

    onServiceConnected()
    يستدعي النظام هذا الإجراء لتسليم IBinder التي تم إرجاعها من خلال طريقة onBind() الخاصة بالخدمة.
    onServiceDisconnected()
    يستدعي نظام Android هذا الإجراء عند فقدان الاتصال بالخدمة بشكل غير متوقّع، مثلاً عند تعطُّل الخدمة أو إنهائها. لا يتم استدعاء هذا عند إلغاء الربط.
  2. يمكنك الاتصال بـ bindService()، واجتياز تنفيذ ServiceConnection.

    ملاحظة: إذا عرضت الطريقة "خطأ"، لن يتوفّر لدى العميل اتصال صالح بالخدمة. ومع ذلك، يمكنك طلب الرمز unbindService() في عميلك. وبخلاف ذلك، يمنع العميل إيقاف الخدمة عندما يكون في وضع عدم النشاط.

  3. عندما يستدعي النظام طريقة معاودة الاتصال في onServiceConnected()، يمكنك البدء في إجراء اتصالات بالخدمة باستخدام الطرق التي تحدّدها الواجهة.
  4. لقطع اتصالك بالخدمة، يُرجى الاتصال بـ 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(). يوضح الشكل التالي منطق هذا النوع من دورة الحياة.

الشكل 1. دورة حياة الخدمة التي بدأت وتسمح أيضًا بالربط.

لمزيد من المعلومات حول دورة حياة خدمة تم بدؤها، يُرجى الاطِّلاع على نظرة عامة على الخدمات.