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

Service هو مكون التطبيق الذي يمكنه تنفيذ عمليات طويلة الأمد في الخلفية. لا يوفر واجهة مستخدم. مرة واحدة قد تستمر الخدمة في العمل لبعض الوقت، حتى بعد أن ينتقل المستخدم إلى خدمة أخرى التطبيق. بالإضافة إلى ذلك، يمكن ربط مكون بخدمة ما للتفاعل معه وحتى تنفيذ الاتصال البيني للعمليات (IPC). على سبيل المثال، يمكن لإحدى الخدمات معالجة معاملات الشبكة أو تشغيل الموسيقى أو تنفيذ عمليات إدخال/إخراج للملفات أو التفاعل مع موفّر محتوى، كل ذلك من الخلفية.

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

أنواع الخدمات

هذه هي الأنواع الثلاثة المختلفة من الخدمات:

واجهة

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

عند استخدام خدمة تعمل في المقدّمة، يجب عرض إشعار لكي يكون المستخدمون على دراية تامة بأنّ الخدمة قيد التشغيل. يتعذّر إرسال هذا الإشعار إلا إذا تم إيقاف الخدمة أو إزالتها من المقدّمة.

مزيد من المعلومات حول كيفية ضبط الإعدادات الخدمات التي تعمل في المقدّمة في التطبيق.

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

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

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

مربوط
يتم ربط الخدمة عندما يرتبط بها مكوّن تطبيق من خلال طلب bindService(). تقدم الخدمة المرتبطة خادم عميل تسمح للمكونات بالتفاعل مع الخدمة وإرسال الطلبات وتلقي النتائج وحتى القيام بذلك عبر العمليات من خلال الاتصال البيني للعمليات (IPC). لا يتم تشغيل الخدمة المرتبطة إلا طالما يرتبط بها مكون تطبيق آخر. يمكن أن ترتبط المكونات المتعددة هذه الخدمة مرة واحدة، ولكن عند إلغاء ربطها جميعًا، يتم التخلص من الخدمة.

على الرغم من أنّ هذه الوثائق تناقش بشكل عام الخدمات التي بدأت ومرتبطة بشكل منفصل، أن تعمل خدمتك في كلا الاتجاهين، أي يمكن بدؤها (أي تشغيلها لأجل غير مسمى) كما تسمح لك الربط. تعتمد فقط على ما إذا كنت تنفّذ طريقتين لمعاودة الاتصال: onStartCommand() للسماح للمكوّنات ببدء تشغيلها، وonBind() للسماح بالربط.

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

الاختيار بين خدمة وسلسلة محادثات

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

إذا كان عليك تنفيذ عمل خارج سلسلة المحادثات الرئيسية، ولكن فقط أثناء تفاعل المستخدم باستخدام تطبيقك، يجب إنشاء سلسلة محادثات جديدة في سياق تطبيق آخر. المكون. على سبيل المثال، إذا أردت تشغيل بعض الموسيقى، ولكن أثناء تشغيل نشاطك فقط، يمكنك إنشاء سلسلة محادثات في onCreate() ابدأ تشغيله في onStart()، وإيقافه خلال onStop(). يمكنك أيضًا استخدام مجموعات سلاسل المحادثات وأدوات التنفيذ من حزمة java.util.concurrent. أو كوتينين كوتلين بدلاً من صف واحد (Thread). يمكنك الاطّلاع على مستند Threading على Android لمزيد من المعلومات حول جارٍ نقل التنفيذ إلى سلاسل المحادثات في الخلفية.

تذكر أنه إذا كنت تستخدم خدمة، ستظل تعمل في سلسلة التعليمات الرئيسية لتطبيقك عن طريق ولذلك لا يزال يتعين عليك إنشاء سلسلة محادثات جديدة داخل الخدمة إذا كانت تعمل بشكل مكثف أو وعمليات الحظر.

الأساسيات

لإنشاء خدمة، يجب إنشاء فئة فرعية من Service أو استخدام فئة. من فئاتها الفرعية الحالية. أثناء التنفيذ، يجب إلغاء بعض طرق معاودة الاتصال التي التعامل مع الجوانب الرئيسية لدورة حياة الخدمة وتوفير آلية تسمح للمكونات الالتزام بالخدمة، إذا كان ذلك مناسبًا. هذه هي أهم طرق معاودة الاتصال التي يجب عليك تجاوز:

