תהליכים ברקע יכולים להיות עתירי זיכרון וסוללה. לדוגמה, שידור משתמע עשוי להפעיל הרבה תהליכי רקע שנרשמו להאזין לו, גם אם התהליכים האלה לא מבצעים הרבה עבודה. הדבר יכול להשפיע באופן משמעותי על ביצועי המכשיר ועל חוויית המשתמש.
כדי לפתור את הבעיה הזו, מערכת Android 7.0 (רמת API 24) מחילה את הדברים הבאים הגבלות:
- אפליקציות שמטרגטות את Android מגרסה 7.0 (רמת API 24) ואילך לא מקבלות שידורי
CONNECTIVITY_ACTION
אם הן מצהירות על מקלט השידור שלהן במניפסט. האפליקציות עדיין יפעלו לקבל שידורים שלCONNECTIVITY_ACTION
אם הם יירשמוBroadcastReceiver
עםContext.registerReceiver()
וההקשר הזה עדיין תקף. - אפליקציות לא יכולות לשלוח או לקבל שידורים של
ACTION_NEW_PICTURE
אוACTION_NEW_VIDEO
. האופטימיזציה הזו משפיעה על כל האפליקציות, לא רק על אלה שמטרגטות ל-Android 7.0 (רמת API 24).
אם האפליקציה משתמשת באחד מהאובייקטים האלה של Intent, צריך להסיר את יחסי התלות שבהם
בהקדם האפשרי כדי לטרגט כראוי מכשירים עם Android 7.0
ומעלה. מסגרת Android מספקת כמה פתרונות כדי לצמצם את הצורך בשידורים מרומזים כאלה. לדוגמה, JobScheduler
וגם
WorkManager החדש מספק מנגנונים יעילים לתזמון רשתות
כאשר מתקיימים התנאים המוגדרים, כמו חיבור לרשת
עומדים בדרישות. עכשיו אפשר גם להשתמש ב-JobScheduler
כדי להגיב לשינויים בספקי התוכן. JobInfo
אובייקטים כוללים את הפרמטרים ש-JobScheduler
שבו נעשה שימוש כדי לתזמן את העבודה שלך. כשהתנאים של המשימה מתקיימים, המערכת מבצעת אותה ב-JobService
של האפליקציה.
בדף הזה נלמד איך להשתמש בשיטות חלופיות, כמו
JobScheduler
, כדי להתאים את האפליקציה שלך לשינויים האלה
ההגבלות.
הגבלות ביוזמת המשתמש
בדף שימוש בסוללה בהגדרות המערכת, המשתמש יכול לבחור באחת מהאפשרויות הבאות:
- ללא הגבלה: מתן הרשאה לכל הפעולות ברקע, שעשויות לצרוך יותר סוללה.
- אופטימיזציה (ברירת מחדל): אופטימיזציה ליכולת של האפליקציה לבצע עבודה ברקע, על סמך האינטראקציה של המשתמש עם האפליקציה.
- מוגבלת: ההגדרה הזו מונעת לגמרי מאפליקציה לפעול ברקע. יכול להיות שהאפליקציות לא יפעלו כצפוי.
אם אפליקציה מציגה חלק מההתנהגויות השליליות שמתוארות במדדי הפעילות של Android, יכול להיות שהמערכת תבקש מהמשתמש להגביל את הגישה של האפליקציה הזו למשאבי המערכת.
אם המערכת מזהה שאפליקציה צורכת יותר מדי משאבים, היא תודיע למשתמש ותאפשר לו להגביל את הפעולות של האפליקציה. התנהגויות שעשויות להפעיל את ההודעה כוללות:
- יותר מדי חסימות מצב שינה: נעילת מצב שינה חלקית אחת מוחזקת למשך שעה כשהמסך כבוי
- שירותי רקע מוגזמים: אם האפליקציה מטרגטת לרמות API נמוכות מ-26 ויש לה יותר מדי שירותי רקע
ההגבלות המדויקות שהוטלו נקבעות על ידי יצרן המכשיר. לדוגמה, בגרסאות build של AOSP שפועלות עם Android 9 (רמת API 28) ואילך, לאפליקציות שפועלות ברקע במצב 'מוגבל' יש את המגבלות הבאות:
- אי אפשר להפעיל שירותים שפועלים בחזית
- שירותים קיימים שפועלים בחזית יוסרו ממנה
- ההתראות לא מופעלות
- משימות לא מתבצעות
כמו כן, אם אפליקציה מטרגטת את Android 13 (רמת API 33) ואילך, והיא נמצאת
"מוגבל" המצב הזה, המערכת לא מספקת את השידור של BOOT_COMPLETED
או
את השידור LOCKED_BOOT_COMPLETED
עד שהאפליקציה תופעל במכשירים אחרים
סיבות נוספות.
ההגבלות הספציפיות האלה מפורטות הגבלות על ניהול צריכת החשמל.
הגבלות על קבלת שידורים של פעילות ברשת
אפליקציות שמטרגטות ל-Android 7.0 (רמת API 24) לא מקבלות שידורים של CONNECTIVITY_ACTION
אם הן
להירשם כדי לקבל אותן במניפסט ובתהליכים שתלויים
השידור לא יתחיל. הדבר עלול ליצור בעיה באפליקציות שרוצות
כדי להאזין לשינויים ברשת או לבצע פעילויות ברשת בכמות גדולה כאשר
שהמכשיר מתחבר לרשת שאינה בשימוש לפי שימוש בנתונים. יש כמה פתרונות אפשריים
כבר קיימות ב-framework של Android, אבל בחירת
אחת תלויה במה שרוצים שהאפליקציה שלכם להשיג.
הערה: מכשיר BroadcastReceiver
שמשויך ל-Context.registerReceiver()
ממשיך לקבל את השידור הזה בזמן שהאפליקציה פועלת.
תזמון משימות ברשת בחיבורים ללא חיוב לפי שימוש בנתונים
כשמשתמשים בכיתה JobInfo.Builder
כדי לבנות את האובייקט JobInfo
, צריך להחיל את השיטה setRequiredNetworkType()
ולהעביר את JobInfo.NETWORK_TYPE_UNMETERED
כפרמטר של משימה. דוגמת הקוד הבאה
מתזמנת הפעלה של שירות כשהמכשיר מתחבר לרשת סלולרית
ברשת ונטען:
const val MY_BACKGROUND_JOB = 0 ... fun scheduleJob(context: Context) { val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val job = JobInfo.Builder( MY_BACKGROUND_JOB, ComponentName(context, MyJobService::class.java) ) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) .setRequiresCharging(true) .build() jobScheduler.schedule(job) }
public static final int MY_BACKGROUND_JOB = 0; ... public static void scheduleJob(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo job = new JobInfo.Builder( MY_BACKGROUND_JOB, new ComponentName(context, MyJobService.class)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) .setRequiresCharging(true) .build(); js.schedule(job); }
כשהתנאים של המשימה מתקיימים, האפליקציה תקבל קריאה חוזרת (callback) כדי שהיא תוכל לפעול
השיטה onStartJob()
ב
ציינת JobService.class
. דוגמאות נוספות להטמעת JobScheduler
זמינות באפליקציית הדוגמה JobScheduler.
חלופה חדשה ל-JobScheduler היא WorkManager, ממשק API שמאפשר לתזמן משימות רקע שצריך להבטיח את השלמתן, גם אם תהליך האפליקציה לא פועל. WorkManager בוחר את הדרך המתאימה להפעלת העבודה (באופן ישיר בשרשור בתהליך האפליקציה, וגם באמצעות JobScheduler, FirebaseJobDispatcher או AlarmManager) על סמך גורמים כמו רמת ה-API של המכשיר. בנוסף, WorkManager לא דורש את שירותי Play ומספק מספר תכונות מתקדמות, כמו שרשור של משימות ביחד או בדיקת סטטוס של משימה. למידע נוסף, ראו WorkManager.
מעקב אחר קישוריות הרשת בזמן שהאפליקציה פועלת
אפליקציות שפועלות עדיין יכולות להאזין ל-CONNECTIVITY_CHANGE
באמצעות BroadcastReceiver
רשום. אבל ה-API של ConnectivityManager
מספק שיטה מתקדמת יותר לשליחת בקשה
קריאה חוזרת (callback) רק כשתנאי הרשת שצוינו מתקיימים.
אובייקטים מסוג NetworkRequest
מגדירים את הפרמטרים של הקריאה החוזרת (callback) מהרשת במונחים של NetworkCapabilities
. שלך
ליצור NetworkRequest
אובייקטים עם המחלקה NetworkRequest.Builder
. registerNetworkCallback()
מעביר את האובייקט NetworkRequest
למערכת. מתי
שתנאי הרשת מתקיימים, האפליקציה מקבלת קריאה חוזרת כדי לבצע
השיטה onAvailable()
הוגדרה במחלקה ConnectivityManager.NetworkCallback
.
האפליקציה תמשיך לקבל קריאות חוזרות (callback) עד שהיא תצא או שהיא תתקשר
unregisterNetworkCallback()
הגבלות על קבלת שידורים של תמונות וסרטונים
ב-Android 7.0 (רמת API 24), אפליקציות לא יכולות לשלוח או לקבל שידורים של ACTION_NEW_PICTURE
או ACTION_NEW_VIDEO
. ההגבלה הזו עוזרת
להפחית את ההשפעה על הביצועים וחוויית המשתמש כאשר מספר אפליקציות
להוציא ממצב שינה כדי לעבד תמונה או סרטון חדשים. Android 7.0 (רמת API 24)
להרחיב את JobInfo
ואת JobParameters
כדי לספק פתרון חלופי.
הפעלת משימות כשיש שינויים ב-URI של תוכן
כדי להפעיל משימות כשיש שינויים ב-URI של תוכן, ב-Android 7.0 (רמת API 24) נוספו ל-JobInfo
API השיטות הבאות:
-
JobInfo.TriggerContentUri()
- עטיפת הפרמטרים הנדרשים להפעלת משימה כשיש שינויים במזהי URI של תוכן.
-
JobInfo.Builder.addTriggerContentUri()
-
מעביר אובייקט
TriggerContentUri
אלJobInfo
.ContentObserver
מנטר את URI של התוכן המתומצת. אם יש מספר אובייקטים שלTriggerContentUri
שמשויכים למשימה, המערכת מספקת גם אם היא מדווחת על שינוי באחד ממזהי ה-URI של התוכן. -
מוסיפים את הדגל
TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
כדי להפעיל את המשימה אם יש שינוי באחד מהצאצאים של ה-URI הנתון. הדגל הזה תואם לפרמטרnotifyForDescendants
שהועבר אלregisterContentObserver()
.
הערה: אי אפשר להשתמש ב-TriggerContentUri()
בשילוב עם setPeriodic()
או setPersisted()
. כדי לעקוב ברציפות אחר שינויים בתוכן, לתזמן
JobInfo
לפני סיום הטיפול בקריאה חוזרת (callback) של האפליקציה ב-JobService
.
הקוד לדוגמה הבא מתזמנ משימה להפעלה כשהמערכת מדווחת
שינוי ב-URI של התוכן, MEDIA_URI
:
const val MY_BACKGROUND_JOB = 0 ... fun scheduleJob(context: Context) { val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val job = JobInfo.Builder( MY_BACKGROUND_JOB, ComponentName(context, MediaContentJob::class.java) ) .addTriggerContentUri( JobInfo.TriggerContentUri( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS ) ) .build() jobScheduler.schedule(job) }
public static final int MY_BACKGROUND_JOB = 0; ... public static void scheduleJob(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo.Builder builder = new JobInfo.Builder( MY_BACKGROUND_JOB, new ComponentName(context, MediaContentJob.class)); builder.addTriggerContentUri( new JobInfo.TriggerContentUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS)); js.schedule(builder.build()); }
כשהמערכת מדווחת על שינוי ב-URI של התוכן שצוין, האפליקציה מקבלת קריאה חוזרת (callback) ואובייקט JobParameters
מועבר לשיטה onStartJob()
ב-MediaContentJob.class
.
איך בודקים אילו רשויות תוכן הפעילו משימה
Android 7.0 (רמת API 24) נמשכת גם JobParameters
עד
מאפשרת לאפליקציה לקבל מידע שימושי על רשויות התוכן
ו-URI הפעילו את המשימה:
-
Uri[] getTriggeredContentUris()
-
מערך של מזהי URI שהפעילו את המשימה. הערך יהיה
null
אם אין מזהי URI שהפעילו את המשימה (לדוגמה, המשימה בעקבות תאריך יעד או מסיבה אחרת), או מספר השינויים מספר מזהי ה-URI גדול מ-50. -
String[] getTriggeredContentAuthorities()
-
החזרת מערך מחרוזות של רשויות תוכן שהפעילו את המשימה.
אם המערך המוחזר הוא לא
null
, משתמשים ב-getTriggeredContentUris()
כדי לאחזר את הפרטים של מזהי ה-URI שהשתנו.
הקוד לדוגמה הבא מבטל את השיטה JobService.onStartJob()
ומתעדה את רשויות התוכן ואת מזהי ה-URI שהפעילו את המשימה:
override fun onStartJob(params: JobParameters): Boolean { StringBuilder().apply { append("Media content has changed:\n") params.triggeredContentAuthorities?.also { authorities -> append("Authorities: ${authorities.joinToString(", ")}\n") append(params.triggeredContentUris?.joinToString("\n")) } ?: append("(No content)") Log.i(TAG, toString()) } return true }
@Override public boolean onStartJob(JobParameters params) { StringBuilder sb = new StringBuilder(); sb.append("Media content has changed:\n"); if (params.getTriggeredContentAuthorities() != null) { sb.append("Authorities: "); boolean first = true; for (String auth : params.getTriggeredContentAuthorities()) { if (first) { first = false; } else { sb.append(", "); } sb.append(auth); } if (params.getTriggeredContentUris() != null) { for (Uri uri : params.getTriggeredContentUris()) { sb.append("\n"); sb.append(uri); } } } else { sb.append("(No content)"); } Log.i(TAG, sb.toString()); return true; }
ביצוע אופטימיזציה נוספת לאפליקציה
אופטימיזציה של האפליקציות כך שיפעלו במכשירים עם נפח זיכרון נמוך או עם נפח זיכרון נמוך יכולים לשפר את הביצועים ואת חוויית המשתמש. הסרת יחסי התלות בשירותי רקע ובמקלטים משתמעים של שידורים שרשומים במניפסט יכולה לעזור לאפליקציה לפעול טוב יותר במכשירים כאלה. למרות מערכת Android 7.0 (רמת API 24) נוקטת פעולות כדי לצמצם חלק מהבעיות האלה, מומלץ לבצע אופטימיזציה של האפליקציה כך שתפעל בלי להשתמש תהליכי רקע לחלוטין.
הממשק הבא של הגשר לניפוי באגים ב-Android (ADB) פקודות יכולות לעזור לכם לבדוק את התנהגות האפליקציה כשתהליכים ברקע מושבתים:
- כדי לדמות תנאים שבהם שידורים משתמעים ושירותי רקע לא זמינים, מזינים את הפקודה הבאה:
-
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore
- כדי להפעיל מחדש את השידורים המשתמעים ואת שירותי הרקע, מזינים את הפקודה הבאה:
-
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow
- אתם יכולים לדמות מצב שבו המשתמש מעביר את האפליקציה שלכם למצב 'מוגבל' בנוגע לשימוש בסוללה ברקע. ההגדרה הזו מונעת מהאפליקציה לפעול ברקע. כדי לעשות זאת, מריצים את הפקודה הבאה בחלון טרמינל:
-
$ adb shell cmd appops set <PACKAGE_NAME> RUN_ANY_IN_BACKGROUND deny