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