שירותים שפועלים בחזית

שירותים שפועלים בחזית מבצעים פעולות שהמשתמשים יכולים להבחין בהן.

בשירותים שפועלים בחזית מוצג שורת סטטוס , כדי ליידע את המשתמשים מבצעת משימה בחזית ומשתמשת במשאבי מערכת.

דוגמאות לאפליקציות שמשתמשות בשירותים שפועלים בחזית:

  • אפליקציה של נגן מוזיקה שמשמיעה מוזיקה בשירות שפועל בחזית. ההתראה יכול להראות את השיר הנוכחי שמתנגן.
  • אפליקציית כושר שמתעדת את הריצה של משתמש בשירות שפועל בחזית, אחרי קבלת הרשאה מהמשתמש. יכול להיות שבהתראה יוצג המרחק שהמשתמש עבר בו במהלך סשן הכושר הנוכחי.

יש להשתמש בשירות שפועל בחזית רק כשהאפליקציה צריכה לבצע משימה בולט לעין, גם אם אין למשתמשים אינטראקציה ישירה איתם את האפליקציה. אם הפעולה חשובה מספיק שכדי להשתמש בה הודעה בעדיפות מינימלית, יצירת רקע משימה זו.

במסמך הזה מתוארת ההרשאה הנדרשת כדי להשתמש בשירותים שפועלים בחזית, ואיך להפעיל שירות שפועל בחזית ולהסיר אותו מהרקע. כמו כן מוסבר איך לשייך תרחישים לדוגמה מסוימים לסוגי שירותים שפועלים בחזית, הגבלות הגישה שנכנסות לתוקף כשמפעילים שירות שפועל בחזית מאפליקציה שפועלת ברקע.

המשתמש יכול לסגור התראה כברירת מחדל

החל מ-Android 13 (רמת API 33), משתמשים יכולים לסגור את ההתראה שמשויכים לשירות שפועל בחזית כברירת מחדל. כדי לעשות זאת, המשתמשים מבצעים החלקה על ההתראה, בדרך כלל, ההתראה לא נסגר, אלא אם השירות שפועל בחזית מופסק או הוסר מהחזית.

אם רוצים שהמשתמש לא יוכל לסגור את ההתראה, מעבירים true בsetOngoing() כאשר יוצרים את ההתראה באמצעות Notification.Builder.

שירותים שמציגים התראה באופן מיידי

אם לשירות שפועל בחזית יש לפחות אחד מהמאפיינים הבאים, המערכת מציגה את ההתראה המשויכת מיד לאחר הפעלת השירות, גם במכשירים עם Android 12 ואילך:

ב-Android מגרסה 13 (רמת API 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 (רמת API 28) ואילך שמשתמשות בשירותים שפועלים בחזית צריכים לבקש FOREGROUND_SERVICE בקובץ המניפסט של האפליקציה, כפי שמוצג בקטע הקוד הבא. זהו מצב רגיל , כך שהמערכת מעניק אותו באופן אוטומטי לאפליקציה ששלחה את הבקשה.

בנוסף, אם האפליקציה מטרגטת רמת API 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 (רמת API 34), כשמפעילים שירות שפועל בחזית המערכת בודקת דרישות מוקדמות ספציפיות בהתאם לסוג השירות. לדוגמה, אם תנסו להפעיל שירות שפועל בחזית מסוג location, המערכת תבדוק כדי לוודא שבאפליקציה כבר יש ACCESS_COARSE_LOCATION או הרשאת ACCESS_FINE_LOCATION. אם לא, המערכת תזרוק SecurityException

לכן, עליכם לוודא שאתם עומדים בדרישות המוקדמות הנדרשות. לפני שמתחילים שירות שפועל בחזית. שירות שפועל בחזית מסמכי תיעוד בנושא type מפרטת את הדרישות המוקדמות הנדרשות לכל סוג של שירות שפועל בחזית.

הפעלת שירות שפועל בחזית

לפני שמבקשים מהמערכת להפעיל שירות כשירות שפועל בחזית, מתחילים השירות עצמו:

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() השיטה הזו משתמשת בערך בוליאני שמציין אם להסיר את שורת הסטטוס גם התראה על כך. חשוב לזכור שהשירות ממשיך לפעול.

אם עוצרים את השירות בזמן שהוא פועל בחזית, ההתראה שלו הוסר.

טיפול בעצירה ביוזמת המשתמש של אפליקציות שפועלות בשירותים שפועלים בחזית

בחלק התחתון של חלונית ההזזה להתראות מופיע לחצן שמציין את
    מספר האפליקציות שפועלות כרגע ברקע. כשלוחצים על
    תופיע תיבת דו-שיח עם רשימת השמות של האפליקציות השונות. 
    לחצן העצירה מופיע משמאל לכל אפליקציה
איור 1. תהליך העבודה של מנהל המשימות במכשירים מערכת ההפעלה Android מגרסה 13 ואילך.

החל מ-Android 13 (רמת API 33), משתמשים יכולים להשלים תהליך עבודה דרך חלונית הזזה להתראות כדי להפסיק אפליקציה שיש לה שירותים שפועלים בחזית, בלי קשר לאפליקציה גרסת היעד של ה-SDK. הפרסום הזה, שנקרא מנהל המשימות, מציג רשימה של אפליקציות שמריצים כרגע שירות שפועל בחזית.

רשימה זו מסומנת בתווית אפליקציות פעילות. לצד כל אפליקציה מופיע לחצן עצירה. איור 1 ממחיש תהליך העבודה ב'מנהל המשימות' במכשיר שפועל Android 13.

כשהמשתמש לוחץ על הלחצן עצירה לצד האפליקציה שלכם במנהל המשימות מתרחשות הפעולות הבאות:

  • המערכת תסיר את האפליקציה מהזיכרון. לכן, כל האפליקציה מפסיקה, לא רק השירות שפועל בחזית.
  • המערכת מסירה את מקבץ הגיבוי של נתוני הפעילות של האפליקציה.
  • כל הפעלה של מדיה תיפסק.
  • ההתראה שמשויכת לשירות שפועל בחזית תוסר.
  • האפליקציה שלך נשארת בהיסטוריה.
  • המשימות המתוזמנות מופעלות במועד שנקבע להן.
  • התראות מופעלות במועד או בחלון הזמן שנקבעו להן.

כדי לבדוק שהאפליקציה פועלת כצפוי בזמן שמשתמש עוצר מריצים את פקודת ה-ADB הבאה בחלון טרמינל:

adb shell cmd activity stop-app PACKAGE_NAME

פטורים

המערכת מספקת כמה רמות של פטור לסוגים מסוימים של אפליקציות, כפי שמתואר בקטעים הבאים.

פטורים הם לפי אפליקציה, ולא לפי תהליך. אם המערכת פטורה מתהליך אחד app, כל שאר התהליכים באפליקציה פטורים גם הם.

הצגת פטורים במנהל המשימות בכלל

האפליקציות הבאות יכולות להפעיל שירות שפועל בחזית ולא להופיע מנהל המשימות בכלל:

  • אפליקציות ברמת המערכת
  • אפליקציות בטיחות; כלומר, אפליקציות שיש להן תפקיד ROLE_EMERGENCY
  • מכשירים שנמצאים ב מצב הדגמה

פטורים שניתנים לעצירה על ידי משתמשים

כשסוגי האפליקציות הבאים מפעילים שירות שפועל בחזית, הם מופיעים במנהל המשימות, אבל אין לחצן עצירה ליד שם האפליקציה שבה המשתמש צריך להקיש:

שימוש בממשקי API ייעודיים במקום בשירותים שפועלים בחזית

בתרחישים רבים לדוגמה, יש ממשקי API של Jetpack או פלטפורמות שאפשר להשתמש בהם כדי לעבוד אחרת, אפשר להשתמש בשירות שפועל בחזית. אם קיימת מומלץ להשתמש בממשק API מובנה, כמעט תמיד כדאי להשתמש בו במקום להשתמש בחזית לאחר השיפור. לרוב, ממשקי API מובנים למטרה מספקים נתונים נוספים לתרחיש לדוגמה יכולות שהייתם צריכים לפתח בעצמכם. לדוגמה, בועות API מטפלות בלוגיקה מורכבת של ממשק המשתמש אפליקציות להעברת הודעות שצריך להטמיע בהן תכונות של בועות צ'אט.

במסמכי התיעוד בנושא סוגי השירותים שפועלים בחזית חלופות טובות לשימוש במקום שירותים שפועלים בחזית.

הגבלות על הפעלת שירות שפועל בחזית מהרקע

אפליקציות שמטרגטות את Android 12 ואילך לא יכולות להתחיל לפעול בחזית בזמן שהאפליקציה פועלת ברקע, מלבד מספר במקרים שונים. אם אפליקציה מנסה להפעיל שירות שפועל בחזית בזמן שהאפליקציה פועלת ברקע ובחזית לא עונה על אחד מהמקרים החריגים, המערכת ForegroundServiceStartNotAllowedException

בנוסף, אם האפליקציה רוצה להפעיל שירות שפועל בחזית הרשאות בזמן שימוש (לדוגמה: חיישן גוף, מצלמה, מיקרופון או מקום הרשאות), לא ניתן ליצור את השירות בזמן שהאפליקציה פועלת ברקע. גם אם האפליקציה משתייכת לאחד מהפטורים מהפעלת הרקע ההגבלות. הסיבה לכך מוסברת בקטע הגבלות על הפעלת שירותים שפועלים בחזית שצריכים לפעול בזמן השימוש הרשאות.

פטורים מהגבלות של התחלת פעילות ברקע

במצבים הבאים האפליקציה יכולה להפעיל שירותים שפועלים בחזית גם שהאפליקציה רצה ברקע:

הגבלות על הפעלת שירותים שפועלים בחזית שצריכים הרשאות במהלך השימוש

ב-Android 14 (רמת API 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