onStartCommand()
يستدعي النظام هذه الطريقة من خلال استدعاء startService() عندما يطلب مكوّن آخر (مثل نشاط) بدء الخدمة. عند تنفيذ هذه الطريقة، يتم بدء الخدمة ويمكن تشغيلها في إلى أجل غير مسمى. وفي حال تنفيذ ذلك، تقع على عاتقك مسؤولية إيقاف الخدمة عندما يكتمل عمله عن طريق الاتصال بـ stopSelf() أو stopService(). إذا كنت ترغب فقط في توفير الارتباط، فلا بحاجة إلى تنفيذ هذه الطريقة.
onBind()
يستدعي النظام هذه الطريقة من خلال استدعاء bindService() عندما يريد مكوّن آخر الارتباط بالخدمة (مثل تنفيذ RPC). أثناء تنفيذ هذه الطريقة، يجب توفير واجهة يمكن للعملاء تستخدمها للتواصل مع الخدمة من خلال إرجاع IBinder. يجب عليك دائمًا وتنفيذ هذه الطريقة؛ ومع ذلك، إذا كنت لا تريد السماح بالربط، فيجب الرجوع خالية.
onCreate()
يستدعي النظام هذه الطريقة لتنفيذ إجراءات الإعداد لمرة واحدة عند تشغيل الخدمة تم إنشاؤه في البداية (قبل أن يستدعي إما onStartCommand() أو onBind()). إذا كانت الخدمة قيد التشغيل، لا يتم استخدام هذه الطريقة استدعيت.
onDestroy()
يستدعي النظام هذه الطريقة عندما لا يتم استخدام الخدمة ويتم تدميرها. يجب أن تنفذ الخدمة هذا الإجراء لإزالة أي موارد مثل سلاسل المحادثات المستمعين أو المستلمين. هذا هو الاتصال الأخير الذي تتلقاه الخدمة.

إذا بدأ أحد المكونات الخدمة من خلال استدعاء startService() (ما يؤدي إلى طلب onStartCommand())، فسيتم استخدام الخدمة يستمر في العمل إلى أن يتوقف تلقائيًا مع stopSelf() أو آخر يتوقف ذلك عن طريق طلب stopService().

في حال استدعاء أحد المكونات bindService() لإنشاء الخدمة ولا يتم استدعاء onStartCommand()، يتم تشغيل الخدمة طالما أن المكون مرتبط بها. بعد إلغاء ربط الخدمة من جميع عملائها، يقوم النظام بتدميرها.

لا يوقف نظام Android أي خدمة إلا عندما تكون الذاكرة منخفضة ويجب استرداد النظام. وموارد النشاط الذي يركز على المستخدم. في حال كانت الخدمة مرتبطة بنشاط يتضمّن مستخدِمًا التركيز، يقل احتمال القتل؛ إذا تم الإعلان عن تشغيل الخدمة في المقدّمة، نادرًا ما يتم إنهاؤها. إذا تم بدء تشغيل الخدمة واستمرارها لفترة طويلة، فسيخفض النظام موضعها. في قائمة المهام التي تعمل في الخلفية بمرور الوقت، وتصبح الخدمة عرضة بشكل كبير وهو ما يعني أنه في حال بدء الخدمة، يجب تصميمها للتعامل مع عمليات إعادة التشغيل بسلاسة. النظام. إذا أنهى النظام خدمتك، ستتم إعادة تشغيلها فور تراجُع الموارد. متاحة، ولكن هذا يعتمد أيضًا على القيمة التي تعرضها من خلال onStartCommand(). لمزيد من المعلومات حول الوقت الذي قد يدمر فيه النظام إحدى الخدمات، راجع العمليات وسلاسل المحادثات. جلسة المراجعة.

في الأقسام التالية، ستتعرف على كيفية إنشاء startService() و bindService() طريقة للخدمة وطريقة استخدامها من مكونات التطبيق الأخرى.

تعريف خدمة في البيان

يجب الإفصاح عن جميع الخدمات في ملف البيان، تمامًا كما تفعل مع الأنشطة والمكونات الأخرى.

للإعلان عن خدمتك، عليك إضافة العنصر <service>. وهي تابعة لمؤسسة <application> العنصر. يُرجى الاطّلاع على المثال أدناه:

