שינויים בהתנהגות ב-Android 8.0

לצד תכונות ויכולות חדשות, מערכת Android 8.0 (רמת API 26) כוללת מגוון שינויים בהתנהגות של מערכות ו-API. במסמך הזה נדגיש כמה מהשינויים העיקריים שצריך להבין ולהבין באפליקציות שלך.

רוב השינויים האלה משפיעים על כל האפליקציות, ללא קשר לגרסת Android שאליה הן מטרגטות. עם זאת, כמה שינויים משפיעים רק על אפליקציות שמטרגטות ל-Android 8.0. כדי להבהיר את הנושא, הדף הזה מחולק לשני חלקים: שינויים לכל האפליקציות ושינויים באפליקציות שמטרגטות את Android 8.0.

שינויים בכל האפליקציות

שינויי ההתנהגות האלה חלים על כל האפליקציות כשהן פועלות בפלטפורמת Android 8.0 (רמת API 26), ללא קשר לרמת ה-API שהן מטרגטות. כל המפתחים צריכים לבדוק את השינויים האלה ולשנות את האפליקציות שלהם כדי שיתמכו בהם כראוי, במקרים שבהם זה רלוונטי.

מגבלות על הרצה ברקע

אחד מהשינויים בגרסה 8.0 של Android‏ (רמת API‏ 26) שנועד לשפר את חיי הסוללה הוא שבזמן שהאפליקציה נמצאת במצב שנשמר במטמון, ללא רכיבים פעילים, המערכת משחררת את כל ה-wakelocks שהאפליקציה מחזיקה.

בנוסף, כדי לשפר את ביצועי המכשיר, המערכת מגבילה התנהגויות מסוימות של אפליקציות שלא פועלות בחזית. פרטים נוספים:

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

כברירת מחדל, ההגבלות האלה חלות רק על אפליקציות שמטרגטות את O. עם זאת, המשתמשים יכולים להפעיל את ההגבלות האלה לכל אפליקציה במסך הגדרות, גם אם האפליקציה לא טירגטה את O.

ב-Android 8.0 (רמת API‏ 26) יש גם את השינויים הבאים בשיטות ספציפיות:

  • השיטה startService() מקפיצה עכשיו IllegalStateException אם אפליקציה שמטרגטת את Android 8.0 תנסה להשתמש בשיטה הזו במצב שבו היא לא מורשית ליצור שירותים ברקע.
  • השיטה החדשה Context.startForegroundService() מפעילה שירות שפועל בחזית. המערכת מאפשרת לאפליקציות להפעיל את Context.startForegroundService() גם כשהן פועלות ברקע. עם זאת, האפליקציה חייבת להפעיל את השיטה startForeground() של השירות הזה תוך חמש שניות אחרי יצירת השירות.

מידע נוסף זמין במאמר מגבלות על ביצוע ברקע.

מגבלות על מיקום ברקע ב-Android

כדי לשמור על הסוללה, על חוויית המשתמש ועל תקינות המערכת, אפליקציות ברקע מקבלות עדכוני מיקום בתדירות נמוכה יותר כשמשתמשים בהן במכשיר עם Android 8.0. שינוי ההתנהגות הזה משפיע על כל האפליקציות שמקבלות עדכוני מיקום, כולל Google Play Services.

השינויים האלה משפיעים על ממשקי ה-API הבאים:

  • ספק מיקום משולב (FLP)
  • גבולות וירטואליים
  • מדידות GNSS
  • מנהל מיקומים
  • מנהל Wi-Fi

כדי לוודא שהאפליקציה תפעל כצפוי, יש לבצע את השלבים הבאים:

  • מומלץ לבדוק את הלוגיקה של האפליקציה ולוודא שנעשה שימוש בממשקי ה-API העדכניים של המיקום.
  • בודקים שהאפליקציה מתנהגת כצפוי בכל תרחיש לדוגמה.
  • מומלץ להשתמש בספק המיקום המשולב (FLP) או בגיאו-פיינס כדי לטפל בתרחישי לדוגמה שתלויים במיקום הנוכחי של המשתמש.

מידע נוסף על השינויים האלה זמין במאמר מגבלות על מיקום ברקע.

קיצורי דרך של אפליקציות

Android 8.0 (רמת API 26) כולל את השינויים הבאים במקשי קיצור של אפליקציות:

  • לשידור com.android.launcher.action.INSTALL_SHORTCUT כבר אין השפעה על האפליקציה שלכם, כי הוא עכשיו שידור פרטי וסמוי. במקום זאת, צריך ליצור קיצור דרך לאפליקציה באמצעות ה-method requestPinShortcut() מהמחלקה ShortcutManager.
  • עכשיו אפשר ליצור קיצורי דרך לאפליקציות באמצעות הכוונה ACTION_CREATE_SHORTCUT, ולנהל אותם באמצעות הכיתה ShortcutManager. הכוונה הזו יכולה גם ליצור קיצורי דרך מדור קודם במרכז האפליקציות שלא מקיימים אינטראקציה עם ShortcutManager. בעבר, הכוונה הזו הייתה יכולה ליצור רק קיצורי דרך מדור קודם למרכז האפליקציות.
  • קיצורי דרך שנוצרו באמצעות requestPinShortcut() וקיצורי דרך שנוצרו בפעילות שמטפלת בכוונה ACTION_CREATE_SHORTCUT הם עכשיו קיצורי דרך מלאים של אפליקציות. כתוצאה מכך, אפליקציות יכולות עכשיו לעדכן אותן באמצעות השיטות שמפורטות בקטע ShortcutManager.
  • קיצורי הדרך הקודמים שומרים על הפונקציונליות שלהם מגרסאות קודמות של Android, אבל צריך להמיר אותם לקיצור דרך של אפליקציה באופן ידני באפליקציה.

מידע נוסף על השינויים בקיצורי הדרך לאפליקציות זמין במדריך התכונות הצמדת קיצורי דרך וווידג'טים.

