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

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