<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>

الاطّلاع على العنصر <service> للحصول على مزيد من المعلومات حول الإعلان عن الخدمة في البيان.

هناك سمات أخرى يمكنك تضمينها في العنصر <service> من أجل تحديد الخصائص مثل الأذونات المطلوبة لبدء الخدمة والعملية في الذي يجب تشغيله للخدمة. android:name هي السمة المطلوبة الوحيدة؛ فهي تحدد اسم فئة الخدمة. بعد نشرت تطبيقك، فاترك هذا الاسم بدون تغيير لتجنب المخاطرة الرمز بسبب الاعتماد على نوايا صريحة لبدء الخدمة أو ربطها (يُرجى قراءة مشاركة المدونة، أشياء لا يمكن التغيير).

تنبيه: لضمان أمان تطبيقك، يُرجى دائمًا استخدام النية الصريحة عند بدء Service وعدم تعريف فلاتر الأهداف خدماتك. يعتبر استخدام نية ضمنية لبدء خدمة ما خطرًا أمنيًا لأنه لا يمكنك التأكد من الخدمة التي تستجيب للغرض، ولا يمكن للمستخدم معرفة أي خدمة تبدأ. بدءًا من Android 5.0 (مستوى واجهة برمجة التطبيقات 21)، يطرح النظام استثناءً في حالة طلب bindService() بغرض ضمني

يمكنك التأكُّد من توفُّر الخدمة لتطبيقك فقط عن طريق بما في ذلك android:exported وتعيينها إلى false. يؤدي هذا الإجراء إلى منع التطبيقات الأخرى بشكلٍ فعّال الخدمة، حتى عند استخدام نية صريحة.

ملاحظة: يمكن للمستخدمين الاطّلاع على الخدمات التي تعمل على أجهزتهم. إذا رأوا خدمة لا يتعرف عليها أو يثق بها، فيمكنه إيقاف الخدمة. ضِمن لتجنب توقف الخدمة عن طريق الخطأ من قبل المستخدمين، تحتاج لإضافة android:description إلى <service> العنصر في بيان التطبيق. في الوصف، قدِّم جملة قصيرة توضّح مزايا الخدمة ومزاياها. التي تقدمها.

إنشاء خدمة تم بدؤها

الخدمة التي تم بدؤها هي خدمة يبدأها مكوّن آخر باستدعاء startService()، وينتج عن ذلك اتصال بخدمة طريقة onStartCommand().

عند بدء تشغيل أي خدمة، تكون لها دورة حياة مستقلة عن المكون الذي بدأه. يمكن تشغيل الخدمة في الخلفية إلى أجل غير مسمى، حتى يتم إتلاف المكون الذي بدأ تشغيله. وبناءً على ذلك، يجب أن تتوقف الخدمة عن نفسها عندما تعمل مكتملة من خلال استدعاء stopSelf()، أو يمكن لمكوِّن آخر أن يمكنك إيقافه من خلال الاتصال بـ stopService().

يمكن لمكوِّن تطبيق مثل نشاط بدء الخدمة من خلال استدعاء startService() وتمرير Intent. تحدد الخدمة وتتضمن أي بيانات لكي تستخدمها الخدمة. تتلقى الخدمة هذا Intent بطريقة onStartCommand().

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

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

الفئة Service هي القاعدة الفئة لجميع الخدمات. عند تمديد هذا الصف، من المهم إنشاء سلسلة محادثات جديدة يمكن للخدمة إكمال جميع أعمالها؛ تستخدم الخدمة سلسلة التعليمات الرئيسية لتطبيقك عن طريق قد يؤدي ذلك إلى إبطاء أداء أي نشاط يشغّله تطبيقك.

يوفّر إطار عمل Android أيضًا IntentService. فئة فرعية من Service تستخدم عامل واحد لمعالجة جميع طلبات البدء، واحدة تلو الأخرى. استخدام هذا الصف ليس مقترَحة للتطبيقات الجديدة لأنّها لن تعمل بشكل جيد بدءًا من نظام التشغيل Android 8 Oreo، وذلك بسبب طرحنا مقالة حدود التنفيذ في الخلفية علاوةً على ذلك، تم إيقافه نهائيًا بدءًا من نظام التشغيل Android 11. يمكنك استخدام JobIntentService بديل "IntentService" المتوافق مع الإصدارات الأحدث من Android.

