الخدمات التي تعمل في المقدّمة

تؤدي الخدمات التي تعمل في المقدّمة عمليات يمكن للمستخدم ملاحظتها.

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

تشمل أمثلة التطبيقات التي تستخدم الخدمات التي تعمل في المقدّمة ما يلي:

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

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

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

يمكن للمستخدم إغلاق الإشعار تلقائيًا

بدءًا من نظام التشغيل Android 13 (المستوى 33)، يمكن للمستخدمين إغلاق الإشعار المرتبط بخدمة تعمل في المقدّمة تلقائيًا. للقيام بذلك، يقوم المستخدمون بإيماءة تمرير سريع على الإشعار. في العادة، لا يتم إغلاق الإشعار ما لم يتم إيقاف الخدمة التي تعمل في المقدّمة أو إزالتها من المقدّمة.

إذا أردت أن يكون المستخدم لا يستطيع إغلاق الإشعار، يمكنك ضبط true على طريقة setOngoing() عند إنشاء الإشعار باستخدام Notification.Builder.

الخدمات التي تعرض إشعارًا على الفور

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

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

يُرجى تعريف الخدمات التي تعمل في المقدّمة ضمن البيان.

في ملف بيان تطبيقك، يُرجى تحديد كل خدمة من الخدمات التي تعمل في المقدّمة في تطبيقك باستخدام عنصر <service>. يمكنك استخدام سمة android:foregroundServiceType لكل خدمة لتحديد نوع العمل الذي تؤديه الخدمة.

على سبيل المثال، إذا أنشأ تطبيقك خدمة تعمل في المقدّمة لتشغيل الموسيقى، يمكنك الإفصاح عن الخدمة على النحو التالي:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
  <application ...>

    <service
        android:name=".MyMediaPlaybackService"
        android:foregroundServiceType="mediaPlayback"
        android:exported="false">
    </service>
  </application>
</manifest>

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

android:foregroundServiceType="camera|microphone"

طلب أذونات الخدمات التي تعمل في المقدّمة

على التطبيقات التي تستهدف Android 9 (المستوى 28 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث وتستخدم خدمات تعمل في المقدّمة طلب عرض الرمز FOREGROUND_SERVICE في بيان التطبيق كما هو موضّح في مقتطف الرمز التالي. هذا إذن عادي، وبالتالي يمنحه النظام تلقائيًا للتطبيق الذي يطلب الإذن.

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

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA"/>

    <application ...>
        ...
    </application>
</manifest>

المتطلّبات الأساسية للخدمة التي تعمل في المقدّمة

بدءًا من Android 14 (المستوى 34)، عند إطلاق خدمة تعمل في المقدّمة، يتحقّق النظام من متطلبات أساسية محدّدة استنادًا إلى نوع الخدمة. على سبيل المثال، إذا حاولت تشغيل خدمة تعمل في المقدّمة من النوع location، يتحقّق النظام من أنّ تطبيقك لديه الإذن ACCESS_COARSE_LOCATION أو ACCESS_FINE_LOCATION. وإذا لم يحدث ذلك، يعرض النظام SecurityException.

لهذا السبب، عليك التأكّد من استيفاء المتطلبات الأساسية المطلوبة قبل بدء استخدام خدمة تعمل في المقدّمة. تسرد مستندات نوع الخدمة التي تعمل في المقدّمة المتطلبات الأساسية المطلوبة لكل نوع من أنواع الخدمات التي تعمل في المقدّمة.

بدء خدمة تعمل في المقدّمة

قبل أن تطلب من النظام تشغيل خدمة كخدمة تعمل في المقدّمة، ابدأ تشغيل الخدمة نفسها:

Kotlin

val intent = Intent(...) // Build the intent for the service
context.startForegroundService(intent)

Java

Context context = getApplicationContext();
Intent intent = new Intent(...); // Build the intent for the service
context.startForegroundService(intent);

داخل الخدمة، والتي عادةً ما تكون في onStartCommand()، يمكنك طلب تشغيل خدمتك في المقدّمة. لإجراء ذلك، يمكنك الاتصال برقم ServiceCompat.startForeground() (متوفّر في الإصدار 1.12 من androidx-core والإصدارات الأحدث). تستخدم هذه الطريقة المعلمات التالية:

قد تكون هذه الأنواع مجموعة فرعية من الأنواع التي تم تعريفها في البيان، بناءً على حالة الاستخدام المحددة. بعد ذلك، إذا كنت بحاجة إلى إضافة المزيد من أنواع الخدمات، يمكنك الاتصال بـ startForeground() مرة أخرى.

على سبيل المثال، لنفترض أنّ أحد تطبيقات اللياقة البدنية يشغّل خدمة تتبُّع الجري والتي تحتاج دائمًا إلى معلومات حول location، ولكنها قد تحتاج إلى تشغيل الوسائط أو لا تحتاج إليها. يجب الإفصاح عن السمتَين location وmediaPlayback في البيان. إذا بدأ المستخدم عملية الجري وأردت فقط تتبُّع موقعه الجغرافي، يجب أن يتصل تطبيقك بـ "startForeground()" ويجتاز إذن "ACCESS_FINE_LOCATION" فقط. بعد ذلك، إذا أراد المستخدم بدء تشغيل الصوت، عليك استدعاء startForeground() مرة أخرى وتمرير مجموعة جميع أنواع الخدمات التي تعمل في المقدّمة (في هذه الحالة، ACCESS_FINE_LOCATION|FOREGROUND_SERVICE_MEDIA_PLAYBACK).

إليك مثال عن تشغيل خدمة الكاميرا التي تعمل في المقدّمة:

Kotlin

class MyCameraService: Service() {

  private fun startForeground() {
    // Before starting the service as foreground check that the app has the
    // appropriate runtime permissions. In this case, verify that the user has
    // granted the CAMERA permission.
    val cameraPermission =
            PermissionChecker.checkSelfPermission(this, Manifest.permission.CAMERA)
    if (cameraPermission != PermissionChecker.PERMISSION_GRANTED) {
        // Without camera permissions the service cannot run in the foreground
        // Consider informing user or updating your app UI if visible.
        stopSelf()
        return
    }

    try {
        val notification = NotificationCompat.Builder(this, "CHANNEL_ID")
            // Create the notification to display while the service is running
            .build()
        ServiceCompat.startForeground(
            /* service = */ this,
            /* id = */ 100, // Cannot be 0
            /* notification = */ notification,
            /* foregroundServiceType = */
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA
            } else {
                0
            },
        )
    } catch (e: Exception) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
                && e is ForegroundServiceStartNotAllowedException) {
            // App not in a valid state to start foreground service
            // (e.g. started from bg)
        }
        // ...
    }
  }
}

