Android 7.0 行为变更

除了丰富的新功能,Android 7.0 包括对系统和 API 行为的各种变更。此文档 会重点介绍您应该了解并加以考虑的一些关键变更 。

如果您之前发布过 Android 应用,请注意您的应用 可能会受到平台中这些变更的影响。

电池和内存

Android 7.0 包含旨在延长电池续航时间的系统行为变更 并减少 RAM 使用量。这些变更可能会影响您的应用对以下内容的访问权限: 系统资源,以及您的应用通过 某些隐式 intent。

低电耗模式

Android 6.0(API 级别 23)中引入,低电耗模式可延长电池续航时间 在用户未接通设备电源时延迟其 CPU 和网络活动; 静止不动,同时屏幕已关闭。Android 7.0 进一步 通过应用部分 CPU 和网络限制来增强低电耗模式 在拔下电源并关闭屏幕时启动,但不一定 静止不动,例如,当手机放入口袋中时。

低电耗模式如何应用第 1 级的图示
  旨在延长电池续航时间的系统活动限制

图 1. 低电耗模式如何应用第 1 级的图示 系统活动限制以延长电池续航时间。

设备使用电池供电且屏幕已关闭一定时间 时,设备会进入低电耗模式并应用第一部分限制: 关闭应用网络访问权限,推迟作业和同步。如果设备 在进入低电耗模式后静止一段时间后,系统会应用 PowerManager.WakeLock 的低电耗模式限制, AlarmManager 次闹钟、GPS 和 WLAN 扫描。无论 无论应用的是部分或全部低电耗模式限制,系统都会唤醒 进行短暂的维护,在此期间允许应用 网络访问权限,并可以执行任何延迟的作业/同步。

低电耗模式如何应用低电耗模式第二级的图示
  设备静止一段时间后的系统活动限制

图 2. 低电耗模式如何应用低电耗模式第二级的图示 设备静止一段时间后系统活动限制。

请注意,激活屏幕或将设备插入电源会退出低电耗模式, 移除这些处理限制。额外行为不会 影响应用的建议和最佳实践, Android 6.0(API 级别 23)中引入的低电耗模式版本,如 <ph type="x-smartling-placeholder"></ph> 针对低电耗模式和应用待机模式进行优化。您仍然应该 遵循这些建议,例如使用 Firebase Cloud Messaging (FCM) 收发消息,并开始规划更新,以适应 其他低电耗模式行为

Project Svelte:后台优化

Android 7.0 移除了三项隐式广播,以帮助优化这两者 内存用量和功耗此项更改是必要的,因为隐式 广播会频繁启动已在 背景。移除这些广播可为设备带来实质性的好处 性能和用户体验

移动设备会经历频繁的连接变更,例如在移动 Wi-Fi 和移动数据网络之间的连接。目前,应用可以监控 方法是在其 API 中针对隐式 CONNECTIVITY_ACTION 广播注册接收器 清单。由于许多应用都会注册接收此广播,因此单个 网络切换可能会导致所有设备在 3D 时唤醒并处理广播, 一次。

同样,在之前的 Android 版本中,应用可以注册接收来自其他应用的隐式 ACTION_NEW_PICTUREACTION_NEW_VIDEO 广播,例如 摄像头。当用户使用相机应用拍照时,这些应用会被唤醒 处理广播。

为缓解这些问题,Android 7.0 应用了 优化:

如果您的应用使用其中任何 intent,您应移除依赖项 ,以便您正确以 Android 7.0 设备为目标平台。 Android 框架提供了多种解决方案来缓解对 这些隐式广播例如,JobScheduler API 提供了一种稳健的机制来调度 特定条件下的网络操作,例如连接到 不按流量计费的网络。您甚至可以使用 JobScheduler 来响应 content provider 的变化。

详细了解 Android 7.0(API 级别)中的后台优化 24) 以及如何改写您的应用,请参阅背景信息 优化

权限更改