توضّح الأقسام التالية كيفية تنفيذ خدمتك المخصّصة، ولكن عليك ننصحك بشدة باستخدام WorkManager بدلاً من ذلك في معظم حالات الاستخدام. الرجوع إلى دليل المعالجة في الخلفية على Android لمعرفة ما إذا كان هناك حل يناسب احتياجاتك.

تمديد فئة الخدمة

يمكنك تمديد الصف الدراسي "Service". للتعامل مع كل نية واردة. في ما يلي الشكل الذي قد تظهر به عملية التنفيذ الأساسية:

Kotlin

class HelloService : Service() {

    private var serviceLooper: Looper? = null
    private var serviceHandler: ServiceHandler? = null

    // Handler that receives messages from the thread
    private inner class ServiceHandler(looper: Looper) : Handler(looper) {

        override fun handleMessage(msg: Message) {
            // Normally we would do some work here, like download a file.
            // For our sample, we just sleep for 5 seconds.
            try {
                Thread.sleep(5000)
            } catch (e: InterruptedException) {
                // Restore interrupt status.
                Thread.currentThread().interrupt()
            }

            // Stop the service using the startId, so that we don't stop
            // the service in the middle of handling another job
            stopSelf(msg.arg1)
        }
    }

    override fun onCreate() {
        // Start up the thread running the service.  Note that we create a
        // separate thread because the service normally runs in the process's
        // main thread, which we don't want to block.  We also make it
        // background priority so CPU-intensive work will not disrupt our UI.
        HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply {
            start()

            // Get the HandlerThread's Looper and use it for our Handler
            serviceLooper = looper
            serviceHandler = ServiceHandler(looper)
        }
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show()

        // For each start request, send a message to start a job and deliver the
        // start ID so we know which request we're stopping when we finish the job
        serviceHandler?.obtainMessage()?.also { msg ->
            msg.arg1 = startId
            serviceHandler?.sendMessage(msg)
        }

        // If we get killed, after returning from here, restart
        return START_STICKY
    }

    override fun onBind(intent: Intent): IBinder? {
        // We don't provide binding, so return null
        return null
    }

    override fun onDestroy() {
        Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show()
    }
}

Java

public class HelloService extends Service {
  private Looper serviceLooper;
  private ServiceHandler serviceHandler;

  // Handler that receives messages from the thread
  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          // Normally we would do some work here, like download a file.
          // For our sample, we just sleep for 5 seconds.
          try {
              Thread.sleep(5000);
          } catch (InterruptedException e) {
              // Restore interrupt status.
              Thread.currentThread().interrupt();
          }
          // Stop the service using the startId, so that we don't stop
          // the service in the middle of handling another job
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    // Start up the thread running the service. Note that we create a
    // separate thread because the service normally runs in the process's
    // main thread, which we don't want to block. We also make it
    // background priority so CPU-intensive work doesn't disrupt our UI.
    HandlerThread thread = new HandlerThread("ServiceStartArguments",
            Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();

    // Get the HandlerThread's Looper and use it for our Handler
    serviceLooper = thread.getLooper();
    serviceHandler = new ServiceHandler(serviceLooper);
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // For each start request, send a message to start a job and deliver the
      // start ID so we know which request we're stopping when we finish the job
      Message msg = serviceHandler.obtainMessage();
      msg.arg1 = startId;
      serviceHandler.sendMessage(msg);

      // If we get killed, after returning from here, restart
      return START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
      // We don't provide binding, so return null
      return null;
  }

  @Override
  public void onDestroy() {
    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
  }
}

يعالج الرمز النموذجي جميع المكالمات الواردة في onStartCommand(). وينشر العمل على Handler يتم تشغيله على سلسلة محادثات في الخلفية. وهي تعمل تمامًا مثل IntentService وتعالج جميع الطلبات بشكل تسلسلي، واحدًا تلو الآخر. يمكنك تغيير الرمز لتشغيل مجموعة سلاسل المحادثات، على سبيل المثال، إذا أردت تنفيذ طلبات متعددة في الوقت نفسه.

