Android 15 变更及适配攻略

在这里插入图片描述

2025年的第一篇Android适配,比以往来的更晚一些。废话不多说,我们开始!!

准备工作

首先将我们项目中的 targetSdkcompileSdk 升至 35。

  • 推荐使用Android Studio Koala Feature Drop | 2024.1.2或更高版本。
  • AGP版本最低升级到8.3.0
plugins {id 'com.android.application' version '8.3.0' apply false
}

影响Android 15上所有应用

1.最低可安装的目标 API 级别提升

Android 15要求应用的targetSDK最低为24,否则无法安装。这块和之前Android 14一样,以后每年应该都会加1,所以注意提前处理,避免新系统无法安装。

2.16KB页面大小支持

影响范围

使用任何NDK库的应用都需要重新编译以支持16KB页面大小的设备。这点之前还是默认开启的,后面调整为了不强制适配。但是自 2025 年 11 月 1 日起,提交到 Google Play 且以 Android 15 及更高版本的设备为目标平台的所有新应用和现有应用更新都必须支持 64 位设备上的 16 KB 页面大小。

它带来的性能提升有以下几点:

  • 应用启动时间平均缩短3.16%,部分应用可达30%

  • 应用启动时耗电量平均减少4.56%

  • 相机启动速度:热启动平均加快4.48%,冷启动平均加快6.60%

  • 系统启动时间平均改善8%

适配步骤

  • 升级AGP版本到8.3或更高,推荐AGP 8.5.1 或更高版本。

  • 使用16KB ELF对齐编译应用

  • 检查引用特定页面大小的代码

具体操作,参考官方文档。

我们是做出海项目的,所以比较关注这块,目前使用到的三方sdk都有支持。比如我们项目中使用了MMKV,虽然MMKV 2.x版本支持16KB页面大小,但不支持32位,所以还不能直接升级使用。1.3.x版本支持32位,但不支持16KB页面大小。作者开始也明确不会支持,本来还打算自己修改重新编译。好在前一阵谷歌有了上架的限制,作者也对MMKV 1.3.x版本做了相关适配支持,这里也是表示感谢。

3.当用户强制停止应用时,widget被停用

如果用户在搭载 Android 15 的设备上强制停止应用,系统会暂时停用该应用的所有widget。这些 widget 会灰显,用户无法与其互动。这是因为从 Android 15 开始,当系统强制停止应用时,会取消应用的所有待处理 intent

系统会在用户下次启动应用时重新启用这些微件。

影响以Android 15或更高版本为目标平台的应用

以下都是影响targetSDK >= 35的应用,适配时需要重点关注

1.edge-to-edge

首先说个影响面比较大的,那就是edge-to-edge,翻译过来就是边到边,应用的显示区域延伸到了全屏,不会避开状态栏/导航栏区域。

首先状态栏/手势导航条区域默认将透明,三键导航默认不透明度为80%。

比如我之前都会给布局添加android:fitsSystemWindows="true"和设置状态栏颜色进行沉浸状态栏的适配,但是由于Android 15状态栏现在变为透明,且setStatusBarColorR.attr#statusBarColor 已废弃,对 Android 15 没有任何作用。所以出现了下面的情况:
请添加图片描述

状态栏透明,因此颜色为页面的背景色,和标题栏颜色不同。

请添加图片描述

未添加android:fitsSystemWindows="true"的布局,底部按钮绘制在系统导航栏后面。

适配方法:

简单粗暴的方法就是停用edge-to-edge,可以创建values-v35,在theme中添加windowOptOutEdgeToEdgeEnforcement并设为true:


<resources xmlns:tools="http://schemas.android.com/tools"><style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">...<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item></style></resources>

不过windowOptOutEdgeToEdgeEnforcement在以Android 16为目标平台的应用将不再生效,无法停用edge-to-edge。所以这个只是缓兵之计,如果你的适配工作量比较大且紧急,可以使用此方法。

精细化适配方法,首先注意两点:

  • setStatusBarColor 在Android 15上不生效。
  • setNavigationBarColor 只针对三键导航栏有效果,手势导航条无效。

