«

»

Dec 08

Foreground Service

ועדיפויות לסגירה ע”י מערכת ההפעלה: Foreground Service

 
 

רקע תאורטי:

 

ל Processes (תהליכים) באנדרואיד יש כמה דרגות חשיבות (במאמר על Process ותהליכונים באנדרואיד נרחיב יותר על הנושא) נפשיט פה את הנושא ונחלק ל 2 חשיבויות (למרות שיש יותר).

 

1. תהליכים שהם Foreground – קדמיים: אלו תהליכים שהמשתמש מפעיל אותם עכשיו באפליקציה שהוא עובד איתה והם נראים למשתמש. (יותר נכון התהליך שבו נמצא ה activity שהמשתמש רואה כרגע או תהליך שמכיל service שה activity מצביע ל IBind שלו).

 

2. תהליכים שהם Background – אחוריים: אלו תהליכים שרצים ברקע שהמשתמש לא רואה.

 

אם מערכת ההפעלה לחוצה בזיכרון קודם כל תמחק תהליכים שהם ברקע ורק אחרי זה תהליכים שהם מ’קדמיים’.

 

- אם מערכת ההפעלה כרגע מריצה קוד ב onCreate/onStartCommand/onDestroy של service אז התהליך המארח (hosting process) שעליו רץ ה service יהפוך לקדמי.

 

- אם ה service הוא כבר started אז העדיפות של התהליך המארח שלו היא נמוכה מתהליך קדמי אך גבוהה יותר מכל תהליך רקע. (בדרך כלל מעטים התהליכים שנראים ע”י המשתמש ולכן נדיר שה service יסגר, אבל ישנה אופציה כזאת)

 

חוץ מלתהליך שמארח את ה service, גם ל service עצמו יש מצב של קדמי ואחורי. והוא בברירת מחדל שלו, אחורי.
- אם יש ל service משתמשים שהם Bound אליו (יש להם reference אליו) אז החשיבות שלו תהיה לפחות כמו החשיבות של התהליך הכי גבוה מאלה שמצביעים אליו.

 

בשביל לקבוע ש service יהיה קדמי ללא קשר לתהליך שמריץ אותו אפשר לקרוא ל:
startForeground(int, Notification);

בשביל לשים לו (לתהליך המארח שלו) עדיפות של תהליך קדמי. ואז הוא יסגר ע”י מערכת ההפעלה רק במקרים קיצוניים.

 
 

דוגמה והסבר:

 

יצירת ההתראה והאייקון:

CharSequence text = "Service Running, Millisecs: " + System.currentTimeMillis();
CharSequence title = "Service Notification";
final Notification notification = new Notification(R.drawable.ic_launcher, text, System.currentTimeMillis());
PendingIntent notificationIntent = 
    PendingIntent.getActivity(ServiceExampleActivity.this, 0
	, new Intent(ServiceExampleActivity.this, ServiceExampleActivity.class)
	, 0);
notification.setLatestEventInfo(ServiceExampleActivity.this, title,
	text, notificationIntent);

 
הפיכת ה Service לקדמי:

_boundService.startForeground(notificationId, notification);

 
 

דוגמה לאפליקציה לשימוש:

 

כאשר אנחנו מפעילים תוכנת מוזיקה, נגיד youtube, כאשר אנחנו לוחצים על הפעלת וידאו רצוי שיווצר service שיוצר תהליכון שלוקח מהשרת את ה stream של הוידאו. ה service יפעיל את המוזיקה למשתמש. עכשיו כאשר המשתמש ילחץ על Home. אז youtube יהפוך לתהליך שהוא אחורי ואיתו גם ה service שמפעיל את המוזיקה. ועכשיו ייתכן שה service הזה של youtube יסגר. (למרות שהתהליך של youtube לא יסגר) ואז המשתמש ירגיש שהמוזיקה לא פועלת ולכן זה בעייתי כי ה service שמפעיל את המוזיקה למרות שהוא לא קדמי ומוצג ע”י המשתמש, הוא בפועל קדמי.
במצב כזה נגדיר את ה service כקדמי ב startForeground ואז תהיה לו עדיפות גבוהה לרוץ ומערכת ההפעלה תסגור services אחרים לפניו בשביל שהמשתמש יוכל להנות מהמוזיקה.
(נ.ב youtube בפועל לא ממשיך עם המוזיקה כאשר לוחצים על home הוא בטח סוגר את ה mediaPlayer)

 
 

לינק להורדת הפרויקט לדוגמה

 