لاحظ أن الطريقة onStartCommand() يجب أن تعرض عدد صحيح. العدد الصحيح هو قيمة تصف كيفية استمرار النظام في توفير الخدمة ، وهو الحدث الذي يقتله النظام. القيمة المعروضة من onStartCommand() يجب أن يكون واحدًا مما يلي الثوابت:

START_NOT_STICKY
إذا أنهى النظام الخدمة بعد إرجاع onStartCommand()، يجب عدم إعادة إنشاء الخدمة ما لم تكن في انتظار المراجعة. نية تسليمه. هذا هو الخيار الأكثر أمانًا لتجنُّب تشغيل خدمتك عند عدم الضرورة. وعندما يتمكن تطبيقك من إعادة تشغيل أي مهام غير مكتملة.
START_STICKY
إذا أنهى النظام الخدمة بعد عودة onStartCommand()، يمكنك إعادة إنشاء الخدمة وطلب onStartCommand()، ولكن عدم إعادة إرسال الغرض الأخير. وبدلاً من ذلك، يطلب النظام الاتصال بـ onStartCommand() باستخدام لا نية بالشراء، ما لم تكن هناك أغراض في انتظار المراجعة لبدء الخدمة. في هذه الحالة، يتم تحقيق هذه الأهداف. وهذا المحتوى مناسب لمشغّلات الوسائط (أو الخدمات المشابهة) التي لا وتنفذ الأوامر ولكنها تعمل إلى أجل غير مسمى وتنتظر المهمة.
START_REDELIVER_INTENT
إذا أنهى النظام الخدمة بعد عودة onStartCommand()، يمكنك إعادة إنشاء الخدمة والاتصال بـ onStartCommand() مع الغرض الأخير الذي تم تسليمه إلى خدمة ما. ويتم تسليم أي نوايا في انتظار المراجعة تباعًا. هذه الحالة مناسبة للخدمات أداء وظيفة يجب استئنافها على الفور، مثل تنزيل ملف.

لمزيد من التفاصيل حول القيم المعروضة هذه، يُرجى الاطّلاع على المرجع المرتبط. التوثيق لكل ثابت.

بدء خدمة

يمكنك بدء خدمة من نشاط أو من مكون تطبيق آخر من خلال اجتياز Intent إلى startService() أو startForegroundService(). تشير رسالة الأشكال البيانية يستدعي نظام Android طريقة onStartCommand() للخدمة ويمررها إلى Intent، الذي يحدد الخدمة التي سيتم تشغيلها.

ملاحظة: إذا كان تطبيقك يستهدف المستوى 26 من واجهة برمجة التطبيقات أو مستوى أعلى، سيجري النظام يفرض قيودًا على استخدام خدمات الخلفية أو إنشائها ما لم يكن نفسها في المقدمة. إذا احتاج أحد التطبيقات إلى إنشاء خدمة تعمل في المقدّمة، من المفترض أن يتصل التطبيق بـ startForegroundService(). تنشئ هذه الطريقة خدمة تعمل في الخلفية، ولكن النظام على أن الخدمة ستروّج لنفسها لدى المقدّمة. بعد إنشاء الخدمة، يجب أن تستدعي الخدمة طريقة واحدة (startForeground()) خلال خمس ثوانٍ.

على سبيل المثال، يمكن أن يبدأ نشاط الخدمة كمثال في القسم السابق (HelloService) باستخدام intent صريح مع startService()، كما هو موضّح هنا:

Kotlin

startService(Intent(this, HelloService::class.java))

Java

startService(new Intent(this, HelloService.class));

تُرجع الطريقة startService() فورًا يستدعي نظام Android طريقة onStartCommand() للخدمة. إذا لم تكن الخدمة قيد التشغيل بالفعل، يتصل النظام أولاً بـ onCreate()، ثم يتصل onStartCommand()

إذا لم تكن الخدمة توفّر أيضًا ملزِمًا، يكون الغرض الذي يتم إرساله باستخدام startService() هو وسيلة التواصل الوحيدة بين مكون التطبيق والخدمة. ومع ذلك، إذا أردت من الخدمة إرسال إحدى النتائج مرة أخرى، يمكن للعميل الذي يبدأ الخدمة إنشاء PendingIntent للبث. (مع getBroadcast()) وتسليمها إلى الخدمة في Intent الذي يبدأ تشغيل الخدمة. ويمكن للخدمة بعد ذلك استخدام البث لتقديم نتيجة.