לוקאלים ובינלאומיות

ב-Android 7.0 (רמת API 24) הושקה האפשרות לציין לוקאל של קטגוריה שמוגדר כברירת מחדל, אבל בחלק מממשקי ה-API נעשה שימוש בשיטה הכללית Locale.getDefault(), ללא ארגומנטים, במקרים שבהם הם היו צריכים להשתמש במקום זאת בלוקאל של קטגוריית ברירת המחדל DISPLAY. ב-Android 8.0 (רמת API 26), בשיטות הבאות נעשה עכשיו שימוש ב-Locale.getDefault(Category.DISPLAY) במקום ב-Locale.getDefault():

Locale.getDisplayScript(Locale) חוזר גם לערך Locale.getDefault() כשהערך displayScript שצוין לארגומנט Locale לא זמין.

שינויים נוספים שקשורים ללוקאל ולתמיכה בשפות שונות הם:

  • קריאה ל-Currency.getDisplayName(null) מפעילה NullPointerException, בהתאם להתנהגות המתועדת.
  • ניתוח השם של אזור הזמן השתנה. בעבר, מכשירי Android השתמשו בערך של שעון המערכת שנדגם בזמן האתחול כדי לשמור במטמון את שמות אזורי הזמן ששימשו לניתוח תאריכים ושעות. כתוצאה מכך, יכול להיות שהניתוח יושפע לרעה אם שעון המערכת היה שגוי בזמן האתחול או במקרים נדירים אחרים.

    עכשיו, במקרים נפוצים, לוגיקה של הניתוח משתמשת ב-ICU ובערך הנוכחי של שעון המערכת בזמן הניתוח של שמות תחומי הזמן. השינוי הזה מספק תוצאות נכונות יותר, והן עשויות להיות שונות מגרסאות קודמות של Android כשבאפליקציה שלך נעשה שימוש במחלקות כמו SimpleDateFormat.

  • ב-Android 8.0 (רמת API‏ 26) הגרסה של ICU מתעדכנת לגרסה 58.

חלונות התראות

אם אפליקציה משתמשת בהרשאה SYSTEM_ALERT_WINDOW ומשתמשת באחד מסוגי החלונות הבאים כדי לנסות להציג חלונות התראה מעל אפליקציות אחרות וחלונות מערכת:

...החלונות האלה תמיד מופיעים מתחת לחלונות שנעשה בהם שימוש בסוג החלון TYPE_APPLICATION_OVERLAY. אם אפליקציה מטרגטת ל-Android 8.0 (רמת API‏ 26), היא משתמשת בסוג החלון TYPE_APPLICATION_OVERLAY כדי להציג חלונות התראה.

מידע נוסף זמין בקטע סוגי חלונות נפוצים לחלונות התראות במסגרת השינויים בהתנהגות של אפליקציות שמטרגטות את Android 8.0.

קלט וניווט

בעקבות ההשקה של אפליקציות ל-Android ב-ChromeOS וגורמי צורה גדולים אחרים, כמו טאבלטים, אנחנו רואים עלייה מחודשת בשימוש בניווט באמצעות המקלדת באפליקציות ל-Android. בגרסה 8.0 של Android‏ (רמת API‏ 26), שיפרנו את השימוש במקלדת כמכשיר קלט לניווט, וכתוצאה מכך יש מודל מהימן יותר וניתן לחיזוי לניווט באמצעות חיצים ולחיצה על Tab.

באופן ספציפי, ביצענו את השינויים הבאים בהתנהגות של התמקדות באלמנט:

  • אם לא הגדרתם צבעים למצב התמקדות לאובייקט View (ל-drawable של החזית או ל-drawable של הרקע), המסגרת מגדירה עכשיו צבע הדגשה של התמקדות כברירת מחדל לאובייקט View.View הדגשת המיקוד היא רכיב גרפי של תנודות שמבוסס על העיצוב של הפעילות.

    אם אתם לא רוצים שהאובייקט View ישתמש בהדגשה שמוגדרת כברירת מחדל כשהוא מקבל את המיקוד, צריך להגדיר את המאפיין android:defaultFocusHighlightEnabled לערך false בקובץ ה-XML של הפריסה שמכיל את View, או להעביר את הערך false אל setDefaultFocusHighlightEnabled() בלוגיקה של ממשק המשתמש של האפליקציה.

  • כדי לבדוק איך קלט המקלדת משפיע על המיקוד של רכיבי ממשק המשתמש, אפשר להפעיל את האפשרות למפתחים שרטוט > הצגת גבולות פריסה. ב-Android 8.0, האפשרות הזו מציגה את הסמל 'X' מעל האלמנט שכרגע מתמקד בו.

בנוסף, כל הרכיבים בסרגל הכלים ב-Android 8.0 הם באופן אוטומטי אשכולות ניווט במקלדת, כך שמשתמשים יכולים לנווט בקלות לכל סרגל כלים ולצאת ממנו כמכלול.

למידע נוסף על שיפור התמיכה בניווט באמצעות מקלדת בתוך האפליקציה, קראו את המדריך תמיכה בניווט באמצעות מקלדת.

מילוי אוטומטי של טפסים באינטרנט

עכשיו, כשAutofill Framework של Android מספק תמיכה מובנית בפונקציית המילוי האוטומטי, השתנו השיטות הבאות שקשורות לאובייקטים מסוג WebView באפליקציות שמותקנות במכשירים עם Android 8.0 (רמת API 26):

WebSettings
  • השיטה getSaveFormData() מחזירה עכשיו את הערך false. בעבר, השיטה הזו החזירה במקום זאת את הערך true.
  • לקריאה ל-setSaveFormData() כבר אין השפעה.
WebViewDatabase
  • לקריאה ל-clearFormData() כבר אין השפעה.
  • השיטה hasFormData() מחזירה עכשיו את הערך false. בעבר, השיטה הזו החזירה את הערך true כשהטופס הכיל נתונים.