Java

public class MyCameraService extends Service {

    private void startForeground() {
        // Before starting the service as foreground check that the app has the
        // appropriate runtime permissions. In this case, verify that the user
        // has granted the CAMERA permission.
        int cameraPermission =
            ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
        if (cameraPermission == PackageManager.PERMISSION_DENIED) {
            // Without camera permissions the service cannot run in the
            // foreground. Consider informing user or updating your app UI if
            // visible.
            stopSelf();
            return;
        }

        try {
            Notification notification =
                new NotificationCompat.Builder(this, "CHANNEL_ID")
                    // Create the notification to display while the service
                    // is running
                    .build();
            int type = 0;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                type = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
            }
            ServiceCompat.startForeground(
                    /* service = */ this,
                    /* id = */ 100, // Cannot be 0
                    /* notification = */ notification,
                    /* foregroundServiceType = */ type
            );
        } catch (Exception e) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
                    e instanceof ForegroundServiceStartNotAllowedException
            ) {
                // App not in a valid state to start foreground service
                // (e.g started from bg)
            }
            // ...
        }
    }

    //...
}

إزالة خدمة من المقدّمة

لإزالة الخدمة من المقدّمة، يمكنك طلب stopForeground(). تستخدم هذه الطريقة قيمة منطقية، تشير إلى ما إذا كان يجب إزالة إشعار شريط الحالة أيضًا. يُرجى ملاحظة أنّ الخدمة لا تزال تعمل.

إذا أوقفت الخدمة أثناء تشغيلها في المقدّمة، ستتم إزالة إشعاراتها.

معالجة الإيقاف الذي يبدأه المستخدم للتطبيقات التي تشغّل الخدمات التي تعمل في المقدّمة

ويظهر زر أسفل درج الإشعارات يشير إلى عدد التطبيقات التي تعمل حاليًا في الخلفية. عند الضغط على
    هذا الزر، يظهر مربع حوار يسرد أسماء التطبيقات المختلفة. يظهر
    زر &quot;إيقاف&quot; على يسار كل تطبيق
الشكل 1. سير عمل "إدارة المهام" على الأجهزة التي تعمل بالإصدار 13 من نظام التشغيل Android أو الإصدارات الأحدث.

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

تحمل هذه القائمة التصنيف التطبيقات النشطة. وبجانب كل تطبيق، يظهر زر إيقاف. ويوضّح الشكل 1 سير عمل "إدارة المهام" على جهاز يعمل بنظام التشغيل Android 13.

عندما يضغط المستخدم على الزر إيقاف بجانب تطبيقك في "إدارة المهام"، يتم تنفيذ الإجراءات التالية:

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

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

adb shell cmd activity stop-app PACKAGE_NAME

