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

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

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

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

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

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

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

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

بدءًا من نظام التشغيل 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" ...>

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

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

android:foregroundServiceType="camera|microphone"

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

إنّ التطبيقات التي تستهدف الإصدار 9 من نظام التشغيل Android (المستوى 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() (متوفّر في الإصدار androidx-core 1.12 والإصدارات الأحدث). تأخذ هذه الطريقة المعلَمات التالية:

قد تكون هذه الأنواع مجموعة فرعية من الأنواع التي تم تعريفها في البيان، بناءً على حالة الاستخدام المحددة. بعد ذلك، إذا كنت بحاجة إلى إضافة المزيد من أنواع الخدمات، يمكنك الاتصال بـ "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 =
            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 {
        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(). تستخدم هذه الطريقة قيمة منطقية تشير إلى ما إذا كانت ستتم إزالة إشعار شريط الحالة أيضًا. يُرجى العِلم أنّ الخدمة لا تزال تعمل.

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

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

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

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

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

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

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

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

adb shell cmd activity stop-app PACKAGE_NAME

الإعفاءات

ويقدِّم النظام عدة مستويات من الاستثناءات لأنواع معيَّنة من التطبيقات، وتوضيحها في الأقسام التالية.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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