נגישות

Android 8.0 (רמת API 26) כולל את השינויים הבאים בנגישות:

  • מסגרת הנגישות ממירה עכשיו את כל התנועות של הקשה כפולה לפעולות ACTION_CLICK. השינוי הזה מאפשר ל-TalkBack לפעול בצורה דומה יותר לשירותי נגישות אחרים.

    אם באובייקטים מסוג View באפליקציה שלכם נעשה שימוש בטיפול מותאם אישית במגע, עליכם לוודא שהם עדיין פועלים עם TalkBack. יכול להיות שתצטרכו פשוט לרשום את ה-handler של הקליקים שבו משתמשים View האובייקטים שלכם. אם TalkBack עדיין לא מזהה תנועות שמבוצעות על אובייקטים View האלה, משנים את הערך של performAccessibilityAction().

  • שירותי הנגישות יודעים עכשיו על כל המופעים של ClickableSpan באובייקטים של TextView באפליקציה.

למידע נוסף על שיפור הנגישות של האפליקציה, ראו נגישות.

רשתות וקישוריות HTTP(S)

ב-Android 8.0 (רמת API‏ 26) יש שינויים בהתנהגות של הרשתות וקישוריות ה-HTTP(S):

  • לבקשות OPTIONS ללא גוף יש כותרת Content-Length: 0. בעבר לא הייתה להם כותרת Content-Length.
  • כדי לתקן כתובות URL שמכילות נתיבים ריקים, הספרייה HttpURLConnection מוסיפה קו נטוי אחרי שם המארח או הרשות. לדוגמה, הפונקציה ממירה את הערך http://example.com לערך http://example.com/.
  • בורר proxy בהתאמה אישית שמוגדר באמצעות ProxySelector.setDefault() מטרגט רק את הכתובת (הסכמה, המארח והיציאה) של כתובת ה-URL המבוקשת. כתוצאה מכך, בחירת שרת ה-proxy עשויה להתבסס רק על הערכים האלה. כתובת URL שמועברת לבורר שרת proxy בהתאמה אישית לא כוללת את הנתיב, פרמטרים של השאילתה או מקטעים של כתובת ה-URL המבוקשת.
  • מזהי URI לא יכולים לכלול תוויות ריקות.

    בעבר, הפלטפורמה תמכה בפתרון עקיף לקבלת תוויות ריקות בשמות המארחים, שהוא שימוש לא חוקי במזהי URI. הפתרון הזה נועד לתאימות עם גרסאות ישנות יותר של libcore. מפתחים שמשתמשים ב-API יראו באופן שגוי הודעת ADB: "בכתובת ה-URI example.com יש תוויות ריקות בשם המארח. פורמט זה שגוי ולא יתקבל בגרסאות עתידיות של Android." ב-Android 8.0 הפתרון החלופי הזה הוסר, והמערכת מחזירה ערך null עבור URIs בפורמט שגוי.

  • ההטמעה של HttpsURLConnection ב-Android 8.0 לא מבצעת חזרה לגרסה לא מאובטחת של פרוטוקול TLS/‏SSL.
  • הטיפול במנהור חיבורי HTTP(S) השתנה באופן הבא:
    • כשמפעילים מנהרה של חיבור HTTPS דרך חיבור, המערכת ממוקמת בצורה נכונה את מספר היציאה (‎:443) בשורת המארח כששולחים את המידע הזה לשרת ביניים. בעבר, מספר היציאה הופיע רק בשורה CONNECT.
    • המערכת לא שולחת יותר כותרות של סוכן משתמש והרשאה לשרת proxy מבקשות שנשלחות במנהרה לשרת ה-proxy.

      המערכת לא שולחת יותר כותרת להרשאה של שרת proxy ב-Http(s)URLConnection עם מנהרה לשרת ה-proxy, בעת הגדרת המנהרה. במקום זאת, המערכת יוצרת כותרת של הרשאת proxy ושולחת אותה ל-proxy כשה-proxy שולח HTTP 407 בתגובה לבקשה הראשונית.

      באופן דומה, המערכת כבר לא מעתיקה את הכותרת של סוכן המשתמש מהבקשה שעוברת במנהרה לבקשת ה-proxy שמגדירה את המנהרה. במקום זאת, הספרייה יוצרת כותרת של סוכן משתמש לבקשה הזו.

  • אם שיטת connect()‎ שבוצעה קודם נכשלה, ה-method send(java.net.DatagramPacket)‎ תשליך SocketException.
    • אם יש שגיאה פנימית, הפונקציה DatagramSocket.connect() מגדירה את pendingSocketException. לפני Android 8.0, קריאה חוזרת של recv()‏ זרקה SocketException, גם אם קריאה של send()‏ הייתה מצליחה. כדי לשמור על עקביות, בשתי הקריאות מתרחשת עכשיו הודעת SocketException.
  • InetAddress.isReachable() מנסה את ה-ICMP לפני חזרה לפרוטוקול TCP Echo.
    • יכול להיות שחלק מהמארחים שסוגרים את יציאה 7 (TCP Echo), כמו google.com, יהיו עכשיו נגישים אם הם יקבלו את פרוטוקול ICMP Echo.
    • אם לא ניתן להגיע למארחים מסוימים, השינוי הזה אומר שיחלפו פי שניים יותר זמן עד שהקריאה תוחזר.

‫Bluetooth

בגרסה 8.0 של Android (רמת API‏ 26) מתבצעים השינויים הבאים באורך הנתונים שמשחזרת השיטה ScanRecord.getBytes():

  • השיטה getBytes() לא מבוססת על הנחות לגבי מספר הבייטים שהתקבלו. לכן, לא כדאי להסתמך על מספר מינימלי או מקסימלי של בייטים שמוחזרים. במקום זאת, צריך להעריך את האורך של המערך שנוצר.
  • מכשירים שתואמים ל-Bluetooth 5 עשויים להחזיר אורך נתונים שחורג מהאורך המקסימלי הקודם של כ-60 בייטים.
  • אם מכשיר מרוחק לא מספק תגובה לסריקה, יכול להיות שיוחזר גם פחות מ-60 בייטים.