如果你使用Compose的话:

  • 使用Material 3 组件(androidx.compose.material3),例如 TopAppBarBottomAppBarNavigationBar,这些组件不会受到影响,因为它们会自动处理insets
  • 使用Material 2 组件(androidx.compose.material),或者自定义的 Composables,这些组件不会自动处理insets 。需要自己设置padding

非compose应用:

  • 在应用布局中添加android:fitsSystemWindows="true",但是需要检查状态栏颜色是否符合UI要求。
  • 未添加android:fitsSystemWindows="true"的布局,相信大多数都处理了状态栏,所以需要检查页面底部在系统导航栏后面的情况。同时查看导航栏颜色是否符合UI要求。
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) {ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.root), new OnApplyWindowInsetsListener() {@NonNull@Overridepublic WindowInsetsCompat onApplyWindowInsets(@NonNull View v, @NonNull WindowInsetsCompat insets) {Insets statusBarInsets = insets.getInsets(WindowInsetsCompat.Type.statusBars());Insets navigationBarInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars());v.setPadding(navigationBarInsets.left,statusBarInsets.top, // 如果之前处理了状态栏,可以改为0navigationBarInsets.right,navigationBarInsets.bottom);return insets;}});
}

2.前台服务相关变更

前台服务超时限制

系统会限制某些前台服务在应用处于后台时允许运行的时长。目前,此限制仅适用于 dataSyncmediaProcessing 前台服务类型。

限制规则:

  • 24小时内总共只能运行6小时

  • 达到限制后,系统调用Service.onTimeout(int, int)方法,服务将不再被视为前台服务。

  • 服务有几秒钟时间调用Service.stopSelf(),如果服务未调用,系统会抛出内部异常。

  • 用户将应用打开到前台可重置计时。再开一个 dataSync 时间也不会重新计时。

适配代码参考:

public class MyDataSyncService extends Service {@Overridepublic int onTimeout(int startId, int fgsType) {Log.w("ForegroundService", "服务超时,类型: " + fgsType);// 实现onTimeout方法,同时必须在几秒内调用stopSelf()stopSelf();return super.onTimeout(startId, fgsType);}@Overridepublic IBinder onBind(Intent intent) {return null;}
}

如果24小时内已经运行了 6 个小时,则无法启动另一个dataSync前台服务,系统会抛出ForegroundServiceStartNotAllowedException,并显示类似“前台服务类型 dataSync 的时间限制已用完”的错误消息。

public class MainActivity extends AppCompatActivity {private void startDataSyncService() {Intent serviceIntent = new Intent(this, MyDataSyncService.class);try {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {startForegroundService(serviceIntent);} else {startService(serviceIntent);}} catch (ForegroundServiceStartNotAllowedException e) {Log.e("Service", "前台服务启动被限制: " + e.getMessage());// 使用WorkManager等替代方案scheduleWorkWithWorkManager();}}private void scheduleWorkWithWorkManager() {// 使用WorkManager作为替代方案OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(DataSyncWorker.class).setConstraints(new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()).build();WorkManager.getInstance(this).enqueue(workRequest);}
}
BOOT_COMPLETED广播接收器限制

在启动 BOOT_COMPLETED 广播接收器方面存在新限制前台服务。BOOT_COMPLETED 接收器不能启动以下类型的前台服务:

  • dataSync
  • camera
  • mediaPlayback
  • phoneCall
  • mediaProjection
  • microphone(自 Android 14 起,microphone 就受到此限制)

如果 BOOT_COMPLETED 接收器尝试启动任何上述类型的前台 服务,系统会抛出ForegroundServiceStartNotAllowedException

public class BootReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {// ❌ 不能在BOOT_COMPLETED中启动这些类型的前台服务// Intent serviceIntent = new Intent(context, DataSyncService.class);// context.startForegroundService(serviceIntent);// ✅ 可以使用WorkManager调度任务实现你的需求OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(BootWorker.class).setInitialDelay(5, TimeUnit.SECONDS).build();WorkManager.getInstance(context).enqueue(workRequest);}}
}
SYSTEM_ALERT_WINDOW权限限制

持有SYSTEM_ALERT_WINDOW权限的应用需要有可见的overlay窗口才能启动前台服务,也就是说,应用需要先启动 TYPE_APPLICATION_OVERLAY 窗口,并且该窗口需要处于可见状态,然后您才能启动前台服务。否则系统会抛出 ForegroundServiceStartNotAllowedException

public class OverlayService extends Service {private View overlayView;private WindowManager windowManager;@Overridepublic void onCreate() {super.onCreate();createOverlayWindow();}private void createOverlayWindow() {windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);overlayView = LayoutInflater.from(this).inflate(R.layout.overlay_layout, null);WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,PixelFormat.TRANSLUCENT);windowManager.addView(overlayView, params);// 检查窗口可见性overlayView.setOnWindowVisibilityChangedListener(new View.OnWindowVisibilityChangeListener() {@Overridepublic void onWindowVisibilityChanged(int visibility) {if (visibility == View.VISIBLE) {// 可见时才可以安全启动前台服务startForegroundServiceSafely();}}});}private void startForegroundServiceSafely() {if (overlayView != null && overlayView.getWindowVisibility() == View.VISIBLE) {try {Intent serviceIntent = new Intent(this, MyForegroundService.class);startForegroundService(serviceIntent);} catch (ForegroundServiceStartNotAllowedException e) {Log.e("Service", "前台服务启动失败: " + e.getMessage());}}}
}

3.提高 intent 安全性

  • Intent必须有``action`
  • 与目标 intent 过滤器匹配
Intent intent = new Intent(context, targetClass);
// 确保Intent有action
intent.setAction(Intent.ACTION_VIEW);
try {context.startActivity(intent);
} catch (ActivityNotFoundException e) {Log.e("Intent", "无法启动Activity", e);
}    

4.OpenJDK API 变更

Android 15 继续推进 Android 核心库的现代化,以与最新 OpenJDK LTS 版本(OpenJDK 17)的功能保持一致。这些变更可能会影响针对 Android 15(API 级别 35)的应用兼容性,特别是在字符串格式化、集合处理和随机数生成等方面。

字符串格式化 API 变更

Android 15 对以下字符串格式化 API 的参数索引、标志、宽度和精度验证变得更加严格:

  • String.format(String, Object[])

  • String.format(Locale, String, Object[])

  • Formatter.format(String, Object[])

  • Formatter.format(Locale, String, Object[])

使用参数索引 0 时会抛出异常:

// 错误示例 - 会抛出异常
String result = String.format("%0s", "test");
// IllegalFormatArgumentIndexException: Illegal format argument index = 0// 正确做法 - 使用索引 1
String result = String.format("%1$s", "test");// 或者不使用索引
String result = String.format("%s", "test");
Arrays.asList(…).toArray() 的组件类型变更

使用 Arrays.asList(…).toArray() 时,生成的数组的组件类型现在是 Object,而不是底层数组元素的类型。因此,以下代码会抛出 ClassCastException:

String[] elements = (String[]) Arrays.asList("a", "b", "c").toArray();

适配方案

  1. 使用类型安全的方法‘
// 正确做法 - 指定数组类型
List<String> list = Arrays.asList("a", "b", "c");
String[] array = list.toArray(new String[0]);
  1. 使用 List.of()(推荐)
// 使用 Java 9+ 的 List.of()
List<String> list = List.of("a", "b", "c");
String[] array = list.toArray(new String[0]);
语言代码处理方式变更

使用 Locale API 时,希伯来语、意第绪语和印度尼西亚语的语言代码不再转换为已废弃的形式(希伯来语:iw、意第绪语:ji、印度尼西亚语:in)。为其中一种语言区域指定语言代码时,请改用 ISO 639-1 中的代码(希伯来语:he、意第绪语:yi、印度尼西亚语:id)。

例如:

旧文件夹:
- values-iw/(希伯来语)
- values-ji/(意第绪语)
- values-in/(印度尼西亚语)新文件夹:
- values-he/(希伯来语)
- values-yi/(意第绪语)
- values-id/(印度尼西亚语)

其他还有PDF的优化,HDR 余量控制,请求音频焦点的限制等变更。因为我实际也没有适配,这里就不说明了,有需要的可以查看官方文档。

参考

  • Android 15 功能和变更列表

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/86157.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/86157.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue项目使用defer优化页面白屏,性能优化提升,秒加载!!!

defer表示延迟加载&#xff0c;针对大量节点的渲染加载&#xff0c;结合使用关键帧requestAnimationFrame的形式来分片加载&#xff0c;可以优化白屏时间 知识补充&#xff1a; requestAnimationFrame requestAnimationFrame 是根据帧数来执行回调函数的&#xff0c;就是屏幕…

sentinel与seata组件在微服务中的基本作用

微服务基础内容&#xff1a; 在微服务中&#xff0c;首先学习了微服务的横向拆分与纵向拆分&#xff0c;纵向拆分指按照功能拆分模块&#xff0c;横向拆分指将高复用的模块单独拆分&#xff0c;使纵向拆分的模块去调用这部分内容。 学习了基本拆分后&#xff0c;需要知道微服…

微信点餐小程序—美食物

本项目是基于WAMP Server 和PHP 动态网页技术构建的微信小程序点餐系统&#xff0c;该系统主要分为前端&#xff08;微信小程序&#xff09;和后端&#xff08;基于PHPMySQL服务器端&#xff09; 整体架构流程 1、前端部分 用户界面&#xff1a;展示菜品、处理用户点餐操作、…

记录Idea运行控制台乱码处理方案

记录Idea运行控制台乱码处理方案 方法1&#xff1a;修改运行配置 打开 Run/Debug Configurations在对应的运行配置中 → 找到 VM Options → 添加&#xff1a; -Dfile.encodingUTF-8 -Dsun.jnu.encodingUTF-8重新运行程序 方法2&#xff1a;强制指定输出流编码 在代码中显…

JVM对象内存分配机制全解析

jvm创建对象的内存分配过程 1、逃逸对象在栈上分配 通过在栈上为对象分配内存,使对象占用的内存空间随着方法结束栈帧弹出而销毁,避免了GC垃圾收集器回收对象,减小GC的压力; 栈上分配内存依赖逃逸分析和标量替换。 逃逸分析: 分析对象的动态作用域逃逸:当一个对象在方…

揭秘OSPF核心:LSA类型与路由计算

一、区域内路由计算 同一区域内中的所有路由器有相同的LSDB LSA关键字段&#xff1a; 【1】LS Age&#xff08;链路状态老化时间&#xff09;&#xff1a;LSA生存的时间&#xff0c;单位秒 【2】Option&#xff08;选项字段&#xff09; 【3】LS Type&#xff08;链路状…

英文摘要给成中文摘要模型

你现在使用的 UNIMO 项目&#xff08;PaddlePaddle/Research/NLP/UNIMO&#xff09;&#xff0c;默认是做英文摘要任务&#xff0c;如你在 README 中看到的数据集是 CNN/DailyMail&#xff0c;它是一个 英文摘要数据集。不过&#xff0c;这个项目的架构完全支持中文&#xff0c…

前端面试专栏-主流框架:13.vue3组件通信与生命周期

&#x1f525; 欢迎来到前端面试通关指南专栏&#xff01;从js精讲到框架到实战&#xff0c;渐进系统化学习&#xff0c;坚持解锁新技能&#xff0c;祝你轻松拿下心仪offer。 前端面试通关指南专栏主页 前端面试专栏规划详情 Vue3组件通信与生命周期深度解析 在Vue3的开发体系…

自动化交易优化网格策略

一、动态参数调整 1. 网格间距优化 - 波动率自适应&#xff1a;使用平均真实波幅&#xff08;ATR&#xff09;指标动态调整间距。例如&#xff0c;当ATR值上升20%时&#xff0c;将间距从原定的1%扩大至1.5%&#xff1b;ATR下降时则缩小间距至0.8%。可通过Python的TA-Lib库实时计…

测试平台ui自动化demo说明

1. 要启动celery worker windows 开发时&#xff0c;用第二行 。&#xff08;试过&#xff0c;可以&#xff09;&#xff0c;第一行的没试过。 celery -A myproject worker --loglevelinfo # windows电脑用下面的&#xff0c;并且settings中还要加那个solo celery -A your_p…

五大主要Token类型之字符标记Token

如大家所了解的&#xff0c;在数字化时代&#xff0c;我们每天都会与Token&#xff08;令牌&#xff09;打交道——无论是在线支付、登录社交媒体&#xff0c;还是调用API接口&#xff0c;都离不开这一关键技术。 今天我们主要来学习&#xff1a;字符标记Token 在自然语言处理…

可理解性输入:洗澡习惯

一、开场与淋浴准备 Today we’re going to learn bathroom English. Let’s get started. So the first thing we want to do. Make sure we have our towel and we’ll hang it on the towel rack before we have a shower. Because if we have a shower and then forget ou…

GO Echo框架面试题及参考答案

目录 Echo 框架的核心结构是什么?Echo 和 Context 分别扮演什么角色? 如何创建一个 Echo 实例?简述常见配置项。 e.Start () 与 e.StartServer () 的区别是什么? Echo 如何实现基于先后顺序路由匹配? 如何注册 GET、POST、PUT、DELETE 等不同 HTTP 方法的路由? Echo…

Java 中LinkedList 总结

406.根据身高重建队列 力扣题目链接(opens new window) 假设有打乱顺序的一群人站成一个队列&#xff0c;数组 people 表示队列中一些人的属性&#xff08;不一定按顺序&#xff09;。每个 people[i] [hi, ki] 表示第 i 个人的身高为 hi &#xff0c;前面 正好 有 ki 个身高…

大模型微调:从零到实践,掌握AI大模型的核心技能

大模型微调&#xff1a;从零到实践&#xff0c;掌握AI大模型的核心技能 引言 大规模语言模型&#xff08;如DeepSeek、通义千问&#xff09;的出现&#xff0c;彻底改变了自然语言处理的格局。这些模型不仅在学术界取得了突破性进展&#xff0c;在工业界也得到了广泛应用。 …

Flutter - 原生交互 - 相册

环境 Flutter 3.29 macOS Sequoia 15.4.1 Xcode 16.3 iOS 13.4.1 iOS 18.5 集成image_picker 在Flutter中可以使用image_picker插件实现从相册中获取图片 添加插件 flutter中访问相册image_picker插件 flutter pub add image_pickerflutter pub getXcode工程的GenerateP…

node.js在vscode的配置

文章目录 概要1. 使用和webstrom一样的快捷键2. 让vscode的主题变成webstrom3. 如何在 Node.js 环境下写代码3.1 使用 ESLint配置规则3.2 配置.vscode/settings.json 4. Prettier安装5. 其它问题解决 概要 node.js在webstrom编辑器中可以完美使用代码提示、错误提示等功能&…

Android14音频子系统-Audio HAL分析

文章目录 1&#xff09;概述2&#xff09;HAL的打开流程3&#xff09;HAL库的实现(Qualcomm)4&#xff09;tinyalsa5&#xff09;数据结构6&#xff09;代码流程 1&#xff09;概述 1、回顾HAL、tinyalsa与linux driver的关系 2、与AudioFlinger的关系 3、 1、如何判断当前…

前端与 Spring Boot 后端无感 Token 刷新 - 从原理到全栈实践

&#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Micro麦可乐的博客 &#x1f425;《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程&#xff0c;入门到实战 &#x1f33a;《RabbitMQ》…

【AI智能体】新手教程-通过 Chat SDK 搭建网页在线客服

通过扣子搭建的智能体可以一键发布为 Chat SDK&#xff0c;快速部署到你的自建网站中&#xff0c;作为在线智能客服面向网站的用户提供 AI 答疑服务。本文档介绍通过 Chat SDK 搭建网页版在线客服的详细操作步骤。 场景说明 网站作为企业和组织与用户互动的重要平台&#xff…