تؤدي الطلبات المتعددة لبدء الخدمة إلى إنشاء عدة طلبات مقابلة مطابِقة لخدمة onStartCommand() ومع ذلك، لا يوجد سوى طلب واحد لإيقاف يجب أن تكون الخدمة (مع stopSelf() أو stopService()) مطلوبة لإيقافها.

إيقاف خدمة

ويجب أن تدير الخدمة التي بدأتها دورة حياتها الخاصة. أي أن النظام لا يتوقف أو إتلاف الخدمة ما لم يكن يلزم استرداد ذاكرة النظام والخدمة يستمر التنفيذ بعد عودة onStartCommand(). تشير رسالة الأشكال البيانية يجب أن تتوقف الخدمة تلقائيًا عن طريق الاتصال بـ stopSelf() أو إيقافه عن طريق استدعاء stopService().

بعد طلب إيقاف الخدمة مع stopSelf() أو stopService()، يتسبّب النظام في تعطّل الخدمة ممكن.

إذا كانت خدمتك تعالج طلبات متعدّدة إلى onStartCommand() بشكل متزامن، يجب عدم إيقاف عند الانتهاء من معالجة طلب بدء، فربما تكون قد تلقيت رسالة طلب بدء (الإيقاف في نهاية الطلب الأول يؤدي إلى إنهاء الطلب الثاني). لتجنُّب هذه المشكلة، يمكنك استخدام stopSelf(int) للتأكد من أن طلبك إيقاف الخدمة دائمًا إلى أحدث طلب بدء. وهذا يعني أنّك عندما تطلب stopSelf(int)، فإنّك تمرِّر رقم تعريف طلب البدء (startId). التي تم تسليمها إلى onStartCommand()) الذي أرسل إليه طلب التوقف يتجاوب. بعد ذلك، إذا تلقّت الخدمة طلب بدء جديدًا قبل أن تتمكّن من الاتصال بـ stopSelf(int)، لن يتطابق رقم التعريف ولا تتوقف الخدمة.

تنبيه: لتجنُّب إهدار موارد النظام واستهلاك طاقة البطارية، فتأكد من أن تطبيقك يوقف خدماته عند اكتمال عمله. إذا لزم الأمر، يمكن لمكونات أخرى إيقاف الخدمة من خلال طلب stopService(). حتى في حال تفعيل الربط للخدمة، يجب عليك إيقاف الخدمة بنفسك دائمًا في حال تلقّي مكالمة هاتفية إلى onStartCommand().

لمزيد من المعلومات عن دورة حياة الخدمة، يُرجى الاطّلاع على القسم أدناه حول إدارة دورة حياة الخدمة.

إنشاء خدمة مرتبطة

الخدمة المرتبطة هي خدمة تسمح لمكونات التطبيق بالارتباط بها من خلال طلب bindService() لإنشاء اتصال طويل الأمد. بشكل عام، لا يسمح للمكوّنات بتشغيلها من خلال طلب startService().

يمكنك إنشاء خدمة مرتبطة عندما تريد التفاعل مع الخدمة من الأنشطة. والمكونات الأخرى في تطبيقك أو لكشف بعض وظائف التطبيق التطبيقات الأخرى من خلال الاتصال البيني للعمليات (IPC).

لإنشاء خدمة مرتبطة، يجب تنفيذ طريقة معاودة الاتصال onBind() لعرض IBinder التي وتحدد واجهة الاتصال بالخدمة. يمكن لمكونات التطبيق الأخرى بعد ذلك استدعاء bindService() لاسترداد الواجهة بدء استدعاء الطرق على الخدمة. وتتواجد الخدمة فقط لخدمة مكون التطبيق الذي تكون الخدمة ملزمة بها، لذلك في حالة عدم وجود أي مكونات مرتبطة بالخدمة، يقوم النظام بتدميرها. لا تحتاج إلى إيقاف خدمة مرتبطة بالطريقة نفسها التي يجب اتّباعها عندما تكون الخدمة متاحة بدأت حتى onStartCommand().