קישוריות חלקה

בגרסה 8.0 של Android (רמת API‏ 26) נוספו כמה שיפורים להגדרות ה-Wi-Fi כדי שיהיה קל יותר לבחור את רשת ה-Wi-Fi שמספקת את חוויית המשתמש הטובה ביותר. שינויים ספציפיים כוללים:

  • שיפורים ביציבות ובאמינות.
  • ממשק משתמש אינטואיטיבי יותר לקריאה.
  • תפריט מאוחד של העדפות Wi-Fi.
  • במכשירים תואמים, הפעלה אוטומטית של Wi-Fi כשיש רשת שמורה באיכות גבוהה בקרבת מקום.

אבטחה

ב-Android מגרסה 8.0 יש את השינויים הבאים שקשורים לאבטחה:

  • הפלטפורמה כבר לא תומכת ב-SSLv3.
  • כשמקשרים HTTPS לשרת שמטמיע באופן שגוי את המשא ומתן על גרסת פרוטוקול TLS, HttpsURLConnection לא מנסה יותר את הפתרון החלופי של חזרה לגרסאות קודמות של פרוטוקול TLS וניסיון חוזר.
  • ב-Android 8.0 (רמת API‏ 26) חל מסנן של Secure Computing‏ (SECCOMP) על כל האפליקציות. רשימת הקריאות למערכת (syscalls) המותרות מוגבלת לאלה שנחשפות דרך bionic. למרות שיש כמה מערכות קריאה אחרות לתאימות לאחור, אנחנו ממליצים לא להשתמש בהן.
  • האובייקטים מסוג WebView באפליקציה פועלים עכשיו במצב מרובה תהליכים. תוכן האינטרנט מטופל בתהליך נפרד ומבודד מהתהליך של האפליקציה המכילה, כדי לשפר את האבטחה.
  • לא ניתן יותר להניח שקבצי APK נמצאים בספריות ששמותיהן מסתיימים ב-‎-1 או ב-‎-2. האפליקציות צריכות להשתמש ב-sourceDir כדי לקבל את הספרייה, ולא להסתמך ישירות על הפורמט של הספרייה.
  • מידע על שיפורי האבטחה שקשורים לשימוש בספריות מקוריות זמין במאמר ספריות מקוריות.

בנוסף, ב-Android 8.0 (רמת API 26) מוצגים השינויים הבאים שקשורים להתקנת אפליקציות לא ידועות ממקורות לא מוכרים:

  • הערך של ההגדרה הקודמת INSTALL_NON_MARKET_APPS הוא עכשיו תמיד 1. כדי לקבוע אם מקור לא ידוע יכול להתקין אפליקציות באמצעות מנהל ההתקנה של החבילה, צריך להשתמש במקום זאת בערך המוחזר של canRequestPackageInstalls().
  • אם מנסים לשנות את הערך של INSTALL_NON_MARKET_APPS באמצעות setSecureSetting(), מתקבלת הודעת השגיאה UnsupportedOperationException. כדי למנוע ממשתמשים להתקין אפליקציות לא מוכרות באמצעות מקורות לא מוכרים, צריך להחיל במקום זאת את הגבלת המשתמש DISALLOW_INSTALL_UNKNOWN_SOURCES.
  • הגבלת המשתמש DISALLOW_INSTALL_UNKNOWN_SOURCES מופעלת באופן אוטומטי בפרופילים מנוהלים שנוצרים במכשירים עם מערכת הפעלה Android מגרסה 8.0 (API ברמה 26). בפרופילים מנוהלים קיימים במכשירים ששודרגו ל-Android 8.0, ההגבלה על המשתמש DISALLOW_INSTALL_UNKNOWN_SOURCES מופעלת באופן אוטומטי, אלא אם הבעלים של הפרופיל השבית את ההגבלה הזו באופן מפורש (לפני השדרוג) על ידי הגדרת הערך INSTALL_NON_MARKET_APPS ל-1.

פרטים נוספים על התקנת אפליקציות לא מוכרות זמינים במדריך הרשאות התקנה של אפליקציות לא מוכרות.

הנחיות נוספות לשיפור האבטחה באפליקציה זמינות במאמר אבטחה למפתחי Android.

פרטיות

בגרסה 8.0 של Android (רמת API‏ 26) בוצעו בפלטפורמה השינויים הבאים שקשורים לפרטיות.

  • הפלטפורמה מטפלת עכשיו במזהים באופן שונה.
    • באפליקציות שהותקנו לפני עדכון OTA לגרסה של Android 8.0‏ (רמת API‏ 26), הערך של ANDROID_ID נשאר ללא שינוי, אלא אם האפליקציה הוסרה ולאחר מכן הותקנה מחדש אחרי העדכון OTA. כדי לשמור את הערכים אחרי התקנות מחדש לאחר עדכון OTA, המפתחים יכולים לשייך את הערכים הישנים והחדשים באמצעות גיבוי של מפתח/ערך.
    • באפליקציות שמותקנות במכשיר עם Android מגרסה 8.0, הערך של ANDROID_ID מוגדר עכשיו לכל מפתח חתימה של אפליקציה ולכל משתמש. הערך של ANDROID_ID הוא ייחודי לכל שילוב של מפתח לחתימה על אפליקציות, משתמש ומכשיר. כתוצאה מכך, אפליקציות עם מפתחות חתימה שונים שפועלות באותו מכשיר כבר לא רואות את אותו מזהה Android (גם אם מדובר באותו משתמש).
    • הערך של ANDROID_ID לא משתנה בהסרה או בהתקנה מחדש של החבילה, כל עוד מפתח החתימה זהה (והאפליקציה לא הותקנה לפני OTA לגרסת Android 8.0).
    • הערך של ANDROID_ID לא משתנה גם אם עדכון מערכת גורם למפתח החתימה על החבילה להשתנות.
    • במכשירים שכוללים את Google Play Services ואת מזהה הפרסום, חייבים להשתמש ב מזהה הפרסום. מערכת פשוטה וסטנדרטית למונטיזציה של אפליקציות. מזהה הפרסום הוא מזהה ייחודי שניתן לאיפוס על ידי המשתמש ומשמש לצורך פרסום. הוא מסופק על ידי Google Play Services.

      יצרני מכשירים אחרים אמורים להמשיך לספק את ANDROID_ID.

  • שאילתה על נכס המערכת net.hostname מניבה תוצאה null.