Android 7.0 做了一些权限更改,这些更改可能会影响您的应用。

系统权限更改

为了提高私有文件的安全性, 以 Android 7.0 或更高版本为目标平台的应用的访问权限受限 (0700)。 此设置可防止私有文件的元数据泄露,如其大小 或存在。此权限更改具有多种副作用:

在应用间共享文件

对于以 Android 7.0 为目标平台的应用,Android 框架会强制执行 禁止公开 file:// URI 的 StrictMode API 政策 。如果包含文件 URI 的 intent 离开您的应用,应用将失败 但 FileUriExposedException 除外。

如需在应用之间共享文件,您应发送 content:// URI 并授予 URI 临时访问权限。要授予此权限,最简单的方法是 使用 FileProvider 类。更多信息 包括权限和共享文件 请参阅共享文件

无障碍功能改进

Android 7.0 包含一些旨在提高 专为有视力障碍的用户打造的平台。这些更改应该 通常不需要更改应用中的代码,但是您应该查看 并在您的应用中测试这些功能,以评估它们对用户的潜在影响 体验

屏幕缩放

Android 7.0 允许用户设置显示大小,用于放大 或缩小屏幕上的所有元素,从而改进设备的无障碍功能 为弱视用户打造个性化使用体验用户无法将屏幕缩放到最小屏幕尺寸 宽度:<ph type="x-smartling-placeholder"></ph> sw320dp,即可调整为 Nexus 4(普通中等大小手机)的宽度。

显示运行 Android 7.0 系统映像的设备未缩放显示大小的屏幕
一个屏幕,显示了搭载 Android 7.0 系统映像的设备增大显示大小的效果

图 3. 右侧画面显示的是 增大搭载 Android 7.0 系统映像的设备的显示尺寸。

当设备密度发生更改时,系统会通知 方法:

  • 如果应用以 API 级别 23 或更低级别为目标平台,系统会自动终止 所有后台进程运行也就是说,如果用户切换显示 以打开 Settings 屏幕并更改 Display size [显示大小] 设置,系统会在同一屏幕中终止应用 和内存不足时执行的操作相同。如果应用在前台运行 进程时,系统会通知这些进程配置更改, 请参阅处理 运行时更改,就像设备的屏幕方向发生了变化一样。
  • 如果应用以 Android 7.0 为目标平台,则其所有进程 (前台和后台)都会收到关于配置更改的通知, 请参阅处理 运行时更改