لإنشاء خدمة مرتبطة، يجب تحديد الواجهة التي تحدد الكيفية التي يمكن للعميل من خلالها التواصل مع الخدمة. تظهر هذه الواجهة بين الخدمة ويجب أن يكون العميل من تنفيذ IBinder وأن يكون هو العنصر المطلوب في خدمتك من طريقة معاودة الاتصال onBind(). بعد أن يتلقّى العميل إذن IBinder، يمكن أن يبدأ. التفاعل مع الخدمة من خلال تلك الواجهة.

يمكن لعدة عملاء الارتباط بالخدمة في وقت واحد. عندما ينتهي العميل من التفاعل مع الخدمة، تطلب unbindService() لإلغاء الربط. عندما لا يكون هناك عملاء ملتزمون بالخدمة، يتسبّب النظام في تدمير الخدمة.

هناك عدة طرق لتنفيذ خدمة مرتبطة، ويصبح عملية التنفيذ أكثر تعقيدًا من الخدمة التي بدأت. ولهذه الأسباب، تظهر مناقشة الخدمة المرتبطة في مستند منفصل حول الخدمات المرتبطة

إرسال الإشعارات إلى المستخدم

عندما تكون الخدمة قيد التشغيل، يمكنها إعلام المستخدم بالأحداث من خلال إشعارات شريط الإعلام أو إشعارات شريط الحالة.

إشعار شريط الإعلام المنبثق هو رسالة تظهر على سطح النافذة الحالية لـ قبل أن يختفي. يقدم إشعار شريط الحالة رمزًا في شريط الحالة التي يمكن للمستخدم اختيارها لاتخاذ إجراء (مثل بدء نشاط).

عادةً ما يكون إشعار شريط الحالة هو أفضل أسلوب للاستخدام عند العمل في الخلفية مثل اكتمل تنزيل الملف، ويمكن للمستخدم الآن اتخاذ إجراء بشأنه. عندما يجرّب يحدد الإشعار من العرض الموسَّع، يمكن أن يبدأ الإشعار نشاطًا (مثل عرض الملف الذي تم تنزيله).

إدارة دورة حياة خدمة

وتكون دورة حياة الخدمة أبسط بكثير من دورة النشاط. ومع ذلك، من الأفضل أن تولي اهتمامًا وثيقًا لكيفية إنشاء خدمتك وتدميرها بسبب يمكن أن تعمل الخدمة في الخلفية دون أن يكون المستخدم على دراية.

يمكن أن تتبع دورة حياة الخدمة - بدءًا من تاريخ إنشائها وحتى إتلافها - أحد المسارين التاليين:

  • خدمة بدأت

    يتم إنشاء الخدمة عندما يستدعي مكوّن آخر الطلب startService(). ثم تعمل الخدمة إلى أجل غير مسمى ويجب التوقف تلقائيًا عن طريق الاتصال برقم stopSelf(). يمكن لمكون آخر أيضًا إيقاف عبر الاتصال بالرقم stopService(). عند إيقاف الخدمة، يتسبّب النظام في تدميرها.

  • خدمة مرتبطة

    يتم إنشاء الخدمة عندما يستدعي مكوّن آخر (أحد البرامج) bindService(). بعد ذلك يتواصل العميل مع الخدمة من خلال واجهة IBinder. يمكن للعميل إغلاق الاتصال عن طريق الاتصال unbindService() يمكن لعدة عملاء الالتزام نفس الخدمة وعندما يتم إلغاء ربطها جميعًا، يتلف النظام الخدمة. الخدمة لا يحتاج إلى التوقف عن نفسه.

هذان المساران ليسا منفصلين تمامًا. يمكنك الربط بخدمة بدأ بـ startService(). على سبيل المثال، يمكنك يمكنك تشغيل خدمة موسيقى في الخلفية من خلال الاتصال بـ startService() باستخدام Intent الذي يحدّد الموسيقى المراد تشغيلها. لاحقًا، ربما عندما يريد المستخدم ممارسة بعض التحكم في اللاعب أو الحصول على معلومات حول الأغنية الحالية، يمكن ربط نشاط بالخدمة من خلال الاتصال بالرقم bindService(). وفي مثل هذه الحالات، لا يوقف stopService() أو stopSelf() الخدمة فعليًا إلى أن يلغي جميع العملاء الربط.

تنفيذ استدعاءات مراحل النشاط

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

Kotlin