רישום ביומן של חריגים שלא זוהו

אם אפליקציה מתקינה Thread.UncaughtExceptionHandler שלא מבצע קריאה ל-Thread.UncaughtExceptionHandler שמוגדר כברירת מחדל, המערכת לא תהרוג את האפליקציה אם מתרחשת חריגה שלא תפסה. החל מגרסה Android 8.0‏ (רמת API‏ 26), המערכת מתעדת ביומן את סטאק-טראק החריגה במצב כזה. בגרסאות קודמות של הפלטפורמה, המערכת לא הייתה מתעדת ביומן את סטאק-טראק החריגה.

מומלץ שתמיד תתבצע קריאה של הטמעות בהתאמה אישית של Thread.UncaughtExceptionHandler למטפל ברירת המחדל. אפליקציות שתואמות להמלצה הזו לא מושפעות מהשינוי ב-Android 8.0.

שינוי בחתימה של findViewById()

כל המופעים של השיטה findViewById() מחזירים עכשיו את הערך <T extends View> T במקום View. השינוי הזה משפיע על הדברים הבאים:

  • כתוצאה מכך, יכול להיות שבקוד הקיים יהיה עכשיו סוג החזרה לא ברור. לדוגמה, אם יש גם someMethod(View) וגם someMethod(TextView) שמקבל את התוצאה של הקריאה findViewById().
  • כשמשתמשים בשפת המקור Java 8, צריך לבצע הטמעה מפורשת ל-View כשסוג ההחזרה לא מוגבל (לדוגמה, assertNotNull(findViewById(...)).someViewMethod())).
  • כדי לעדכן את סוג ההחזרה של שיטות findViewById() לא סופיות (למשל, Activity.findViewById()), צריך לבצע החלפה.

שינוי בנתוני השימוש של ספק אנשי הקשר

בגרסאות קודמות של Android, הרכיב של ספק אנשי הקשר מאפשר למפתחים לקבל נתוני שימוש לגבי כל איש קשר. נתוני השימוש האלה חושפים מידע על כל כתובת אימייל ועל כל מספר טלפון שמשויכים לאיש קשר, כולל מספר הפעמים שבהן יצרתם קשר עם איש הקשר והפעם האחרונה שבה יצרתם קשר עם איש הקשר. אפליקציות שמבקשות את ההרשאה READ_CONTACTS יכולות לקרוא את הנתונים האלה.

אפליקציות עדיין יכולות לקרוא את הנתונים האלה אם הן מבקשות את ההרשאה READ_CONTACTS. בגרסה 8.0 של Android (רמת API‏ 26) ואילך, שאילתות לגבי נתוני שימוש מחזירות הערכות גסות במקום ערכים מדויקים. מערכת Android שומרת את הערכים המדויקים באופן פנימי, כך שהשינוי הזה לא משפיע על ממשק ה-API להשלמה אוטומטית.

שינוי ההתנהגות הזה משפיע על הפרמטרים הבאים של השאילתות:

טיפול באוספים

הפונקציות AbstractCollection.removeAll() ו-AbstractCollection.retainAll() תמיד גורמות להשלכת NullPointerException. בעבר, ההשלכה של NullPointerException לא התרחשה כשהאוסף היה ריק. השינוי הזה מביא את ההתנהגות לידי הסכמה עם המסמכים.

Android לארגונים

ב-Android 8.0 (רמת API‏ 26) השתנה ההתנהגות של ממשקי API מסוימים ותכונות מסוימות באפליקציות ארגוניות, כולל בקרי מדיניות המכשיר (DPC). השינויים כוללים:

  • התנהגויות חדשות שיעזרו לאפליקציות לתמוך בפרופילים של עבודה במכשירים מנוהלים באופן מלא.
  • שינויים בטיפול בעדכוני מערכת, באימות אפליקציות ובאימות, כדי לשפר את תקינות המכשיר והמערכת.
  • שיפורים בחוויית המשתמש להקצאת הרשאות ידנית, להתראות, למסך 'אחרונים' ול-VPN שפועל כל הזמן.

במאמר Android בארגון מפורטים כל השינויים לארגונים ב-Android 8.0 (רמת API 26), ומוסבר איך הם עשויים להשפיע על האפליקציה שלכם.

אפליקציות שמטרגטות את Android 8.0

השינויים האלה בהתנהגות חלים רק על אפליקציות שמטרגטות ל-Android 8.0 (רמת API 26) ואילך. אפליקציות שמתאימות ל-Android 8.0 או שמגדירות את targetSdkVersion ל-Android 8.0 ואילך צריכות לשנות את האפליקציות שלהן כדי לתמוך בהתנהגויות האלה בצורה תקינה, במקרים הרלוונטיים.

חלונות התראות

אפליקציות שמשתמשות בהרשאה SYSTEM_ALERT_WINDOW כבר לא יכולות להשתמש בסוגי החלונות הבאים כדי להציג חלונות התראה מעל אפליקציות אחרות וחלונות מערכת:

במקום זאת, האפליקציות צריכות להשתמש בסוג חלון חדש שנקרא TYPE_APPLICATION_OVERLAY.

כשמשתמשים בסוג החלון TYPE_APPLICATION_OVERLAY כדי להציג חלונות התראה באפליקציה, חשוב לזכור את המאפיינים הבאים של סוג החלון החדש:

  • חלונות ההתראות של האפליקציה תמיד מופיעים מתחת לחלונות קריטיים של המערכת, כמו שורת הסטטוס וממשקי IME.
  • המערכת יכולה להזיז חלונות או לשנות את הגודל שלהם, אם הם משתמשים בסוג החלון TYPE_APPLICATION_OVERLAY, כדי לשפר את התצוגה במסך.
  • כשפותחים את חלונית ההתראות, המשתמשים יכולים לגשת להגדרות כדי למנוע מאפליקציה להציג חלונות התראה שמוצגים באמצעות סוג החלון TYPE_APPLICATION_OVERLAY.

התראות על שינויים בתוכן

ב-Android 8.0 (רמת API‏ 26) יש שינוי בהתנהגות של ContentResolver.notifyChange() ושל registerContentObserver(Uri, boolean, ContentObserver) באפליקציות שמטרגטות את Android 8.0.

מעכשיו, ממשקי ה-API האלה דורשים הגדרה של ContentProvider תקין עבור הרשות בכל כתובות ה-URI. הגדרת ContentProvider תקין עם ההרשאות הרלוונטיות תעזור להגן על האפליקציה מפני שינויים בתוכן מפני אפליקציות זדוניות, ותמנע הדלפה של מידע שעשוי להיות פרטי לאפליקציות זדוניות.

הצגת מצב הריכוז

עכשיו אפשר להתמקד גם באובייקטים View שניתן ללחוץ עליהם כברירת מחדל. אם רוצים שאפשר יהיה ללחוץ על אובייקט View אבל לא להעביר אליו את המיקוד, צריך להגדיר את המאפיין android:focusable לערך false בקובץ ה-XML של הפריסה שמכיל את View, או להעביר את הערך false אל setFocusable() בלוגיקה של ממשק המשתמש של האפליקציה.

התאמה של סוכן משתמש בזיהוי דפדפנים

בגרסאות Android 8.0 (רמת API‏ 26) ואילך נכללת המחרוזת של מזהה ה-build‏ OPR. התאמות מסוימות של דפוסים עלולות לגרום ללוגיקה של זיהוי הדפדפן לזהות בטעות דפדפן שאינו Opera בתור Opera. דוגמה להתאמת תבניות כזו יכולה להיות:

if(p.match(/OPR/)){k="Opera";c=p.match(/OPR\/(\d+.\d+)/);n=new Ext.Version(c[1])}

כדי למנוע בעיות שנובעות מזיהוי שגוי כזה, צריך להשתמש במחרוזת שאינה OPR כתוצאה של התאמה לדפוס בדפדפן Opera.

אבטחה

השינויים הבאים משפיעים על האבטחה ב-Android 8.0 (רמת API‏ 26):

  • אם בתצורה של אבטחת הרשת של האפליקציה לא מופיעה תמיכה בתנועה ללא הצפנה, לאובייקטים WebView של האפליקציה אין גישה לאתרים דרך HTTP. כל אובייקט WebView חייב להשתמש ב-HTTPS.
  • הגדרת המערכת התרת מקורות לא ידועים הוסרה. במקום זאת, ההרשאה התקנת אפליקציות לא מוכרות מנהלת את ההתקנות של אפליקציות לא מוכרות ממקורות לא ידועים. מידע נוסף על ההרשאה החדשה הזו זמין במדריך הרשאות להתקנת אפליקציות לא מוכרות.

הנחיות נוספות לשיפור האבטחה באפליקציה זמינות במאמר אבטחה למפתחי Android.

גישה לחשבון וחשיפה

ב-Android 8.0 (רמת API 26), אפליקציות לא יכולות יותר לקבל גישה לחשבונות משתמשים, אלא אם לאימות החשבונות יש בעלות על החשבונות או שהמשתמש מעניק את הגישה הזו. ההרשאה GET_ACCOUNTS כבר לא מספיקה. כדי לקבל גישה לחשבון, אפליקציות צריכות להשתמש ב-AccountManager.newChooseAccountIntent() או בשיטה ספציפית למאמת החשבונות. אחרי קבלת גישה לחשבונות, אפליקציה יכולה להפעיל את AccountManager.getAccounts() כדי לגשת אליהם.

ב-Android 8.0 הופסק השימוש ב-LOGIN_ACCOUNTS_CHANGED_ACTION. במקום זאת, אפליקציות צריכות להשתמש ב-addOnAccountsUpdatedListener() כדי לקבל עדכונים על חשבונות במהלך זמן הריצה.

מידע על ממשקי API ושיטות חדשים שנוספו לגישה לחשבון ולחשיפה שלו זמין בקטע גישה לחשבון וחשיפה בקטע 'ממשקי API חדשים' במסמך הזה.

פרטיות

השינויים הבאים משפיעים על הפרטיות ב-Android 8.0 (רמת API‏ 26).

  • מאפייני המערכת net.dns1, net.dns2, net.dns3 ו-net.dns4 כבר לא זמינים – שינוי שמשפר את הפרטיות בפלטפורמה.
  • כדי לקבל מידע על רשתות, כמו שרתי DNS, אפליקציות עם ההרשאה ACCESS_NETWORK_STATE יכולות לרשום אובייקט NetworkRequest או NetworkCallback. הכיתות האלה זמינות ב-Android מגרסה 5.0 (רמת API‏ 21) ואילך.
  • Build.SERIAL הוצא משימוש. אפליקציות שצריכות לדעת את המספר הסידורי של החומרה צריכות להשתמש במקום זאת בשיטה החדשה Build.getSerial(), שמחייבת את ההרשאה READ_PHONE_STATE.
  • ה-API של LauncherApps לא מאפשר יותר לאפליקציות בפרופיל העבודה לקבל מידע על הפרופיל הראשי. כשמשתמש נמצא בפרופיל עבודה, ה-API של LauncherApps פועל כאילו לא מותקנות אפליקציות בפרופילים אחרים באותה קבוצת פרופילים. כמו קודם, ניסיונות לגשת לפרופילים לא קשורים גורמים ל-Security חריגים.

