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

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

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

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

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

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

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

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

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

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

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

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

في الإصدار 13 من نظام التشغيل Android (المستوى 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"

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

بالنسبة إلى التطبيقات التي تستهدف الإصدار 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 =
            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
  • الأجهزة التي تكون في الوضع التجريبي

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

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

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

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

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

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

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

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

الاستثناءات من القيود المفروضة على بدء الخدمات عندما تكون التطبيقات قيد التشغيل في الخلفية

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

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

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

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

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

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