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