大多数应用无需进行任何更改即可支持此功能,前提是 应用遵循 Android 最佳实践需要检查的具体事项:

  • 在屏幕宽度为 sw320dp 的设备上测试您的应用 并确保其充分运行
  • 当设备配置发生变化时,更新与密度相关的任何事件 缓存的信息(例如缓存的位图或从 。当应用从暂停状态恢复运行时,检查配置更改 状态。

    注意:如果您要缓存依赖于配置的数据, 最好在代码中包含相关的元数据,例如 尺寸或像素密度保存这些元数据后,你可以 决定是否需要在配置完成后刷新缓存数据 更改。

  • 避免用像素单位指定尺寸,因为像素不会随尺寸一起缩放 屏幕密度而是应该使用“与密度无关”指定尺寸 像素 (dp) 单位。

设置向导中的视觉设置

Android 7.0 在“Welcome”屏幕中加入了“Vision Settings”,用户可以 在新设备上设定以下无障碍设置: 放大手势字体大小 显示大小TalkBack。此更改 增加了与不同屏幕设置相关的错误的可见性。接收者 评估此功能的影响时,您应该使用以下这些条件测试您的应用: 设置已启用。您可以在设置 > 无障碍功能

NDK 应用链接至平台库

从 Android 7.0 开始,系统会阻止应用动态链接 非 NDK 库,这可能会导致应用崩溃。此次变更在 行为旨在为各种平台更新提供一致的应用体验 和不同的设备。即使您的代码可能未链接到 私有库,则您的外部环境中的第三方静态库 可能的原因是什么因此,所有开发者都应检查以确保 确保应用不会在搭载 Android 7.0 的设备上崩溃。如果您的应用使用 原生代码,您只能使用公开 NDK API

您的应用可通过三种方式尝试访问私有平台 API:

  • 您的应用直接访问私有平台库。您应更新 您的应用添加自己的库副本,或使用公开 NDK API
  • 您的应用使用访问私有平台的第三方库 库。即使您确定自己的应用不访问专用库 您仍然应该针对这种情况测试您的应用。
  • 应用引用的库未包含在其 APK 中。对于 例如,如果您尝试使用自己的 OpenSSL 副本, 忘记将其与您应用的 APK 捆绑在一起。该应用可以在各版本上正常运行 的 Android 平台版本(包含 libcrypto.so)。不过,该应用 在不包含此库的更高 Android 版本中可能会崩溃 (例如 Android 6.0 及更高版本)。要解决此问题,请确保将 非 NDK 库与您的 APK 结合使用。

应用不应使用 NDK 中未包含的原生库,因为 它们可能会在不同的 Android 版本之间更改或移除。通过 从 OpenSSL 切换到 BoringSSL 就是此类更改的一个示例。此外, 因为对平台库没有兼容性要求, 包含在 NDK 中,不同的设备可能会提供不同级别的 兼容性。

为了减少这一限制可能对目前造成的影响 发布的应用,即一系列备受瞩目的库,例如 libandroid_runtime.solibcutils.solibcrypto.solibssl.so 对于以 API 级别 23 为目标平台的应用,可在 Android 7.0(API 级别 24)上访问;或 。如果您的应用加载其中某个库,logcat 会生成一条警告 并且目标设备上会显示消息框来通知您。如果您看到 您应更新自己的应用,使其包含自己的警告副本 库或仅使用公共 NDK API。Android 的未来版本 可能会完全限制对私有库的使用,并导致您的 应用崩溃

所有应用在调用既非正式版的 API 时都会生成运行时错误 或暂时访问。其结果是 System.loadLibrarydlopen(3) 均返回 NULL,并且可能会导致应用崩溃。您应当查看自己的 应用代码,以避免使用私有平台 API 并全面测试应用 使用搭载 Android 7.0(API 级别 24)的设备或模拟器。如果您 不确定您的应用是否使用专用库,您可以检查 logcat 以识别运行时错误。

下表介绍了您应该会发现的 具体取决于应用使用的私有原生库及其目标 API 级别 (android:targetSdkVersion)。

图书馆 目标 API 级别 通过动态链接器进行运行时访问 Android 7.0(API 级别 24)行为 未来的 Android 平台行为
NDK 公开 不限 无障碍设施 运行正常 运行正常
私享(暂时可访问的私享库) 23 或更低 暂时可访问 按预期运行,但您会收到 logcat 警告。 运行时错误
私享(暂时可访问的私享库) 24 或更高版本 受限 运行时错误 运行时错误
不公开(其他) 不限 受限 运行时错误 运行时错误

检查应用是否使用专用库

为帮助您识别加载私有库的问题,logcat 可能会生成 警告或运行时错误。例如,如果您的应用以 API 级别 23 或 并尝试在运行 Android 7.0 的设备上访问私有库, 您可能会看到类似于以下内容的警告:

03-21 17:07:51.502 31234 31234 W linker  : library "libandroid_runtime.so"
("/system/lib/libandroid_runtime.so") needed or dlopened by
"/data/app/com.popular-app.android-2/lib/arm/libapplib.so" is not accessible
for the namespace "classloader-namespace" - the access is temporarily granted
as a workaround for http://b/26394120

这些 logcat 警告会告诉您哪个库正在尝试访问 私有平台 API,但不会导致您的应用崩溃。如果应用 目标 API 级别为 24 或更高级别,但 logcat 会生成以下 运行时错误,您的应用可能会崩溃:

java.lang.UnsatisfiedLinkError: dlopen failed: library "libcutils.so"
("/system/lib/libcutils.so") needed or dlopened by
"/system/lib/libnativeloader.so" is not accessible for the namespace
"classloader-namespace"
  at java.lang.Runtime.loadLibrary0(Runtime.java:977)
  at java.lang.System.loadLibrary(System.java:1602)

如果您的应用使用第三方库,您可能还会看到这些 logcat 输出 与专用平台 API 动态链接。Readelf 工具 Android 7.0DK 可让您生成所有动态链接的 指定 .so 文件的库。

aarch64-linux-android-readelf -dW libMyLibrary.so

更新应用

您可以采取以下措施来修正这些类型的错误 确保您的应用在将来进行平台更新时不会崩溃:

  • 如果您的应用使用专用平台库,您应该更新应用以包含 这些库的自有副本,或使用公开 NDK API
  • 如果您的应用使用访问私有符号的第三方库,请联系 库作者来更新库。
  • 请务必将所有非 NDK 库与 APK 打包在一起。
  • 使用标准 JNI 函数(而非 getJavaVM)和 来自libandroid_runtime.sogetJNIEnv
    AndroidRuntime::getJavaVM -> GetJavaVM from <jni.h>
    AndroidRuntime::getJNIEnv -> JavaVM::GetEnv or
    JavaVM::AttachCurrentThread from <jni.h>.
    
  • 使用 __system_property_get 而不是私有 property_getlibcutils.so 开始。为此,请使用 __system_property_get 包括:
    #include <sys/system_properties.h>
    

    注意:系统属性的可用性和内容 未通过 CTS 测试。更好的解决方法是避免使用这些 属性。

  • 使用 libcrypto.soSSL_ctrl 符号的本地版本。例如,您应在以下位置静态链接 libcyrpto.a: 您的 .so 文件,或添加 BoringSSL/OpenSSL 中的 libcrypto.so 动态链接版本,并将其打包到您的 APK 中。

Android for Work

Android 7.0 包含针对以 Android for Work 为目标平台的应用所做的更改,包括 证书安装更改、密码重置、次要用户 对设备标识符的管理和访问如果您正在为 Android for Work 环境,请查看这些更改并修改 做出相应的调整

  • 您必须先安装委派证书安装程序,然后 DPC 才能设置 。对于以 Android 7.0(API 级别 24)为目标平台的个人资料和设备所有者应用: 您应先安装授权证书安装程序,然后再安装设备政策 控制器 (DPC) 调用 DevicePolicyManager.setCertInstallerPackage()。如果安装程序 尚未安装,则系统会抛出 IllegalArgumentException
  • 针对设备管理员的重置密码限制现已应用于个人资料 所有者。设备管理员无法再使用 DevicePolicyManager.resetPassword()即可清除或更改密码 已经设置的应用设备管理员仍然可以设置密码,但只能 当设备没有密码、PIN 码或图案时。
  • 即使受到了限制,设备和资料所有者仍可管理账号 。设备所有者和资料所有者可以调用 Account Management API 即使应用了 DISALLOW_MODIFY_ACCOUNTS 用户限制。
  • 设备所有者可以更轻松地管理次要用户。当设备 在设备所有者模式下运行,则 DISALLOW_ADD_USER 限制 系统会自动设置这样可以防止用户创建非受管辅助资源 用户。此外,CreateUser() 和 废弃了 createAndInitializeUser() 方法;新的 DevicePolicyManager.createAndManageUser() 方法会替换它们。
  • 设备所有者可以访问设备标识符。设备所有者可以访问 设备的 Wi-Fi MAC 地址,使用 DevicePolicyManager.getWifiMacAddress()。如果 Wi-Fi 从未 则此方法会返回 null 值。
  • 工作模式设置用于控制对工作应用的访问权限。关闭工作模式后, 系统启动器通过使工作应用显示为灰色来指示它们不可用。正在启用 工作模式会再次恢复正常行为。
  • 安装包含客户端证书链的 PKCS #12 文件时 设置界面中的相应私钥、 链不再安装到可信凭据存储空间中。这样可以 当应用尝试检索客户端时,不会影响 KeyChain.getCertificateChain() 的结果 证书链如果需要,应安装 CA 证书 通过“设置”界面单独访问可信凭据存储空间,并且 采用 .crt 或 .cer 扩展名的 DER 编码格式。
  • 从 Android 7.0 开始,指纹注册和存储空间将由系统管理 。如果个人资料所有者的 Device Policy Client (DPC) 以 API 级别为目标平台 23(或更低版本),则用户 仍然可以在设备上设置指纹,但工作应用无法 访问设备指纹。当 DPC 面向 API 级别 24 及更高级别时,用户可以设置 设置 > 安全 >工作资料安全性
  • 新的加密状态为“ENCRYPTION_STATUS_ACTIVE_PER_USER” 由DevicePolicyManager.getStorageEncryptionStatus()退回, 表明加密有效,且加密密钥与 用户。仅当 DPC 面向 API 级别 24 及更高级别时,系统才会返回新状态。 对于以较低 API 级别为目标平台的应用,ENCRYPTION_STATUS_ACTIVE 即使加密密钥是用户或个人资料专用的,也是如此。
  • 在 Android 7.0 中,有几个方法通常会影响整个 如果设备安装了工作资料和 单独的工作挑战这些不是影响整个设备, 方法仅适用于工作资料。(此类方法的完整列表为 详见 DevicePolicyManager.getParentProfileInstance() 文档。)例如: DevicePolicyManager.lockNow() 仅锁定工作资料,而非锁定设备 锁定整个设备。对于每种方法,您都可以 通过对父对象实例调用 方法, DevicePolicyManager;可以通过以下方式获取此父级: 调用 DevicePolicyManager.getParentProfileInstance()。例如,如果您调用 父实例的 lockNow() 方法,则整个设备会被锁定。

注解保留

Android 7.0 修复了一个注解可见性被忽略的错误。 此问题使得运行时可以访问本不应存在的注解 。这些注释包括:

  • VISIBILITY_BUILD:仅应在构建时可见。
  • VISIBILITY_SYSTEM:在运行时可见,但仅对 底层系统

如果您的应用依赖此行为,请为注解添加保留政策, 在运行时可用为此,您可以使用 @Retention(RetentionPolicy.RUNTIME)

TLS/SSL 默认配置变更

Android 7.0 对默认的 TLS/SSL 配置进行了以下更改 用于 HTTPS 和其他 TLS/SSL 通信:

  • RC4 加密套件现已停用。
  • CHACHA20-POLY1305 加密套件现已启用。

RC4 默认处于停用状态可能会导致 HTTPS 或 TLS/SSL 损坏 连接。 首选的解决方法是改进服务器配置, 以及更多现代加密套件和协议理想情况下,可以使用 TLSv1.2 和 AES-GCM 应启用 Forward Secrecy 加密套件 (ECDHE) 和首选资源

另一种方法是修改应用,以使用自定义 SSLSocketFactory 与服务器通信。通过 应该创建 SSLSocket 具有服务器所需的某些加密套件的实例 以及默认加密套件外的加密套件

注意:这些更改与 WebView 无关。

以 Android 7.0 为目标平台的应用

这些行为变更仅影响以 Android 7.0(API 级别 24)或更高版本。针对 Android 7.0、 或者将 targetSdkVersion 设置为 Android 7.0 或更高版本必须修改 来适当地支持这些行为(如果适用)。

序列化变更

Android 7.0(API 级别 24)修复了计算默认值时出现的错误 与规范不符的 serialVersionUID。

实现 Serializable 的类 并且不指定明确的serialVersionUID字段, 发现其默认 serialVersionUID 发生了更改,这会导致异常 类的实例反序列化时抛出的异常 已在较早版本上序列化,或由针对早期版本的应用序列化 版本。错误消息将如下所示:

local class incompatible: stream classdesc serialVersionUID = 1234, local class serialVersionUID = 4567

若要解决这些问题,需要在serialVersionUID 错误消息中值为 stream classdesc serialVersionUID 的任何受影响的类,例如1234 英寸 这种情况。此更改遵循了针对 编写序列化代码,将适用于所有 Android 版本。

已修复的具体 bug 与存在静态 初始化式方法,即 <clinit>。根据 用于指定 类将影响针对该类计算的默认 serialVersionUID。 在 bug 修复之前,计算还会检查超类是否有 static initializer。

需要说明的是,此变更不会影响以 API 级别 23 或 具有 serialVersionUID 字段的一个或多个类 具有静态初始化方法的资源

其他重要说明

  • 如果应用在 Android 7.0 上运行,但以较低的 API 级别为目标平台, 当用户更改显示大小时,应用进程将被终止。应用 必须能够妥善处理这种情况。否则,应用会崩溃 当用户从最近使用记录中恢复该应用时触发。

    您应对应用进行测试 不会发生此行为。 为此,您可以使相同的崩溃 通过 DDMS 手动终止应用时。

    以 Android 7.0(API 级别 24)及更高版本为目标平台的应用不会自动 因密度变化而终止但他们仍然对 配置更改。

  • Android 7.0 上的应用应该能够妥善处理配置变更 并且在后续启动时不会崩溃。您可以验证应用行为 更改字体大小(设置 > 展示 >字体大小),然后还原 从“最近使用的应用”中选择该应用
  • 由于之前的 Android 版本中存在 bug,因此系统未标记写入 发送到主线程上的 TCP 套接字。Android 7.0 修复了此错误。 出现此行为的应用现在会抛出 android.os.NetworkOnMainThreadException。 一般情况下,我们不建议在主线程上执行网络操作 通常延迟时间较长,会导致 ANR 和卡顿。
  • Debug.startMethodTracing() 方法系列现在默认为 将输出内容存储到共享存储空间上的软件包专属目录中, 而不是在顶层 。这意味着应用不再需要请求 WRITE_EXTERNAL_STORAGE 权限即可使用这些 API。
  • 许多平台 API 现已开始检查是否发送大型载荷 跨 Binder 笔交易,以及 系统现在再次抛出 TransactionTooLargeExceptions 作为 RuntimeExceptions,而不是静默地记录或抑制它们。一个 一个常见的例子是 Activity.onSaveInstanceState(), 这会导致 ActivityThread.StopInfo 抛出 RuntimeException(当应用以 Android 7.0 为目标平台时)。
  • 如果应用向 View 发布 Runnable 任务,并且 View 未附加到窗口时,系统 使用 ViewRunnable 任务排入队列; Runnable 任务不会执行,直到 已附加View 一个窗口此行为会修复以下 bug: <ph type="x-smartling-placeholder">
      </ph>
    • 如果应用通过非预期线程发布到 View 窗口的界面线程中,因此 Runnable 可能会在错误的线程上运行。
    • 如果 Runnable 任务是从非以下线程发布的: 环路线程,应用可以公开 Runnable 任务。
  • 如果搭载 Android 7.0 的应用 DELETE_PACKAGES 权限尝试删除某个软件包,但另一应用已经安装了该软件包, 系统要求用户确认。在这种情况下,应用应 STATUS_PENDING_USER_ACTION 作为其在调用 PackageInstaller.uninstall()
  • 名为 Crypto 的 JCA 提供程序已被弃用,因为它仅 SHA1PRNG 的加密强度较弱。应用无法再使用 SHA1PRNG 用于(不安全)派生密钥,因为此提供程序不再 可用。详情请参阅博客 发布安全“Crypto”提供程序在 Android 中已废弃 北