שינויים בהתנהגות: אפליקציות שמטרגטות את Android 15 ואילך

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

חשוב גם לבדוק את רשימת השינויים בהתנהגות שמשפיעים על כל האפליקציות שפועלות ב-Android 15, ללא קשר ל-targetSdkVersion של האפליקציה.

פונקציונליות עיקרית

ב-Android 15 יש שינויים או הרחבות של יכולות ליבה שונות במערכת Android.

שינויים בשירותים שפועלים בחזית

我们将在 Android 15 中对前台服务进行以下更改。

数据同步前台服务超时行为

Android 15 introduces a new timeout behavior to dataSync for apps targeting Android 15 (API level 35) or higher. This behavior also applies to the new mediaProcessing foreground service type.

The system permits an app's dataSync services to run for a total of 6 hours in a 24-hour period, after which the system calls the running service's Service.onTimeout(int, int) method (introduced in Android 15). At this time, the service has a few seconds to call Service.stopSelf(). When Service.onTimeout() is called, the service is no longer considered a foreground service. If the service does not call Service.stopSelf(), the system throws an internal exception. The exception is logged in Logcat with the following message:

Fatal Exception: android.app.RemoteServiceException: "A foreground service of
type dataSync did not stop within its timeout: [component name]"

To avoid problems with this behavior change, you can do one or more of the following:

  1. Have your service implement the new Service.onTimeout(int, int) method. When your app receives the callback, make sure to call stopSelf() within a few seconds. (If you don't stop the app right away, the system generates a failure.)
  2. Make sure your app's dataSync services don't run for more than a total of 6 hours in any 24-hour period (unless the user interacts with the app, resetting the timer).
  3. Only start dataSync foreground services as a result of direct user interaction; since your app is in the foreground when the service starts, your service has the full six hours after the app goes to the background.
  4. Instead of using a dataSync foreground service, use an alternative API.

If your app's dataSync foreground services have run for 6 hours in the last 24, you cannot start another dataSync foreground service unless the user has brought your app to the foreground (which resets the timer). If you try to start another dataSync foreground service, the system throws ForegroundServiceStartNotAllowedException with an error message like "Time limit already exhausted for foreground service type dataSync".

Testing

To test your app's behavior, you can enable data sync timeouts even if your app is not targeting Android 15 (as long as the app is running on an Android 15 device). To enable timeouts, run the following adb command:

adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name

You can also adjust the timeout period, to make it easier to test how your app behaves when the limit is reached. To set a new timeout period, run the following adb command:

adb shell device_config put activity_manager data_sync_fgs_timeout_duration duration-in-milliseconds

新建媒体处理前台服务类型

Android 15 引入了一种新的前台服务类型 mediaProcessing。此服务类型适用于转码媒体文件等操作。例如,媒体应用可能会下载音频文件,并需要先将其转换为其他格式,然后才能播放。您可以使用 mediaProcessing 前台服务,确保即使应用在后台运行时转换也会继续。

系统允许应用的 mediaProcessing 服务在 24 小时内总共运行 6 小时,之后系统会调用正在运行的服务的 Service.onTimeout(int, int) 方法(在 Android 15 中引入)。此时,服务有几秒钟的时间来调用 Service.stopSelf()。如果服务未调用 Service.stopSelf(),系统会抛出内部异常。系统会在 Logcat 中记录此异常,并显示以下消息:

Fatal Exception: android.app.RemoteServiceException: "A foreground service of
type mediaProcessing did not stop within its timeout: [component name]"

为避免出现此异常,您可以执行以下任一操作:

  1. 让您的服务实现新的 Service.onTimeout(int, int) 方法。当您的应用收到回调时,请务必在几秒钟内调用 stopSelf()。(如果您未立即停止应用,系统会生成失败情况。)
  2. 确保应用的 mediaProcessing 服务在任何 24 小时内总运行时间不超过 6 小时(除非用户与应用互动,重置计时器)。
  3. 仅在有直接用户互动时启动 mediaProcessing 前台服务;由于服务启动时应用位于前台,因此您的服务在应用进入后台后有完整的 6 小时时间。
  4. 请改用 替代 API(例如 WorkManager),而不是使用 mediaProcessing 前台服务。

如果您的应用的 mediaProcessing 前台服务在过去 24 小时内运行了 6 小时,则您无法启动其他 mediaProcessing 前台服务,除非用户将您的应用切换到前台(这会重置计时器)。如果您尝试启动另一个 mediaProcessing 前台服务,系统会抛出 ForegroundServiceStartNotAllowedException,并显示类似于“前台服务类型 mediaProcessing 的时间限制已用尽”的错误消息。

如需详细了解 mediaProcessing 服务类型,请参阅 Android 15 前台服务类型变更:媒体处理

测试

如需测试应用的行为,您可以启用媒体处理超时,即使您的应用并非以 Android 15 为目标平台也是如此(前提是应用在 Android 15 设备上运行)。如需启用超时,请运行以下 adb 命令:

adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name

您还可以调整超时期限,以便更轻松地测试应用在达到上限时的行为方式。如需设置新的超时期限,请运行以下 adb 命令:

adb shell device_config put activity_manager media_processing_fgs_timeout_duration duration-in-milliseconds

对启动前台服务的 BOOT_COMPLETED 广播接收器实施限制

יש הגבלות חדשות על השקת מקלטי שידור של BOOT_COMPLETED שירותים שפועלים בחזית. למקלטי BOOT_COMPLETED אסור להפעיל את הסוגים הבאים של שירותים שפועלים בחזית:

אם מקלט BOOT_COMPLETED מנסה להפעיל אחד מהסוגים האלה של חזית השירותים האלה, המערכת מטילה ForegroundServiceStartNotAllowedException.

בדיקה

כדי לבדוק את התנהגות האפליקציה, אפשר להפעיל את ההגבלות החדשות האלה גם אם האפליקציה לא מטרגטת ל-Android 15 (כל עוד האפליקציה פועלת עם Android 15). במכשיר). מריצים את הפקודה adb הבאה:

adb shell am compat enable FGS_BOOT_COMPLETED_RESTRICTIONS your-package-name

כדי לשלוח שידור של BOOT_COMPLETED בלי להפעיל מחדש את המכשיר: מריצים את הפקודה הבאה של adb:

adb shell am broadcast -a android.intent.action.BOOT_COMPLETED your-package-name

限制在应用拥有 SYSTEM_ALERT_WINDOW 权限时启动前台服务

以前,如果应用拥有 SYSTEM_ALERT_WINDOW 权限,即使应用当前在后台运行,也可以启动前台服务(如免于后台启动限制中所述)。

如果应用以 Android 15 为目标平台,则此豁免范围现在更窄。现在,应用需要具有 SYSTEM_ALERT_WINDOW 权限,并且需要有一个可见的叠加窗口。也就是说,应用需要先启动 TYPE_APPLICATION_OVERLAY 窗口,并且该窗口需要处于可见状态,然后您才能启动前台服务。

如果您的应用尝试从后台启动前台服务,但不符合这些新要求(并且没有其他豁免情况),系统会抛出 ForegroundServiceStartNotAllowedException

如果您的应用声明了 SYSTEM_ALERT_WINDOW 权限并从后台启动前台服务,则可能会受到此变更的影响。如果您的应用获得了 ForegroundServiceStartNotAllowedException,请检查应用的操作顺序,并确保应用在尝试从后台启动前台服务之前已具有有效的叠加层窗口。您可以通过调用 View.getWindowVisibility() 检查叠加层窗口当前是否可见,也可以替换 View.onWindowVisibilityChanged(),以便在可见性发生变化时收到通知。

测试

如需测试应用的行为,您可以启用这些新限制,即使您的应用并未以 Android 15 为目标平台(只要应用在 Android 15 设备上运行)也是如此。如需针对从后台启动前台服务启用这些新限制,请运行以下 adb 命令:

adb shell am compat enable FGS_SAW_RESTRICTIONS your-package-name

שינויים בזמנים שבהם אפליקציות יכולות לשנות את המצב הגלובלי של מצב 'נא לא להפריע'

以 Android 15 为目标平台的应用无法再更改设备上的全局状态或勿扰 (DND) 政策(通过修改用户设置或关闭 DND 模式)。相反,应用必须提供一个 AutomaticZenRule,系统会将后者合并到一个具有现有最严格的政策胜出方案的全局政策中。调用之前影响全局状态的现有 API(setInterruptionFiltersetNotificationPolicy)会导致创建或更新隐式 AutomaticZenRule,该 AutomaticZenRule 根据这些 API 调用的调用周期而开启或关闭。

请注意,只有在应用调用 setInterruptionFilter(INTERRUPTION_FILTER_ALL) 且预期调用会停用之前由其所有者激活的 AutomaticZenRule 时,此变更才会影响可观察的行为。

שינויים ב-OpenJDK API

ב-Android 15 ממשיכים בתהליך הרענון של ספריות הליבה של Android כדי להתאים אותן לתכונות בגרסאות ה-LTS האחרונות של OpenJDK.

חלק מהשינויים האלה עשויים להשפיע על תאימות האפליקציות לאפליקציות שמטרגטות ל-Android 15 (רמת API 35):

  • שינויים בממשקי ה-API לפורמט מחרוזות: האימות של מדד הארגומנט, הדגלים, הרוחב והדיוק מחמיר עכשיו כשמשתמשים בממשקי ה-API הבאים של String.format() ו-Formatter.format():

    לדוגמה, חריגה מהסוג הבא מתרחשת כשמשתמשים במדד ארגומנט של 0 (%0 במחרוזת הפורמט):

    IllegalFormatArgumentIndexException: Illegal format argument index = 0
    

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

  • שינויים בסוג הרכיב של Arrays.asList(...).toArray(): כשמשתמשים ב-Arrays.asList(...).toArray(), סוג הרכיב של המערך שנוצר הוא עכשיו Object – ולא הסוג של הרכיבים של המערך הבסיסי. לכן הקוד הבא יגרום להשלכת ClassCastException:

    String[] elements = (String[]) Arrays.asList("one", "two").toArray();
    

    במקרה כזה, כדי לשמור את String כסוג הרכיב במערך שנוצר, אפשר להשתמש ב-Collection.toArray(Object[]) במקום זאת:

    String[] elements = Arrays.asList("two", "one").toArray(new String[0]);
    
  • שינויים בטיפול בקוד השפה: כשמשתמשים ב-API של Locale, קודי השפה של עברית, יידיש ואינדונזית כבר לא מומרים לצורות הקודמות שלהם (עברית: iw, יידיש: ji, אינדונזית: in). כשמציינים את קוד השפה של אחת מהשפות האלה, צריך להשתמש במקום זאת בקודים מ-ISO 639-1 (עברית: he, יידיש: yi, אינדונזית: id).

  • שינויים ברצפים אקראיים של מספרים שלמים: בעקבות השינויים שבוצעו ב-https://bugs.openjdk.org/browse/JDK-8301574, השיטות הבאות של Random.ints() מחזירות עכשיו רצף מספרים שונה מזה שמחזירות השיטות של Random.nextInt():

    באופן כללי, השינוי הזה לא אמור לגרום להתנהגות שגורמת לכשל באפליקציה, אבל הקוד לא צריך לצפות שהרצף שנוצר מהשיטות של Random.ints() יתאים ל-Random.nextInt().

