שירותים שפועלים בחזית מבצעים פעולות שהמשתמשים יכולים להבחין בהן.
בשירותים שפועלים בחזית מוצג שורת סטטוס , כדי ליידע את המשתמשים מבצעת משימה בחזית ומשתמשת במשאבי מערכת.
דוגמאות לאפליקציות שמשתמשות בשירותים שפועלים בחזית:
- אפליקציה של נגן מוזיקה שמשמיעה מוזיקה בשירות שפועל בחזית. ההתראה יכול להראות את השיר הנוכחי שמתנגן.
- אפליקציית כושר שמתעדת את הריצה של משתמש בשירות שפועל בחזית, אחרי קבלת הרשאה מהמשתמש. יכול להיות שבהתראה יוצג המרחק שהמשתמש עבר בו במהלך סשן הכושר הנוכחי.
יש להשתמש בשירות שפועל בחזית רק כשהאפליקציה צריכה לבצע משימה בולט לעין, גם אם אין למשתמשים אינטראקציה ישירה איתם את האפליקציה. אם הפעולה חשובה מספיק שכדי להשתמש בה הודעה בעדיפות מינימלית, יצירת רקע משימה זו.
במסמך הזה מתוארת ההרשאה הנדרשת כדי להשתמש בשירותים שפועלים בחזית, ואיך להפעיל שירות שפועל בחזית ולהסיר אותו מהרקע. כמו כן מוסבר איך לשייך תרחישים לדוגמה מסוימים לסוגי שירותים שפועלים בחזית, הגבלות הגישה שנכנסות לתוקף כשמפעילים שירות שפועל בחזית מאפליקציה שפועלת ברקע.
המשתמש יכול לסגור התראה כברירת מחדל
החל מ-Android 13 (רמת API 33), משתמשים יכולים לסגור את ההתראה שמשויכים לשירות שפועל בחזית כברירת מחדל. כדי לעשות זאת, המשתמשים מבצעים החלקה על ההתראה, בדרך כלל, ההתראה לא נסגר, אלא אם השירות שפועל בחזית מופסק או הוסר מהחזית.
אם רוצים שהמשתמש לא יוכל לסגור את ההתראה, מעבירים
true
בsetOngoing()
כאשר יוצרים את ההתראה באמצעות Notification.Builder
.
שירותים שמציגים התראה באופן מיידי
אם לשירות שפועל בחזית יש לפחות אחד מהמאפיינים הבאים, המערכת מציגה את ההתראה המשויכת מיד לאחר הפעלת השירות, גם במכשירים עם Android 12 ואילך:
- השירות משויך להתראה שכוללת פעולה לחצנים.
- לשירות יש
foregroundServiceType
מתוךmediaPlayback
,mediaProjection
, אוphoneCall
. - השירות מספק תרחיש לדוגמה שקשור לשיחות טלפון, לניווט או למדיה הפעלה, כפי שמוגדר בקטגוריה של ההתראה .
- השירות ביטל את הסכמתו לשינוי ההתנהגות על ידי העברת
FOREGROUND_SERVICE_IMMEDIATE
לתוךsetForegroundServiceBehavior()
כשמגדירים את ההתראה.
ב-Android מגרסה 13 (רמת API 33) ואילך, אם המשתמש דוחה את הרשאה לשליחת התראות, הם עדיין רואים הודעות שקשורות לשירותים שפועלים בחזית מנהל המשימות אבל אינם רואים אותם בחלונית ההזזה של ההודעות.
הצהרה על שירותים שפועלים בחזית במניפסט
במניפסט של האפליקציה צריך להצהיר על כל אחד מהשירותים שפועלים בחזית האפליקציה
עם <service>
לרכיב מסוים. לכל שירות, משתמשים
מאפיין android:foregroundServiceType
כדי להצהיר מהו סוג העבודה שהשירות מבצע.
לדוגמה, אם האפליקציה יוצרת שירות שפועל בחזית שמשמיעה מוזיקה, יכול להצהיר על השירות כך:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
<application ...>
<service
android:name=".MyMediaPlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="false">
</service>
</application>
</manifest>
אם יש כמה סוגים שרלוונטיים לשירות שלך, צריך להפריד ביניהם באמצעות באמצעות |
. לדוגמה, שירות שמשתמש במצלמה ובמיקרופון
היה מצהיר על כך:
android:foregroundServiceType="camera|microphone"
בקשת הרשאות לשירות שפועל בחזית
אפליקציות שמטרגטות את Android 9 (רמת API 28) ואילך שמשתמשות בשירותים שפועלים בחזית
צריכים לבקש
FOREGROUND_SERVICE
בקובץ המניפסט של האפליקציה, כפי שמוצג בקטע הקוד הבא. זהו מצב רגיל
, כך שהמערכת
מעניק אותו באופן אוטומטי לאפליקציה ששלחה את הבקשה.
בנוסף, אם האפליקציה מטרגטת רמת API 34 ומעלה, היא חייבת לבקש
סוג ההרשאה המתאים לסוג העבודה שהשירות שפועל בחזית
לעשות. לכל סוג שירות שפועל בחזית
יש סוג הרשאה תואם. לדוגמה, אם אפליקציה מפעילה
בשירות שפועל בחזית שמשתמש במצלמה, צריך לבקש גם את
FOREGROUND_SERVICE
ו-FOREGROUND_SERVICE_CAMERA
הרשאות. אלו כל ההרשאות הרגילות, לכן המערכת מעניקה אותן
באופן אוטומטי, אם הן נכללות במניפסט.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA"/>
<application ...>
...
</application>
</manifest>
דרישות מוקדמות לשימוש בשירות שפועל בחזית
החל מ-Android 14 (רמת API 34), כשמפעילים שירות שפועל בחזית
המערכת בודקת דרישות מוקדמות ספציפיות בהתאם לסוג השירות. לדוגמה,
אם תנסו להפעיל שירות שפועל בחזית מסוג location
, המערכת תבדוק
כדי לוודא שבאפליקציה כבר יש ACCESS_COARSE_LOCATION
או
הרשאת ACCESS_FINE_LOCATION
. אם לא, המערכת תזרוק
SecurityException
לכן, עליכם לוודא שאתם עומדים בדרישות המוקדמות הנדרשות. לפני שמתחילים שירות שפועל בחזית. שירות שפועל בחזית מסמכי תיעוד בנושא type מפרטת את הדרישות המוקדמות הנדרשות לכל סוג של שירות שפועל בחזית.
הפעלת שירות שפועל בחזית
לפני שמבקשים מהמערכת להפעיל שירות כשירות שפועל בחזית, מתחילים השירות עצמו:
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);
בתוך השירות, בדרך כלל בonStartCommand()
, אפשר לבקש
שהשירות שלכם פועל בחזית. כדי לעשות את זה, צריך להתקשר
ServiceCompat.startForeground()
(זמין ב-androidx-core 1.12 ואילך). השיטה הזו פועלת באופן הבא
:
- השירות
- מספר שלם חיובי שמזהה באופן ייחודי את ההתראה בשורת הסטטוס
- האובייקט
Notification
עצמו - סוגי השירותים שפועלים בחזית זיהוי העבודה שביצע השירות
יכול להיות שהסוגים האלה הם קבוצת משנה של הסוגים המוצהרים במניפסט,
בהתאם לתרחיש הספציפי לדוגמה. לאחר מכן, אם צריך להוסיף עוד סוגי שירותים,
אפשר להתקשר שוב אל 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) } // ... } } //... }
הסרה של שירות מקדמת התמונה
כדי להסיר את השירות מחזית השירות, צריך להתקשר
stopForeground()
השיטה הזו משתמשת בערך בוליאני שמציין אם להסיר את שורת הסטטוס
גם התראה על כך. חשוב לזכור שהשירות ממשיך לפעול.
אם עוצרים את השירות בזמן שהוא פועל בחזית, ההתראה שלו הוסר.
טיפול בעצירה ביוזמת המשתמש של אפליקציות שפועלות בשירותים שפועלים בחזית
החל מ-Android 13 (רמת API 33), משתמשים יכולים להשלים תהליך עבודה דרך חלונית הזזה להתראות כדי להפסיק אפליקציה שיש לה שירותים שפועלים בחזית, בלי קשר לאפליקציה גרסת היעד של ה-SDK. הפרסום הזה, שנקרא מנהל המשימות, מציג רשימה של אפליקציות שמריצים כרגע שירות שפועל בחזית.
רשימה זו מסומנת בתווית אפליקציות פעילות. לצד כל אפליקציה מופיע לחצן עצירה. איור 1 ממחיש תהליך העבודה ב'מנהל המשימות' במכשיר שפועל Android 13.
כשהמשתמש לוחץ על הלחצן עצירה לצד האפליקציה שלכם במנהל המשימות מתרחשות הפעולות הבאות:
- המערכת תסיר את האפליקציה מהזיכרון. לכן, כל האפליקציה מפסיקה, לא רק השירות שפועל בחזית.
- המערכת מסירה את מקבץ הגיבוי של נתוני הפעילות של האפליקציה.
- כל הפעלה של מדיה תיפסק.
- ההתראה שמשויכת לשירות שפועל בחזית תוסר.
- האפליקציה שלך נשארת בהיסטוריה.
- המשימות המתוזמנות מופעלות במועד שנקבע להן.
- התראות מופעלות במועד או בחלון הזמן שנקבעו להן.
כדי לבדוק שהאפליקציה פועלת כצפוי בזמן שמשתמש עוצר מריצים את פקודת ה-ADB הבאה בחלון טרמינל:
adb shell cmd activity stop-app PACKAGE_NAME
פטורים
המערכת מספקת כמה רמות של פטור לסוגים מסוימים של אפליקציות, כפי שמתואר בקטעים הבאים.
פטורים הם לפי אפליקציה, ולא לפי תהליך. אם המערכת פטורה מתהליך אחד app, כל שאר התהליכים באפליקציה פטורים גם הם.
הצגת פטורים במנהל המשימות בכלל
האפליקציות הבאות יכולות להפעיל שירות שפועל בחזית ולא להופיע מנהל המשימות בכלל:
- אפליקציות ברמת המערכת
- אפליקציות בטיחות; כלומר, אפליקציות שיש להן
תפקיד
ROLE_EMERGENCY
- מכשירים שנמצאים ב מצב הדגמה
פטורים שניתנים לעצירה על ידי משתמשים
כשסוגי האפליקציות הבאים מפעילים שירות שפועל בחזית, הם מופיעים במנהל המשימות, אבל אין לחצן עצירה ליד שם האפליקציה שבה המשתמש צריך להקיש:
- אפליקציות של בעלי המכשיר
- הבעלים של הפרופיל
- אפליקציות מתמידות
- אפליקציות שכוללות
תפקיד
ROLE_DIALER
שימוש בממשקי API ייעודיים במקום בשירותים שפועלים בחזית
בתרחישים רבים לדוגמה, יש ממשקי API של Jetpack או פלטפורמות שאפשר להשתמש בהם כדי לעבוד אחרת, אפשר להשתמש בשירות שפועל בחזית. אם קיימת מומלץ להשתמש בממשק API מובנה, כמעט תמיד כדאי להשתמש בו במקום להשתמש בחזית לאחר השיפור. לרוב, ממשקי API מובנים למטרה מספקים נתונים נוספים לתרחיש לדוגמה יכולות שהייתם צריכים לפתח בעצמכם. לדוגמה, בועות API מטפלות בלוגיקה מורכבת של ממשק המשתמש אפליקציות להעברת הודעות שצריך להטמיע בהן תכונות של בועות צ'אט.
במסמכי התיעוד בנושא סוגי השירותים שפועלים בחזית חלופות טובות לשימוש במקום שירותים שפועלים בחזית.
הגבלות על הפעלת שירות שפועל בחזית מהרקע
אפליקציות שמטרגטות את Android 12 ואילך לא יכולות להתחיל לפעול בחזית
בזמן שהאפליקציה פועלת ברקע, מלבד מספר
במקרים שונים. אם אפליקציה מנסה להפעיל
שירות שפועל בחזית בזמן שהאפליקציה פועלת ברקע ובחזית
לא עונה על אחד מהמקרים החריגים, המערכת
ForegroundServiceStartNotAllowedException
בנוסף, אם האפליקציה רוצה להפעיל שירות שפועל בחזית הרשאות בזמן שימוש (לדוגמה: חיישן גוף, מצלמה, מיקרופון או מקום הרשאות), לא ניתן ליצור את השירות בזמן שהאפליקציה פועלת ברקע. גם אם האפליקציה משתייכת לאחד מהפטורים מהפעלת הרקע ההגבלות. הסיבה לכך מוסברת בקטע הגבלות על הפעלת שירותים שפועלים בחזית שצריכים לפעול בזמן השימוש הרשאות.
פטורים מהגבלות של התחלת פעילות ברקע
במצבים הבאים האפליקציה יכולה להפעיל שירותים שפועלים בחזית גם שהאפליקציה רצה ברקע:
- האפליקציה עוברת ממצב גלוי למשתמש, כמו פעילות.
- האפליקציה יכולה להתחיל פעילות רקע, חוץ מאשר במקרה הצורך שבה יש פעילות באפליקציה במקבץ האחורי של משימה קיימת.
האפליקציה שלך מקבלת הודעה בעדיפות גבוהה באמצעות Firebase Cloud העברת הודעות.
המשתמש מבצע פעולה ברכיב בממשק המשתמש שקשור לאפליקציה שלכם. לדוגמה, הם עשויים ליצור אינטראקציה עם בועה, התראה, הווידג'ט, או פעילות.
האפליקציה מפעילה התראה מדויקת כדי להשלים פעולה שהמשתמש מבקש.
האפליקציה היא הקלט הנוכחי של המכשיר .
האפליקציה שלך מקבלת אירוע שקשור אל גבולות וירטואליים או פעילות מעבר לזיהוי של.
לאחר שהמכשיר מופעל מחדש ומקבל את
ACTION_BOOT_COMPLETED
ACTION_LOCKED_BOOT_COMPLETED
, אוACTION_MY_PACKAGE_REPLACED
פעולת Intent במקלט שידורים.האפליקציה שלך מקבלת
ACTION_TIMEZONE_CHANGED
ACTION_TIME_CHANGED
, אוACTION_LOCALE_CHANGED
פעולת Intent במקלט השידור.האפליקציה שלך מקבלת
ACTION_TRANSACTION_DETECTED
אירוע מ-NfcService
.אפליקציות עם הרשאות או תפקידי מערכת מסוימים, כמו מכשיר בעלים והפרופיל בעלים.
האפליקציה משתמשת בניהול מכשיר נלווה ומצהירה על
REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND
הרשאה אוREQUEST_COMPANION_RUN_IN_BACKGROUND
הרשאה. כשאפשר, השתמשוREQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND
באפליקציה שלך יש את
SYSTEM_ALERT_WINDOW
הרשאה.המשתמש השבית את אופטימיזציות הסוללה של האפליקציה שלכם.
הגבלות על הפעלת שירותים שפועלים בחזית שצריכים הרשאות במהלך השימוש
ב-Android 14 (רמת API 34) ואילך, יש מצבים מיוחדים שצריך לשים לב אליהם או אם אתם מפעילים שירות שפועל בחזית שזקוק להרשאות 'בזמן השימוש'.
אם האפליקציה מטרגטת את Android 14 ואילך, מערכת ההפעלה
בודק כשיוצרים שירות שפועל בחזית כדי לוודא שבאפליקציה יש את כל
את ההרשאות המתאימות לסוג השירות הזה. לדוגמה, כשיוצרים
שירות שפועל בחזית מסוג
מיקרופון, מערכת ההפעלה
המערכת מאמתת שבאפליקציה יש כרגע
RECORD_AUDIO
הרשאה. אם אין את ההרשאה הזו, המערכת תנפיק
SecurityException
זה יכול לגרום לבעיה בהרשאות 'בזמן השימוש'. אם לאפליקציה שלך יש
בזמן שימוש, יש לו את ההרשאה הזו רק בזמן שהוא
חזית. המשמעות היא שהאפליקציה פועלת ברקע והיא מנסה ליצור
שירות שפועל בחזית מסוג מצלמה, מיקום או מיקרופון, המערכת מזהה
שלאפליקציה שלכם אין כרגע את ההרשאות הנדרשות, והיא גורמת
SecurityException
.
באופן דומה, אם האפליקציה פועלת ברקע ויוצרת
שירות בריאות שדורש את ההרשאה BODY_SENSORS_BACKGROUND
, האפליקציה
אין כרגע את ההרשאה הזו, והמערכת גורמת לחריגה.
(האפשרות הזו לא רלוונטית אם מדובר בשירות בריאות שדורש הרשאות שונות,
כמו ACTIVITY_RECOGNITION
.) התקשרות
PermissionChecker.checkSelfPermission()
לא מונעת את הבעיה הזו. אם לאפליקציה יש הרשאה 'בזמן השימוש', וגם
הפונקציה קוראת ל-checkSelfPermission()
כדי לבדוק אם יש לו את ההרשאה הזו.
הפונקציה מחזירה את הערך PERMISSION_GRANTED
גם אם האפליקציה פועלת ברקע. כאשר
הפונקציה מחזירה את הערך PERMISSION_GRANTED
, כתוב "לאפליקציה שלך יש את ההרשאה הזו"
בזמן שהאפליקציה בשימוש."
לכן, אם נדרשת הרשאה לשירות שפועל בחזית, במהלך השימוש,
חייבים להתקשר אל Context.startForegroundService()
או Context.bindService()
בזמן
באפליקציה יש פעילות גלויה, אלא אם השירות שייך לאחד
פטורים מוגדרים.
פטורים מהגבלות על הרשאות 'בזמן השימוש'
במצבים מסוימים, גם אם שירות שפועל בחזית מופעל בזמן שהאפליקציה פועלת ריצה ברקע, תהיה לו עדיין גישה למיקום, מידע על המצלמה והמיקרופון בזמן שהאפליקציה פועלת בחזית. ("בזמן השימוש").
באותם מצבים, אם השירות מצהיר על
סוג השירות שפועל בחזית של location
, והוא מופעל על ידי אפליקציה
כולל
ACCESS_BACKGROUND_LOCATION
הרשאה, שירות זה יכול לגשת לפרטי המיקום כל הזמן, גם כאשר
שהאפליקציה רצה ברקע.
הרשימה הבאה כוללת את המצבים הבאים:
- רכיב מערכת מפעיל את השירות.
- השירות מתחיל באינטראקציה עם האפליקציה ווידג'טים.
- השירות מתחיל באינטראקציה עם ההתראה.
- השירות מתחיל
PendingIntent
שנשלח אפליקציה גלויה ושונה. - השירות מתחיל באפליקציה שעומדת בדרישות של מדיניות המכשיר שלט רחוק שפועל במצב 'בעלי המכשיר'.
- השירות מתחיל על ידי אפליקציה שמספקת את
VoiceInteractionService
. - השירות מתחיל באפליקציה
הרשאה מסוג
START_ACTIVITIES_FROM_BACKGROUND
.
איך לקבוע אילו שירותים מושפעים באפליקציה שלכם
כשבודקים את האפליקציה, צריך להפעיל את השירותים שפועלים בחזית. אם במסגרת שירות שהתחיל הגישה מוגבלת למיקום, למיקרופון ולמצלמה, להודעה הבאה מופיע ב-Logcat:
Foreground service started from background can not have \ location/camera/microphone access: service SERVICE_NAME