תהליכים שפועלים ברקע יכולים לצרוך הרבה זיכרון וסוללה. לדוגמה, שידור מרומז עשוי להפעיל הרבה תהליכי רקע שנרשמו להאזין לו, גם אם התהליכים האלה לא מבצעים הרבה עבודה. הן עלולות להשפיע באופן משמעותי על הביצועים של המכשיר ועל חוויית המשתמש.
כדי לפתור את הבעיה הזו, ב-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).
אם האפליקציה שלכם משתמשת באחת מהכוונה האלה, כדאי להסיר את התלות בהן בהקדם האפשרי כדי שתוכלו לטרגט בצורה נכונה מכשירים עם Android מגרסה 7.0 ואילך. מסגרת Android מספקת כמה פתרונות לצמצום הצורך בשידורים המרומזים האלה. לדוגמה, JobScheduler
ו-WorkManager החדש מספקים מנגנונים חזקים לתזמון פעולות ברשת כשמתקיימים תנאים מסוימים, כמו חיבור לרשת ללא הגבלת נפח. עכשיו אפשר להשתמש גם ב-JobScheduler
כדי להגיב לשינויים אצל ספקי תוכן. JobInfo
אובייקטים מכילים את הפרמטרים ש-JobScheduler
משתמש בהם כדי לתזמן את העבודה. כשהתנאים של העבודה מתקיימים, המערכת מבצעת את העבודה הזו ב-JobService
של האפליקציה.
בדף הזה נסביר איך להשתמש בשיטות חלופיות, כמו JobScheduler
, כדי להתאים את האפליקציה להגבלות החדשות האלה.
הגבלות שהמשתמשים יוזמים
בדף Battery usage בהגדרות המערכת, המשתמש יכול לבחור מבין האפשרויות הבאות:
- ללא הגבלה: כל הפעולות ברקע מותרות, מה שעלול להגביר את צריכת הסוללה.
- אופטימיזציה (ברירת מחדל): אופטימיזציה של היכולת של האפליקציה לבצע פעולות ברקע, על סמך האינטראקציה של המשתמש עם האפליקציה.
- מוגבלת: האפשרות הזו מונעת לחלוטין את הפעלת האפליקציה ברקע. יכול להיות שהאפליקציות לא יפעלו כמצופה.
אם אפליקציה מסוימת מציגה חלק מההתנהגויות הבעייתיות שמתוארות בנתונים חיוניים של Android, המערכת עשויה להציג למשתמש בקשה להגביל את הגישה של האפליקציה למשאבי המערכת.
אם המערכת מזהה שאפליקציה צורכת יותר מדי משאבים, היא מודיעה על כך למשתמש ומציעה לו אפשרות להגביל את הפעולות של האפליקציה. התנהגויות שיכולות להפעיל את ההודעה כוללות:
- שימוש מוגזם בחסימות מצב שינה: חסימה חלקית של מצב השינה למשך שעה כשהמסך כבוי
- שירותי רקע מוגזמים: אם האפליקציה מטרגטת רמות API נמוכות מ-26 ויש לה שירותי רקע מוגזמים
ההגבלות המדויקות שמוטלות על המכשיר נקבעות על ידי יצרן המכשיר. לדוגמה, בגרסאות של AOSP שמריצות Android 9 (רמת API 28) ומעלה, לאפליקציות שפועלות ברקע ונמצאות במצב 'מוגבל' יש את המגבלות הבאות:
- אי אפשר להפעיל שירותים שפועלים בחזית
- שירותים קיימים שפועלים בחזית מוסרים מהחזית
- ההתראות לא מופעלות
- המשימות לא מבוצעות
בנוסף, אם אפליקציה מטרגטת ל-Android 13 (רמת API 33) ומעלה והיא במצב 'מוגבל', המערכת לא מעבירה את השידור BOOT_COMPLETED
או את השידור LOCKED_BOOT_COMPLETED
עד שהאפליקציה מופעלת מסיבות אחרות.
ההגבלות הספציפיות מפורטות במאמר בנושא הגבלות על ניהול צריכת החשמל.
הגבלות על קבלת שידורים של פעילות ברשת
אפליקציות שמטרגטות את Android 7.0 (רמת API 24) לא מקבלות שידורי CONNECTIVITY_ACTION
אם הן נרשמות לקבלת השידורים האלה במניפסט שלהן, ותהליכים שתלויים בשידור הזה לא יתחילו. זה עלול ליצור בעיה לאפליקציות שרוצות להאזין לשינויים ברשת או לבצע פעולות רשת בכמות גדולה כשהמכשיר מתחבר לרשת ללא הגבלת נפח. קיימים כבר כמה פתרונות במסגרת Android כדי לעקוף את ההגבלה הזו, אבל הבחירה בפתרון הנכון תלויה במטרה של האפליקציה.
הערה: מכשיר BroadcastReceiver
שרשום ל-Context.registerReceiver()
ממשיך לקבל את השידורים האלה בזמן שהאפליקציה פועלת.
תזמון משימות ברשת בחיבורים ללא הגבלת נפח
כשמשתמשים במחלקה JobInfo.Builder
כדי ליצור את האובייקט JobInfo
, צריך להחיל את השיטה setRequiredNetworkType()
ולהעביר את JobInfo.NETWORK_TYPE_UNMETERED
כפרמטר של עבודה. דוגמת הקוד הבאה מתזמנת שירות להפעלה כשהמכשיר מתחבר לרשת לא מוגבלת ומטעין:
Kotlin
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) }
Java
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); }
כשהתנאים להרצת העבודה מתקיימים, האפליקציה מקבלת קריאה חוזרת להרצת השיטה onStartJob()
בJobService.class
שצוין. דוגמאות נוספות להטמעה של JobScheduler
זמינות באפליקציית הדוגמה JobScheduler.
חלופה חדשה ל-JobScheduler היא WorkManager, ממשק API שמאפשר לתזמן משימות ברקע שצריכות להסתיים בוודאות, בלי קשר לשאלה אם תהליך האפליקציה פעיל או לא. WorkManager בוחר את הדרך המתאימה להפעלת העבודה (ישירות בשרשור בתהליך האפליקציה, וגם באמצעות JobScheduler, FirebaseJobDispatcher או AlarmManager) על סמך גורמים כמו רמת ה-API של המכשיר. בנוסף, WorkManager לא דורש את שירותי Google Play ומספק כמה תכונות מתקדמות, כמו שרשור משימות או בדיקת הסטטוס של משימה. מידע נוסף זמין במאמר בנושא WorkManager.
מעקב אחרי הקישוריות לרשת בזמן שהאפליקציה פועלת
אפליקציות שפועלות עדיין יכולות להאזין לCONNECTIVITY_CHANGE
עם BroadcastReceiver
רשום. עם זאת, ה-API ConnectivityManager
מספק שיטה חזקה יותר לבקשת קריאה חוזרת רק כשמתקיימים תנאים ספציפיים ברשת.
אובייקטים של NetworkRequest
מגדירים את הפרמטרים של
הקריאה החוזרת לרשת במונחים של NetworkCapabilities
. אתם יוצרים NetworkRequest
אובייקטים באמצעות המחלקה NetworkRequest.Builder
. registerNetworkCallback()
ואז מעבירה את האובייקט NetworkRequest
למערכת. כשתנאי הרשת מתקיימים, האפליקציה מקבלת קריאה חוזרת (callback) כדי להפעיל את השיטה 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) נוספו ל-API של JobInfo
השיטות הבאות:
-
JobInfo.TriggerContentUri()
- מכיל פרמטרים שנדרשים להפעלת משימה בשינויים ב-URI של התוכן.
-
JobInfo.Builder.addTriggerContentUri()
-
מעבירה אובייקט
TriggerContentUri
אלJobInfo
. ContentObserver
עוקב אחרי ה-URI של התוכן המצורף. אם יש כמה אובייקטים שלTriggerContentUri
שמשויכים לעבודה, המערכת מספקת קריאה חוזרת גם אם היא מדווחת על שינוי רק באחד ממזהי ה-URI של התוכן. -
מוסיפים את הדגל
TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
כדי להפעיל את העבודה אם יש שינוי בצאצאים של ה-URI שצוין. הדגל הזה תואם לפרמטרnotifyForDescendants
שמועבר אלregisterContentObserver()
.
הערה: אי אפשר להשתמש ב-TriggerContentUri()
בשילוב עם setPeriodic()
או setPersisted()
. כדי לעקוב באופן רציף אחרי שינויים בתוכן, צריך לתזמן JobInfo
חדש לפני שJobService
של האפליקציה מסיים לטפל בקריאה החוזרת האחרונה.
בדוגמת הקוד הבאה מתוזמנת משימה להפעלה כשהמערכת מדווחת על שינוי ב-URI של התוכן, MEDIA_URI
:
Kotlin
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) }
Java
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 של התוכן שצוין, האפליקציה מקבלת קריאה חוזרת ואובייקט 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 של התוכן שהפעילו את העבודה נרשמים:
Kotlin
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 }
Java
@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 Debug Bridge (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