בהצלחה !

2 comments

  1. רני

    תודה רבה על ההשקעה!

    ציטוטים ושאלות:
    ——————

    ציטוט 1 -

    אם מערכת ההפעלה כרגע מריצה קוד ב onStartCommand של service
    אז התהליך המארח שעליו רץ ה service יהפוך לקדמי.

    ציטוט 2 -

    אם ה service הוא כבר started אז העדיפות של התהליך המארח שלו
    היא נמוכה מתהליך קדמי אך גבוהה יותר מכל תהליך רקע.

    שאלות :
    - מה הוא “תהליך מארח”?
    - ולמה כאשר רץ אירוע onStartCommand העדיפות של התהליך המארח
    היא גבוהה יותר מאשר אחרי שהסרביס “הוא כבר started”?
    - אילו עוד דברים מארח התהליך? מה הקיבולת שלו?

    ציטוט 3 -

    - אם יש ל service משתמשים שהם Bound אליו,
    אז החשיבות שלו תהיה לפחות כמו החשיבות של התהליך הכי גבוה מאלה שמצביעים אליו.

    שאלה :
    אז אם יש כמה תהליכים שמצביעים אליו,
    מה בעצם קורה אם התהליך “הכי גבוה” שמצביע אליו הוא לא הכי חשוב?

    ציטוט 4 -

    בשביל לקבוע ש service יהיה קדמי ללא קשר לתהליך שמריץ אותו אפשר לקרוא ל:
    startForeground(int, Notification);

    שאלה :
    מדוע נדרש שימוש ב”נוטיפיקיישן” בשביל לקבוע שסרביס יהיה קדמי?
    ומה לגבי הקשר בין נוטיפיקיישן עבור סרביסים אחוריים, או כאלו שהם bind?

    זה נושא שקשה להסביר…
    תודה בכל מקרה על הניסיון.

  2. ליאור זיוי

    היי רני,

    ראשית – תודה על זה שהשקעת את הזמן לקרוא את המאמרים שלי.
    שנית – אני מתנצל על התגובה המאוחרת לשאלה שלך (חצי שנה) אני כבר לא כל כך מתחזק את האתר ולכן לא הייתי זמן לפני.

    אני משער שמפאת הזמן התשובה שלי כבר לא רלוונטית אבל למקרה שכן אני עונה לך פה :)

    שאלה :
    - מה הוא “תהליך מארח”?

    תשובה:
    ה Process שמריץ את האפליקציה שלך – את ה Service.

    שאלה:
    - ולמה כאשר רץ אירוע onStartCommand העדיפות של התהליך המארח
    היא גבוהה יותר מאשר אחרי שהסרביס “הוא כבר started”?

    תשובה:
    onStartCommand רץ על ה MainThread ולכן העדיפות היא גבוה יותר מסרביץ שהוא started שלא מוגדר כ Foreground וגם לא מקושר ל Activity שמוצג למשתמש

    שאלה:
    - אילו עוד דברים מארח התהליך? מה הקיבולת שלו?
    תשובה:
    התהליך מריץ את האפליקציה שלך, מערכת ההפעלה מקצה לו זכרון והקיבולת שלו נקבעת ע”י אילוצים של מערכת ההפעלה בהתאם לחומרה של המכשיר.

    שאלה :
    אז אם יש כמה תהליכים שמצביעים אליו,
    מה בעצם קורה אם התהליך “הכי גבוה” שמצביע אליו הוא לא הכי חשוב?
    תשובה:
    מה שרציתי להגיד פשוט שהעדיפות של ה Service כלפי מערכת ההפעלה נקבעת מהעדיפות הכי גבוה של התהליכים שמשתמשים ב service.

    שאלה :
    מדוע נדרש שימוש ב”נוטיפיקיישן” בשביל לקבוע שסרביס יהיה קדמי?
    ומה לגבי הקשר בין נוטיפיקיישן עבור סרביסים אחוריים, או כאלו שהם bind?
    תשובה:
    זוהי דרישה של גוגל, שכך המשתמש ידע שמשהו פועל ברקע. בשביל שמפתחים פשוט לא יצרו דברים שירוצו ברקע ויטחנו את המכשיר של המשתמש בלי שהוא ידע עליהם.
    לדוגמא שים לב שב Waze שאתה לוחץ על Home, עדיין יש את האייקון שלהם למעלה.

Leave a Reply

Your email address will not be published.

אתם יכולים להשתמש באפשרויות ותגי ה-HTMLהבאים: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>