הרשאות

לפני Android 8.0 (רמת API‏ 26), אם אפליקציה ביקשה הרשאה במהלך זמן הריצה וההרשאה אושרה, המערכת גם העניקה לאפליקציה בטעות את שאר ההרשאות שהשתייכו לאותה קבוצת הרשאות ונרשמו במניפסט.

באפליקציות שמטרגטות את Android 8.0, הבעיה הזו תוקנה. האפליקציה תקבל רק את ההרשאות שביקשה באופן מפורש. עם זאת, אחרי שהמשתמש מעניק הרשאה לאפליקציה, כל הבקשות הבאות להרשאות בקבוצת ההרשאות הזו יתקבלו באופן אוטומטי.

לדוגמה, נניח שאפליקציה מפרטת את READ_EXTERNAL_STORAGE וגם את WRITE_EXTERNAL_STORAGE במניפסט שלה. האפליקציה מבקשת את READ_EXTERNAL_STORAGE והמשתמש נתן את ההרשאה הזו. אם האפליקציה מטרגטת לרמת API 25 או נמוכה יותר, המערכת מעניקה גם את ההרשאה WRITE_EXTERNAL_STORAGE בו-זמנית, כי היא שייכת לאותה קבוצת הרשאות STORAGE ורשומה גם במניפסט. אם האפליקציה מטרגטת את Android 8.0 (רמת API 26), המערכת מעניקה רק את ההרשאה READ_EXTERNAL_STORAGE באותו זמן. עם זאת, אם האפליקציה מבקשת מאוחר יותר את ההרשאה WRITE_EXTERNAL_STORAGE, המערכת מעניקה את ההרשאה הזו באופן מיידי בלי להציג בקשה למשתמש.

מדיה

  • המסגרת יכולה לבצע בעצמה השתקה אוטומטית של אודיו. במקרה כזה, כשאפליקציה אחרת מבקשת להתמקד באמצעות AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, האפליקציה שמקבלת את המיקוד מפחיתה את עוצמת הקול שלה, אבל בדרך כלל לא מקבלת קריאה חוזרת (callback) עם onAudioFocusChange() ולא מאבדת את המיקוד האודיו. יש ממשקי API חדשים שאפשר להשתמש בהם כדי לשנות את ההתנהגות הזו באפליקציות שצריכות להשהות את האודיו במקום להנמיע אותו.
  • כשהמשתמש מבצע שיחת טלפון, שידורי המדיה הפעילים מושתקים במהלך השיחה.
  • בכל ממשקי ה-API שקשורים לאודיו, צריך להשתמש ב-AudioAttributes במקום בסוגי סטרימינג של אודיו כדי לתאר את תרחיש לדוגמה של הפעלת אודיו. ממשיכים להשתמש בסוגי שידורי אודיו לצורך בקרת עוצמת קול בלבד. שימושים אחרים בסוגים של זרמים עדיין פועלים (לדוגמה, הארגומנט streamType ל-constructor AudioTrack שהוצא משימוש), אבל המערכת מתעדת את הפעולה הזו כשגיאה.
  • כשמשתמשים ב-AudioTrack, אם האפליקציה מבקשת מאגר אודיו גדול מספיק, המסגרת תנסה להשתמש בפלט של מאגר הנתונים הזמני העמוק אם הוא זמין.
  • ב-Android 8.0 (רמת API‏ 26), הטיפול באירועים של לחצני מדיה שונה:
    1. אופן הטיפול בלחצני המדיה בפעילות בממשק המשתמש לא השתנה: פעילויות בחזית עדיין מקבלות עדיפות בטיפול באירועים של לחצני מדיה.
    2. אם הפעילות בחזית לא מטפלת באירוע של לחצן המדיה, המערכת מפנה את האירוע לאפליקציה האחרונה שהפעילה אודיו באופן מקומי. הסטטוס הפעיל, הסימונים ומצב ההפעלה של סשן מדיה לא מובאים בחשבון כדי לקבוע איזו אפליקציה מקבלת אירועים של לחצני מדיה.
    3. אם שוחרר מהסשן במדיה של האפליקציה, המערכת תשלח את האירוע של לחצן המדיה אל MediaButtonReceiver של האפליקציה, אם יש כזו.
    4. בכל מקרה אחר, המערכת משליכה את האירוע של לחצן המדיה.

ספריות מקוריות

באפליקציות שמטרגטות ל-Android 8.0 (רמת API 26), ספריות מקוריות לא נטענות יותר אם הן מכילות פלח טעינה שניתן לכתיבה ולהפעלה. יכול להיות שחלק מהאפליקציות יפסיקו לפעול בעקבות השינוי אם יש בהן ספריות מקוריות עם מקטעים שגויים של טעינה. זהו אמצעי לחיזוק האבטחה.

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

השינויים ב-Linker קשורים לרמת ה-API שאליה האפליקציה מטרגטת. אם יש שינוי במנגנון לקישור ברמת ה-API המטורגט, האפליקציה לא תוכל לטעון את הספרייה. אם מטרגטים לרמת API נמוכה יותר מרמת ה-API שבה מתרחש השינוי ב-linker, תוצג אזהרה ב-logcat.

טיפול באוסף

