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