الإعفاءات

يوفر النظام مستويات متعددة من الإعفاءات لأنواع معيّنة من التطبيقات، وتصفها الأقسام التالية.

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

الإعفاءات من الظهور في "إدارة المهام" على الإطلاق

يمكن للتطبيقات التالية تشغيل خدمة تعمل في المقدّمة ولا تظهر في "إدارة المهام" على الإطلاق:

  • التطبيقات على مستوى النظام
  • تطبيقات الأمان، أي التطبيقات التي تمتلك دور ROLE_EMERGENCY
  • الأجهزة التي تكون في الوضع التجريبي

إمكانية إيقاف عرض الإعفاءات من قِبل المستخدمين

عندما تشغِّل الأنواع التالية من التطبيقات خدمة تعمل في المقدّمة، فإنّها تظهر في "إدارة المهام"، ولكن لا يظهر الزر إيقاف بجانب اسم التطبيق لينقر عليه المستخدم:

استخدام واجهات برمجة التطبيقات المخصصة لغرض محدّد بدلاً من الخدمات التي تعمل في المقدّمة

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

تسرد مستندات أنواع الخدمات التي تعمل في المقدّمة بدائل جيدة لاستخدامها بدلاً من الخدمات التي تعمل في المقدّمة.

قيود بدء الخدمة التي تعمل في المقدّمة من الخلفية

لا يمكن للتطبيقات التي تستهدف الإصدار 12 من نظام التشغيل Android أو الإصدارات الأحدث تشغيل الخدمات التي تعمل في المقدّمة أثناء تشغيل التطبيق في الخلفية، باستثناء بعض الحالات الخاصة. إذا حاول أحد التطبيقات بدء خدمة تعمل في المقدّمة أثناء تشغيله في الخلفية، ولم تلبّي الخدمة التي تعمل في المقدّمة إحدى الحالات الاستثنائية، يعرض النظام الرمز ForegroundServiceStartNotAllowedException.

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

الإعفاءات من قيود البدء في الخلفية

في الحالات التالية، يمكن لتطبيقك بدء الخدمات التي تعمل في المقدّمة حتى أثناء تشغيل التطبيق في الخلفية:

القيود المفروضة على بدء الخدمات التي تعمل في المقدّمة التي تحتاج إلى أذونات أثناء الاستخدام

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

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

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

وبالمثل، إذا كان تطبيقك يعمل في الخلفية وينشئ خدمة صحية تحتاج إلى إذن BODY_SENSORS_BACKGROUND، يعني ذلك أنّ التطبيق لا يملك حاليًا هذا الإذن، ويطرح النظام استثناءً. (لا ينطبق ذلك إذا كانت تقدّم خدمة صحية تحتاج إلى أذونات مختلفة، مثل ACTIVITY_RECOGNITION). إنّ الاتصال PermissionChecker.checkSelfPermission() لا يمنع حدوث هذه المشكلة. إذا كان لدى تطبيقك إذن أثناء الاستخدام، وطلب من "checkSelfPermission()" معرفة ما إذا كان لديه هذا الإذن، ستعرض الطريقة الرمز PERMISSION_GRANTED حتى إذا كان التطبيق يعمل في الخلفية. عندما تعرض الطريقة PERMISSION_GRANTED، يعني ذلك أنّ "تطبيقك لديه هذا الإذن عندما يكون التطبيق قيد الاستخدام".

ولهذا السبب، إذا كانت الخدمة التي تعمل في المقدّمة تحتاج إلى إذن أثناء الاستخدام، عليك طلب Context.startForegroundService() أو Context.bindService() عندما يكون هناك نشاط مرئي في التطبيق، ما لم تندرج الخدمة ضمن الاستثناءات المحدّدة.

الإعفاءات من القيود المفروضة على الأذونات أثناء الاستخدام

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

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

تحتوي القائمة التالية على هذه الحالات:

  • يبدأ مكوّن النظام الخدمة.
  • تبدأ الخدمة بالتفاعل مع أدوات التطبيقات.
  • تبدأ الخدمة بالتفاعل مع الإشعار.
  • تبدأ الخدمة باسم PendingIntent يتم إرساله من تطبيق مختلف ومرئي.
  • تبدأ الخدمة من خلال تطبيق يمثّل وحدة التحكّم بسياسة الجهاز ويتم تشغيله في وضع مالك الجهاز.
  • تبدأ الخدمة بتطبيق يوفّر VoiceInteractionService.
  • يبدأ تشغيل الخدمة من خلال تطبيق لديه إذن امتياز START_ACTIVITIES_FROM_BACKGROUND.
تحديد الخدمات المتأثرة في تطبيقك

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

Foreground service started from background can not have \
location/camera/microphone access: service SERVICE_NAME