class ExampleService : Service() {
    private var startMode: Int = 0             // indicates how to behave if the service is killed
    private var binder: IBinder? = null        // interface for clients that bind
    private var allowRebind: Boolean = false   // indicates whether onRebind should be used

    override fun onCreate() {
        // The service is being created
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // The service is starting, due to a call to startService()
        return startMode
    }

    override fun onBind(intent: Intent): IBinder? {
        // A client is binding to the service with bindService()
        return binder
    }

    override fun onUnbind(intent: Intent): Boolean {
        // All clients have unbound with unbindService()
        return allowRebind
    }

    override fun onRebind(intent: Intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }

    override fun onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

Java

public class ExampleService extends Service {
    int startMode;       // indicates how to behave if the service is killed
    IBinder binder;      // interface for clients that bind
    boolean allowRebind; // indicates whether onRebind should be used

    @Override
    public void onCreate() {
        // The service is being created
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The service is starting, due to a call to startService()
        return startMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return binder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return allowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    @Override
    public void onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

ملاحظة: على عكس طرق معاودة الاتصال في مراحل النشاط، لا يمكنك غير مطلوب لاستدعاء التنفيذ المتميز لطرق معاودة الاتصال هذه.

الشكل 2. دورة حياة الخدمة. الرسم التخطيطي الموجود على اليسار يعرِض هذا العمود دورة النشاط عندما يتم إنشاء الخدمة باستخدام startService() ويعرِض الرسم البياني على اليسار دورة النشاط عند إنشاء الخدمة مع bindService().

يوضِّح الشكل 2 الطرق النموذجية لمعاودة الاتصال لإحدى الخدمات. رغم أن الشكل يفصل الخدمات التي أنشأها startService() من تلك من إنشاء bindService()، يُرجى إبقاء مع الأخذ في الاعتبار أن أي خدمة، بغض النظر عن كيفية بدئها، قد تسمح للعملاء بالالتزام بها. خدمة بدأت في البداية باستخدام "onStartCommand()" (من قِبل عميل يتصل بـ startService()) لا يزال بإمكانك تلقّي مكالمة على الرقم onBind() (عندما يتصل أحد العملاء bindService()).

من خلال تنفيذ هاتين الطريقتين، يمكنك مراقبة هاتين الحلقتين المتداخلتين لخدمة دورة الحياة:

  • تتراوح عمر الخدمة بالكامل بين وقت استدعاء onCreate() ووقت إرجاع onDestroy(). مثلما هو الحال مع النشاط، تقوم الخدمة بالإعداد الأولي لها onCreate() وإصدار جميع الموارد المتبقية في onDestroy() على سبيل المثال، يمكن لخدمة تشغيل الموسيقى إنشاء سلسلة محادثات يتم فيها تشغيل الموسيقى في "onCreate()"، ثم إيقاف سلسلة المحادثات في "onDestroy()".

    ملاحظة: onCreate() وonDestroy() طريقة يتم استدعاءها لجميع الخدمات، سواء تم إنشاؤها بواسطة startService() أو bindService().

  • تبدأ مدة النشاط للخدمة بمكالمة إلى onStartCommand() أو onBind(). يتم تسليم كل طريقة Intent التي تم ضبطها إلى startService() أو bindService().

    في حال بدء الخدمة، ينتهي العمر النشط في الوقت نفسه الذي تنتهي فيه الفترة منذ الإنشاء ينتهي (تظل الخدمة نشطة حتى بعد إرجاع onStartCommand()). في حال كانت الخدمة مرتبطة، ينتهي العمر النشط عند رجوع onUnbind().

ملاحظة: على الرغم من أن الخدمة التي تم بدؤها يتم إيقافها عن طريق استدعاء إما stopSelf() أو stopService()، فلا يوجد اتصال مناسب (لا يوجد معاودة اتصال onStop()). ما لم تكن الخدمة مرتبطة بعميل، يتسبّب النظام في إتلاف الجهاز عند إيقاف الخدمة، ويكون onDestroy() هو نوع معاودة الاتصال الوحيدة التي يتم تلقّيها.

لمزيد من المعلومات حول إنشاء خدمة توفّر الربط، راجِع مستند الخدمات المرتبطة. والذي يتضمّن مزيدًا من المعلومات حول onRebind() في القسم المتعلق بإدارة دورة حياة خدمة مرتبطة