ממשק ה-API החדש SequencedCollection יכול להשפיע על תאימות האפליקציה אחרי שתעדכנו את compileSdk בתצורת ה-build של האפליקציה כך שישתמש ב-Android 15 (רמת API 35):

  • התנגשות עם פונקציות התוסף MutableList.removeFirst() ו-MutableList.removeLast() ב-kotlin-stdlib

    הסוג List ב-Java ממופה לסוג MutableList ב-Kotlin. מאחר שממשקי ה-API List.removeFirst() ו-List.removeLast() הוצגו ב-Android 15 (רמת API 35), המהדר של Kotlin פותר קריאות פונקציה, למשל list.removeFirst(), באופן סטטי לממשקי ה-API החדשים של List במקום לפונקציות ההרחבה ב-kotlin-stdlib.

    אם האפליקציה עוברת הידור מחדש עם הערך compileSdk מוגדר ל-35 והערך minSdk מוגדר ל-34 או לגרסה ישנה יותר, ולאחר מכן האפליקציה מופעלת ב-Android 14 וגרסאות קודמות, מתרחשת שגיאה בסביבת זמן הריצה:

    java.lang.NoSuchMethodError: No virtual method
    removeFirst()Ljava/lang/Object; in class Ljava/util/ArrayList;
    

    אפשרות ה-lint הקיימת NewApi בפלאגין של Android Gradle יכולה לזהות את שימושי ה-API החדשים האלה.

    ./gradlew lint
    
    MainActivity.kt:41: Error: Call requires API level 35 (current min is 34): java.util.List#removeFirst [NewApi]
          list.removeFirst()
    

    כדי לתקן את חריגת זמן הריצה ואת שגיאות ה-lint, אפשר להחליף את הקריאות לפונקציות removeFirst() ו-removeLast() ב-Kotlin בקריאות לפונקציות removeAt(0) ו-removeAt(list.lastIndex), בהתאמה. אם אתם משתמשים ב-Android Studio Ladybug | 2024.1.3 ואילך, יש גם אפשרות לתיקון מהיר של השגיאות האלה.

    אם אפשרות האיתור של שגיאות בקוד מושבתת, כדאי להסיר את @SuppressLint("NewApi") ואת lintOptions { disable 'NewApi' }.

  • התנגשות עם שיטות אחרות ב-Java

    נוספו שיטות חדשות לסוגי הנתונים הקיימים, למשל List ו-Deque. יכול להיות שהשיטות החדשות האלה לא יהיו תואמות לשיטות עם אותו שם וסוגים של ארגומנטים בממשקים ובכיתות אחרים. במקרה של התנגשות בחתימת שיטת עם אי-תאימות, המהדר של javac יפיק שגיאה בזמן ה-build. לדוגמה:

    דוגמה לשגיאה 1:

    javac MyList.java
    
    MyList.java:135: error: removeLast() in MyList cannot implement removeLast() in List
      public void removeLast() {
                  ^
      return type void is not compatible with Object
      where E is a type-variable:
        E extends Object declared in interface List
    

    דוגמה לשגיאה 2:

    javac MyList.java
    
    MyList.java:7: error: types Deque<Object> and List<Object> are incompatible;
    public class MyList implements  List<Object>, Deque<Object> {
      both define reversed(), but with unrelated return types
    1 error
    

    דוגמה לשגיאה 3:

    javac MyList.java
    
    MyList.java:43: error: types List<E#1> and MyInterface<E#2> are incompatible;
    public static class MyList implements List<Object>, MyInterface<Object> {
      class MyList inherits unrelated defaults for getFirst() from types List and MyInterface
      where E#1,E#2 are type-variables:
        E#1 extends Object declared in interface List
        E#2 extends Object declared in interface MyInterface
    1 error
    

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

    @Override
    public Object getFirst() {
        return List.super.getFirst();
    }
    

אבטחה

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

השקות של פעילות מאובטחת ברקע

Android 15 protects users from malicious apps and gives them more control over their devices by adding changes that prevent malicious background apps from bringing other apps to the foreground, elevating their privileges, and abusing user interaction. Background activity launches have been restricted since Android 10 (API level 29).

Other changes

In addition to the restriction for UID matching, these other changes are also included:

  • Change PendingIntent creators to block background activity launches by default. This helps prevent apps from accidentally creating a PendingIntent that could be abused by malicious actors.
  • Don't bring an app to the foreground unless the PendingIntent sender allows it. This change aims to prevent malicious apps from abusing the ability to start activities in the background. By default, apps are not allowed to bring the task stack to the foreground unless the creator allows background activity launch privileges or the sender has background activity launch privileges.
  • Control how the top activity of a task stack can finish its task. If the top activity finishes a task, Android will go back to whichever task was last active. Moreover, if a non-top activity finishes its task, Android will go back to the home screen; it won't block the finish of this non-top activity.
  • Prevent launching arbitrary activities from other apps into your own task. This change prevents malicious apps from phishing users by creating activities that appear to be from other apps.
  • Block non-visible windows from being considered for background activity launches. This helps prevent malicious apps from abusing background activity launches to display unwanted or malicious content to users.

כוונות רכישה בטוחות יותר

Android 15 引入了新的可选安全措施,以提高 intent 的安全性和稳健性。这些变更旨在防止潜在的漏洞以及恶意应用可能利用的 intent 滥用行为。Android 15 对 intent 的安全性进行了两项主要改进:

  • 与目标 intent 过滤器匹配:定位到特定组件的 intent 必须与目标的 intent 过滤器规范完全匹配。如果您发送 intent 来启动其他应用的 activity,目标 intent 组件需要与接收 activity 声明的 intent 过滤器保持一致。
  • intent 必须具有操作:没有操作的 intent 将不再与任何 intent 过滤器匹配。这意味着,用于启动 activity 或服务的 intent 必须具有明确定义的操作。

如需检查您的应用对这些更改的响应方式,请在应用中使用 StrictMode。如需查看有关 Intent 使用违规行为的详细日志,请添加以下方法:

Kotlin


fun onCreate() {
    StrictMode.setVmPolicy(VmPolicy.Builder()
        .detectUnsafeIntentLaunch()
        .build()
    )
}

Java


public void onCreate() {
    StrictMode.setVmPolicy(new VmPolicy.Builder()
            .detectUnsafeIntentLaunch()
            .build());
}

חוויית משתמש וממשק המשתמש של המערכת

Android 15 כולל כמה שינויים שנועדו ליצור חוויית משתמש עקבית ואינטואיטיבית יותר.

שינויים בהוספה של חלון

Android 15 中有两个与窗口边衬区相关的变更:默认强制执行无边框模式;还存在配置变更,例如系统栏的默认配置。

Edge-to-edge enforcement

אפליקציות מוצגות מקצה לקצה כברירת מחדל במכשירים עם Android 15, אם האפליקציה מטרגטת ל-Android 15 (API ברמה 35).

אפליקציה שמטרגטת את Android 14 ולא תוצג במסך מלא במכשיר Android 15.


אפליקציה שמטרגטת ל-Android 15 (רמת API 35) ומתפרסת מקצה לקצה במכשיר Android 15. האפליקציה הזו משתמשת בעיקר ברכיבי Compose של Material 3 שמחילים באופן אוטומטי רכיבי inset. המסך הזה לא מושפע לרעה מאכיפה מקצה לקצה ב-Android 15.

זהו שינוי שעלול להשפיע לרעה על ממשק המשתמש של האפליקציה. השינויים משפיעים על אזורי ממשק המשתמש הבאים:

  • סרגל הניווט של תנועת האחיזה
    • לא סודי כברירת מחדל.
    • ההזזה למטה מושבתת, כך שהתוכן מוצג מאחורי סרגל הניווט של המערכת, אלא אם מחילים חתימות.
    • setNavigationBarColor ו-R.attr#navigationBarColor הוצאו משימוש ולא משפיעות על הניווט באמצעות תנועות.
    • ל-setNavigationBarContrastEnforced ול-R.attr#navigationBarContrastEnforced אין השפעה על הניווט באמצעות תנועות.
  • ניווט ב-3 לחצנים
    • כברירת מחדל, השקיפות מוגדרת ל-80%, והצבע עשוי להתאים לצבע הרקע של החלון.
    • ההזזה למטה מושבתת, כך שהתוכן מוצג מאחורי סרגל הניווט של המערכת, אלא אם חלים חיתוכים.
    • כברירת מחדל, setNavigationBarColor ו-R.attr#navigationBarColor מוגדרים להתאים לרקע החלון. כדי שברירת המחדל הזו תחול, רקע החלון צריך להיות פריט גרפי וקטורי שניתן לשרטוט בצבע. ממשק ה-API הזה הוצא משימוש, אבל הוא עדיין משפיע על הניווט באמצעות 3 לחצנים.
    • הערכים של setNavigationBarContrastEnforced ו-R.attr#navigationBarContrastEnforced מוגדרים כ-true כברירת מחדל, כך שמתווסף רקע אטום ב-80% בכל התפריטים של ניווט ב-3 לחצנים.
  • שורת סטטוס
    • שקוף כברירת מחדל.
    • ההיסט העליון מושבת, כך שהתוכן מופיע מאחורי שורת הסטטוס, אלא אם מוחלים מקבצים.
    • setStatusBarColor ו-R.attr#statusBarColor הוצאו משימוש ואין להם השפעה על Android 15.
    • setStatusBarContrastEnforced ו-R.attr#statusBarContrastEnforced הוצאו משימוש, אבל עדיין משפיעים על Android 15.
  • חור במסך
    • הערך של layoutInDisplayCutoutMode בחלונות לא צפים חייב להיות LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS. הערכים SHORT_EDGES, ‏ NEVER ו-DEFAULT מפורשים כ-ALWAYS כדי שהמשתמשים לא יראו פס שחור שנגרם כתוצאה מהחור במסך, והם יוצגו מקצה לקצה.

בדוגמה הבאה מוצגת אפליקציה לפני ואחרי הטירגוט ל-Android 15 (רמת API 35), וגם לפני ואחרי החלת אזורי inset.

אפליקציה שמטרגטת את Android 14 ולא מקצה לקצה במכשיר Android 15.
אפליקציה שמטרגטת ל-Android 15 (רמת API 35) ומתפרסת מקצה לקצה במכשיר Android 15. עם זאת, הרבה רכיבים מוסתרים עכשיו על ידי שורת הסטטוס, סרגל הניווט ב-3 לחצנים או המגרעת במסך, בגלל אכיפת האכיפה מקצה לקצה ב-Android 15. ממשק המשתמש המוסתר כולל את סרגל האפליקציה העליון של Material 2, לחצני פעולה צפים ופריטים ברשימה.
אפליקציה שמטרגטת את Android 15 (רמת API 35), היא מקצה לקצה במכשיר Android 15 ומחילה ערכות inset כך שממשק המשתמש לא יוסתר.
מה צריך לבדוק אם האפליקציה כבר מוצגת מקצה לקצה

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

  • יש לכם חלון לא צף, כמו Activity שמשתמש ב-SHORT_EDGES, ב-NEVER או ב-DEFAULT במקום ב-LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS. אם האפליקציה קורסת בזמן ההפעלה, יכול להיות שהסיבה לכך היא מסך הפתיחה. אפשר לשדרג את התלות של core splashscreen ל-1.2.0-alpha01 או לגרסת build מאוחרת יותר, או להגדיר את window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutInDisplayCutoutMode.always.
  • יכול להיות שיהיו מסכים עם תנועה נמוכה יותר עם ממשק משתמש חסום. מוודאים שהמסכים האלה, שבהם יש פחות ביקורים, לא מכסים את ממשק המשתמש. מסכים עם פחות תנועה:
    • מסכי קליטה או כניסה
    • דפי הגדרות
מה צריך לבדוק אם האפליקציה עדיין לא מוצגת מקצה לקצה

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

  • אם האפליקציה שלכם משתמשת ברכיבי Material 3 (androidx.compose.material3) ב-Compose, כמו TopAppBar,‏ BottomAppBar ו-NavigationBar, סביר להניח שהרכיבים האלה לא מושפעים מהשינוי כי הם מטפלים באופן אוטומטי בהכנסות.
  • אם באפליקציה שלכם נעשה שימוש ברכיבי Material 2 ‏(androidx.compose.material) ב-Compose, הרכיבים האלה לא מטפלים באופן אוטומטי בהכנסות. עם זאת, תוכלו לקבל גישה לתמונות הממוזערות ולהחיל אותן באופן ידני. ב-androidx.compose.material 1.6.0 ואילך, משתמשים בפרמטר windowInsets כדי להחיל את הרכיבים הפנימיים באופן ידני עבור BottomAppBar, TopAppBar, BottomNavigation ו-NavigationRail. באופן דומה, צריך להשתמש בפרמטר contentWindowInsets עבור Scaffold.
  • אם באפליקציה שלכם נעשה שימוש בתצוגות וברכיבי Material (com.google.android.material), רוב רכיבי Material שמבוססים על תצוגות, כמו BottomNavigationView,‏ BottomAppBar, ‏ NavigationRailView או NavigationView, מטפלים בהוספת רכיבים לחלק הפנימי של המסך ולא נדרשת עבודה נוספת. עם זאת, צריך להוסיף את android:fitsSystemWindows="true" אם משתמשים ב-AppBarLayout.
  • בתכנים קומפוזביליים בהתאמה אישית, צריך להחיל את הרכיבים הפנימיים באופן ידני בתור מרווח פנימי. אם התוכן שלכם נמצא בתוך Scaffold, תוכלו להשתמש ב-insets באמצעות ערכי המילוי של Scaffold. אחרת, צריך להוסיף את ה-padding באמצעות אחת מהפונקציות WindowInsets.
  • אם האפליקציה שלכם משתמשת בתצוגות ובמאגרי תגים מסוג BottomSheet, ‏ SideSheet או בהתאמה אישית, צריך להוסיף את הרווח באמצעות ViewCompat.setOnApplyWindowInsetsListener. בשביל RecyclerView, מחילים את המילוי באמצעות הבורר הזה ומוסיפים גם את clipToPadding="false".
מה בודקים אם האפליקציה חייבת להציע הגנה מותאמת אישית לרקע

אם האפליקציה צריכה להציע הגנה מותאמת אישית ברקע לניווט ב-3 לחצנים או לשורת הסטטוס, צריך להציב באפליקציה תוכן קומפוזבילי או תצוגה מאחורי סרגל המערכת באמצעות WindowInsets.Type#tappableElement() כדי לקבל את הגובה של סרגל הניווט ב-3 לחצנים או את WindowInsets.Type#statusBars.

משאבים נוספים מקצה לקצה

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

ממשקי API שהוצאו משימוש

ממשקי ה-API הבאים הוצאו משימוש, אבל לא הושבתו:

ממשקי ה-API הבאים הוצאו משימוש והושבתו:

Stable configuration

如果您的应用以 Android 15(API 级别 35)或更高版本为目标平台,Configuration 不再排除系统栏。如果您使用 Configuration 类中的屏幕尺寸进行布局计算,应根据需要将其替换为合适的 ViewGroupWindowInsetsWindowMetricsCalculator 等更好的替代方案。

Configuration 从 API 1 开始提供。它通常从 Activity.onConfigurationChanged 中获取。它提供窗口密度、屏幕方向和尺寸等信息。从 Configuration 返回的窗口大小的一个重要特征是,它之前会排除系统栏。

配置大小通常用于资源选择(例如 /res/layout-h500dp),这仍然是一个有效的用例。不过,我们一直不建议将其用于布局计算。如果确实如此,您应暂时放弃。您应根据自己的用例,将 Configuration 的使用替换为更合适的用法。

如果您使用它来计算布局,请使用适当的 ViewGroup,例如 CoordinatorLayoutConstraintLayout。如果您使用它来确定系统侧边栏的高度,请使用 WindowInsets。如果您想知道应用窗口的当前大小,请使用 computeCurrentWindowMetrics

以下列表介绍了受此变更影响的字段:

ערך ברירת המחדל של מאפיין maxTextHeight הוא True

For apps targeting Android 15 (API level 35), the elegantTextHeight TextView attribute becomes true by default, replacing the compact font used by default with some scripts that have large vertical metrics with one that is much more readable. The compact font was introduced to prevent breaking layouts; Android 13 (API level 33) prevents many of these breakages by allowing the text layout to stretch the vertical height utilizing the fallbackLineSpacing attribute.

In Android 15, the compact font still remains in the system, so your app can set elegantTextHeight to false to get the same behavior as before, but it is unlikely to be supported in upcoming releases. So, if your app supports the following scripts: Arabic, Lao, Myanmar, Tamil, Gujarati, Kannada, Malayalam, Odia, Telugu or Thai, test your app by setting elegantTextHeight to true.

elegantTextHeight behavior for apps targeting Android 14 (API level 34) and lower.
elegantTextHeight behavior for apps targeting Android 15.

שינויים ברוחב של TextView עבור צורות אותיות מורכבות

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

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

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

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

פריסה רגילה של טקסט באנגלית בגופן כתב יד. חלק מהאותיות חתוכות. זהו ה-XML התואם:

<TextView
    android:fontFamily="cursive"
    android:text="java" />
פריסה לאותו טקסט באנגלית עם רוחב ומרווח נוספים. זהו ה-XML התואם:

<TextView
    android:fontFamily="cursive"
    android:text="java"
    android:useBoundsForWidth="true"
    android:shiftDrawingOffsetForStartOverhang="true" />
פריסה רגילה של טקסט תאילנדי. חלק מהאותיות חתוכות. זהו קוד ה-XML התואם:

<TextView
    android:text="คอมพิวเตอร์" />
פריסה של אותו טקסט בתאילנדית עם רוחב נוסף וריפוי נוסף. זהו קוד ה-XML המתאים:

<TextView
    android:text="คอมพิวเตอร์"
    android:useBoundsForWidth="true"
    android:shiftDrawingOffsetForStartOverhang="true" />

גובה שורה שמוגדר כברירת מחדל בהתאם לאזור הגיאוגרפי של EditText

In previous versions of Android, the text layout stretched the height of the text to meet the line height of the font that matched the current locale. For example, if the content was in Japanese, because the line height of the Japanese font is slightly larger than the one of a Latin font, the height of the text became slightly larger. However, despite these differences in line heights, the EditText element was sized uniformly, regardless of the locale being used, as illustrated in the following image:

Three boxes representing EditText elements that can contain text from English (en), Japanese (ja), and Burmese (my). The height of the EditText is the same, even though these languages have different line heights from each other.

For apps targeting Android 15 (API level 35), a minimum line height is now reserved for EditText to match the reference font for the specified Locale, as shown in the following image:

Three boxes representing EditText elements that can contain text from English (en), Japanese (ja), and Burmese (my). The height of the EditText now includes space to accommodate the default line height for these languages' fonts.

If needed, your app can restore the previous behavior by specifying the useLocalePreferredLineHeightForMinimum attribute to false, and your app can set custom minimum vertical metrics using the setMinimumFontMetrics API in Kotlin and Java.

מצלמה ומדיה

ב-Android 15 יש שינויים בהתנהגות של המצלמה והמדיה באפליקציות שמטרגטות ל-Android 15 ואילך.

הגבלות על בקשות להתמקד באודיו

以 Android 15 为目标平台的应用必须是顶级应用或运行前台服务,才能请求音频焦点。如果应用在不符合其中任何一项要求时尝试请求焦点,该调用将返回 AUDIOFOCUS_REQUEST_FAILED

您可以参阅管理音频焦点,详细了解音频焦点。

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

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

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

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

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