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

יש שני שלבים להפעלת שירות בחזית מהאפליקציה. קודם צריך להפעיל את השירות על ידי קריאה ל-context.startForegroundService(). לאחר מכן, השירות קורא ל-ServiceCompat.startForeground() כדי להפוך לשירות שפועל בחזית.

דרישות מוקדמות

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

  • אפליקציות שמטרגטות ל-Android 12 (רמת API 31) ומעלה לא יכולות להפעיל שירות שפועל בחזית כשהאפליקציה פועלת ברקע, למעט כמה חריגים ספציפיים. מידע נוסף על הכלל הזה ועל החריגים לו זמין במאמר הגבלות על הפעלת שירות חזיתי מהרקע.

  • אפליקציות שמטרגטות ל-Android 14 (רמת API 34) ואילך צריכות לבקש את ההרשאות המתאימות לסוג השירות שפועל בחזית. כשהאפליקציה מנסה להעביר שירות לחזית, המערכת בודקת אם יש לה את ההרשאות המתאימות. אם חסרה לאפליקציה הרשאה כלשהי, המערכת מחזירה את השגיאה SecurityException. לדוגמה, אם מנסים להפעיל שירות שפועל בחזית מסוג location, המערכת בודקת שלאפליקציה כבר יש הרשאה מסוג ACCESS_COARSE_LOCATION או ACCESS_FINE_LOCATION. במסמכי התיעוד בנושא סוג השירות שפועל בחזית מפורטים התנאים המוקדמים הנדרשים לכל סוג של שירות שפועל בחזית.

הפעלת שירות

כדי להפעיל שירות שפועל בחזית, צריך קודם להפעיל אותו כשירות רגיל (לא שירות שפועל בחזית):

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);

נקודות עיקריות לגבי הקוד

  • קטע הקוד מפעיל שירות. אבל השירות עדיין לא פועל בחזית. בתוך השירות עצמו, צריך לקרוא ל-ServiceCompat.startForeground() כדי לקדם את השירות לשירות חזיתי.

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

אחרי שהשירות פועל, צריך להתקשר אל ServiceCompat.startForeground() כדי לבקש שהשירות יפעל בחזית. בדרך כלל, מפעילים את השיטה הזו בשיטה onStartCommand() של השירות.

התג ServiceCompat.startForeground() מקבל את הפרמטרים הבאים:

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

    //...
}

נקודות עיקריות לגבי הקוד

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