ב-Android 8.0 (רמת API 26), Collections.sort() מיושם מעל List.sort(). המצב ההפוך היה נכון ב-Android 7.x (רמות API 24 ו-25): ההטמעה שמוגדרת כברירת מחדל של List.sort() נקראת Collections.sort().

השינוי הזה מאפשר ל-Collections.sort() לנצל את היתרונות של הטמעות List.sort() שהותאמו אופטימלית, אבל יש לו את האילוצים הבאים:

  • בהטמעות של הפונקציה List.sort() אסור לקרוא לפונקציה Collections.sort(), כי פעולה כזו עלולה לגרום גלישת מחסנית בגלל חזרה אינסופית. במקום זאת, אם רוצים את התנהגות ברירת המחדל בהטמעה של List, צריך להימנע משינויים שרלוונטיים ל-sort().

    אם מחלקת הורה מיישמת את sort() בצורה לא הולמת, בדרך כלל אפשר לשנות את List.sort() באמצעות הטמעה שמבוססת על List.toArray(),‏ Arrays.sort() ו-ListIterator.set(). לדוגמה:

    @Override
    public void sort(Comparator<? super E> c) {
      Object[] elements = toArray();
      Arrays.sort(elements, c);
      ListIterator<E> iterator = (ListIterator<Object>) listIterator();
      for (Object element : elements) {
        iterator.next();
        iterator.set((E) element);
      }
    }

    ברוב המקרים, אפשר גם לשנות את הערך של List.sort() באמצעות הטמעה שמעבירה את הגדרת ברירת המחדל להטמעות שונות בהתאם לרמת ה-API. לדוגמה:

    @Override
    public void sort(Comparator<? super E> comparator) {
      if (Build.VERSION.SDK_INT <= 25) {
        Collections.sort(this);
      } else {
        super.sort(comparator);
      }
    }

    אם אתם עושים את הפעולה האחרונה רק כי אתם רוצים ששיטה sort() תהיה זמינה בכל רמות ה-API, כדאי לתת לה שם ייחודי, כמו sortCompat(), במקום לשנות את sort().

  • Collections.sort() נחשב עכשיו לשינוי מבני בהטמעות של רשימות שמפעילות את sort(). לדוגמה, בגרסאות של הפלטפורמה שקדמו ל-Android 8.0 (רמת API‏ 26), אם היינו מבצעים חזרה על ArrayList ומפעילים עליו את sort() באמצע החזרה, המערכת הייתה זורקת ConcurrentModificationException אם המיון בוצע על ידי קריאה ל-List.sort(). Collections.sort() לא החזירה חריג.

    השינוי הזה מאפשר לפלטפורמה לפעול בצורה עקבית יותר: כל אחת מהגישות האלה מובילה עכשיו ל-ConcurrentModificationException.

ההתנהגות של טעינת הכיתה

מערכת Android 8.0 (רמת API 26) בודקת כדי לוודא שמטעמי הכיתות לא מפירים את ההנחות לגבי סביבת זמן הריצה כשטוענים מחלקות חדשות. הבדיקות האלה מתבצעות גם אם יש הפניה לכיתה מ-Java‏ (מ-forName()), מ-Dalvik bytecode או מ-JNI. הפלטפורמה לא מפריעה לשיחות ישירות מ-Java ל-method‏ loadClass(), ולא בודקת את התוצאות של שיחות כאלה. ההתנהגות הזו לא אמורה להשפיע על הפעולה של מערכי טעינה של כיתות שמתנהגים בצורה תקינה.

הפלטפורמה בודקת שהתיאורים של הכיתה שהמעבד יוצר תואמים לתיאורים הצפויים. אם המתאר המוחזר לא תואם, הפלטפורמה תשליך שגיאה מסוג NoClassDefFoundError ותשמור בהודעה מפורטת לגבי אי ההתאמה בחריגה.

הפלטפורמה גם בודקת שהתיאורים של הכיתות המבוקשות תקינים. הבדיקה הזו מזהה קריאות ל-JNI שגורמות לטעינת כיתות באופן עקיף, כמו GetFieldID(), ומעבירות לכיתות האלה מתארים לא חוקיים. לדוגמה, שדה עם החתימה java/lang/String לא נמצא כי החתימה הזו לא חוקית. צריך להשתמש בחתימה Ljava/lang/String;.

הקריאה הזו שונה מקריאה ל-JNI ל-FindClass(), שבה java/lang/String הוא שם תקין שמוגדר במלואו.

ב-Android 8.0 (רמת API‏ 26) אין תמיכה במספר מערכי טעינה של כיתות שמנסים להגדיר כיתות באמצעות אותו אובייקט DexFile. ניסיון לעשות זאת יגרום לזמן הריצה של Android להוציא שגיאת InternalError עם ההודעה "Attempt to register dex file <filename> with multiple class loaders".

DexFile API הוצא משימוש, ומומלץ מאוד להשתמש במקום זאת באחד ממטעני הכיתות של הפלטפורמה, כולל PathClassLoader או BaseDexClassLoader.

הערה: אפשר ליצור כמה מעמיסי הכיתות שמפנים לאותו מאגר של קובץ APK או JAR ממערכת הקבצים. בדרך כלל, הפעולה הזו לא גורמת לשימוש רב בזיכרון: אם קובצי ה-DEX בקונטיינר מאוחסנים במקום להיות דחוסים, הפלטפורמה יכולה לבצע עליהם פעולת mmap במקום לחלץ אותם ישירות. עם זאת, אם הפלטפורמה צריכה לחלץ את קובץ ה-DEX מהקונטיינר, הפנייה לקובץ DEX באופן הזה עשויה לצרוך הרבה זיכרון.

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

זהירות: בגרסאות של הפלטפורמה שקודמות לגרסה Android 8.0 (רמת API‏ 26), הפרת ההנחות האלה עלולה להוביל להגדרה של אותה כיתה כמה פעמים, לזיהום אשכול עקב בלבול בין כיתות ולתוצאות לא רצויות אחרות.