Android5.0-10.0各版本行为变更

Android Q 行为变更

非 SDK 接口限制

为了帮助确保应用稳定性和兼容性,Android 平台开始限制您的应用可在 Android 9(API 级别 28)中使用哪些非 SDK 接口。Android Q 包含更新后的受限非 SDK 接口列表(基于与 Android 开发者之间的协作以及最新的内部测试)。我们的目标是在限制使用非 SDK 接口之前确保有可用的公开替代方案。

如果您不打算以 Android Q 为目标平台,那么其中一些变更可能不会立即对您产生影响。虽然您目前可以使用灰名单中的一些非 SDK 接口(取决于您应用的目标 API 级别),但如果您使用任何非 SDK 方法或字段,则应用无法运行的风险终归较高。

如果您不确定自己的应用是否使用了非 SDK 接口,则可以测试该应用进行确认。如果您的应用依赖于非 SDK 接口,则应该开始计划迁移到 SDK 替代方案。不过,我们知道某些应用具有使用非 SDK 接口的有效用例。如果您无法为应用中的某项功能找到使用非 SDK 接口的替代方案,则应该请求新的公共 API。

要了解详情,请参阅非 SDK 接口在 Android Q 中的受限情况出现变化以及对非 SDK 接口的限制

NDK

Android Q 包含以下 NDK 方面的变更。

共享对象不得包含文本重定位

Android 6.0(API 级别 23)已禁止在共享对象中使用文本重定位。代码必须按原样加载,且不得修改。此变更可以减少应用的加载时间并提高安全性。

在 Android Q 测试版 1 和 2 中,SELinux 对以 Android 8.0(API 级别 26)及更高版本作为目标版本的应用强制执行此限制。如果此类应用继续使用包含文本重定位的共享对象,则出现故障的风险较高。

系统二进制文件/库已映射到只执行内存

从 Android Q 开始,系统二进制文件和库会映射到只执行(不可读取)内存,作为应对代码重用攻击的安全强化技术。有意或意外读入已标记为只执行的内存段会抛出 SIGSEGV,无论此读入行为是来自错误、漏洞还是有意的内存自省都不例外。

您可以通过检查 /data/tombstones/ 中的相关 tombstone 文件来确定崩溃是否由变更改所导致。与只执行相关的崩溃包含以下中止消息:

Cause: execute-only (no-read) memory access error; likely due to data in .text.

要解决此问题,开发者可以通过调用 mprotect() 将只执行内存段标记为“读取+执行”,例如用于执行内存检查。不过,我们强烈建议您事后将其重新设为只执行,因为这样可以更好地保护您的应用和用户。

对 ptrace 的调用不会受到影响,因此 ptrace 调试也不会受到影响。

安全性

Android Q 包含以下安全性方面的变更。

移除了应用主目录的执行权限

以 Android Q 为目标平台的不受信任的应用无法再针对应用主目录中的文件调用 exec()。这种从可写应用的主目录执行文件的行为违反了 W^X。应用应该仅加载嵌入到应用的 APK 文件中的二进制代码。

此外,以 Android Q 为目标平台的应用无法针对已执行 dlopen() 的文件中的可执行代码进行内存中修改。这包括含有文本重定位的所有共享对象 (.so) 文件。

WLAN 直连广播

在 Android Q 中,以下与 WLAN 直连相关的广播不再具有粘性。

  • WIFI_P2P_CONNECTION_CHANGED_ACTION
  • WIFI_P2P_THIS_DEVICE_CHANGED_ACTION

如果您的应用依赖于在注册时接收这些广播(因为其之前一直具有粘性),请在初始化时使用适当的 get() 方法获取信息。

  • WLAN 感知功能

Android Q 扩大了支持范围,现在可以使用 WLAN 感知数据路径轻松创建 TCP/UDP 套接字。要创建连接到 ServerSocket 的 TCP/UDP 套接字,客户端设备需要知道服务器的 IPv6 地址和端口。这在之前需要通过频外方式进行通信(例如使用 BT 或 WLAN 感知第 2 层消息传递),或者使用其他协议(例如 mDNS)通过频内方式发现。而借助 Android Q,可以将此类消息作为网络设置的一部分进行传递。

服务器可以执行以下任一操作:

  • 初始化 ServerSocket 并设置或获取要使用的端口。
  • 将端口信息指定为 WLAN 感知网络请求的一部分。

以下代码示例显示了如何将端口信息指定为网络请求的一部分:

   ServerSocket ss = new ServerSocket();
    WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier
      .Builder(discoverySession, peerHandle)
      .setPskPassphrase(“some-password”)
      .setPort(ss.getLocalPort())
      .build();

    NetworkRequest myNetworkRequest = new NetworkRequest.Builder()
      .addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE)
      .setNetworkSpecifier(ns)
      .build();
    

然后,客户端会执行 WLAN 感知网络请求来获取服务器提供的 IPv6 和端口:

callback = new ConnectivityManager.NetworkCallback() {
      @Override
      public void onAvailable(Network network) {
        ...
      }
      @Override
      public void onLinkPropertiesChanged(Network network,
          LinkProperties linkProperties) {
        ...
      }
      @Override
      Public void onCapabilitiesChanged(Network network,
          NetworkCapabilities networkCapabilities) {
        ...
        TransportInfo ti = networkCapabilities.getTransportInfo();
        if (ti instanceof WifiAwareNetworkInfo) {
           WifiAwareNetworkInfo info = (WifiAwareNetworkInfo) ti;
           Inet6Address peerAddress = info.getPeerIpv6Addr();
           int peerPort = info.getPort();
        }
      }
      @Override
      public void onLost(Network network) {
        ...
      }
    };

    connMgr.requestNetwork(networkRequest, callback);

Go 设备上的 SYSTEM_ALERT_WINDOW

在 Android Q(Go 版本)设备上运行的应用无法获得 SYSTEM_ALERT_WINDOW 权限。这是因为绘制叠加层窗口会使用过多的内存,这对低内存 Android 设备的性能十分有害。

如果在搭载 Android 9 或更低版本的 Go 版本设备上运行的应用收到了 SYSTEM_ALERT_WINDOW 权限,则会保留此权限,即使设备升级到 Android Q 也不例外。不过,尚不具有此权限的应用在设备升级后便无法获得此权限了。

如果 Go 设备上的应用发送具有 ACTION_MANAGE_OVERLAY_PERMISSION 操作的 intent,则系统会自动拒绝此请求,并将用户转到设置屏幕,上面会显示不允许授予此权限,原因是它会降低设备的速度。如果 Go 设备上的应用调用 Settings.canDrawOverlays(),则此方法始终返回 false。同样,这些限制不适用于在设备升级到 Android Q 之前便已收到 SYSTEM_ALERT_WINDOW 权限的应用。

关于以旧版 Android 系统为目标平台的应用的警告

在 Android Q 中,当用户首次运行以 Android 6.0(API 级别 23)以下的版本为目标平台的任何应用时,Android 平台会向用户发出警告。如果此应用要求用户授予权限,则系统会先向用户提供调整应用权限的机会,然后才会允许此应用首次运行。

由于 Google Play 的目标 API 方面的要求,用户只有在运行最近未更新的应用时才会看到这些警告。对于通过其他商店分发的应用,我们也将于 2019 年引入类似的目标 API 方面的要求。要详细了解这些要求,请参阅在 2019 年扩展目标 API 级别方面的要求。

移除了 SHA-2 CBC 加密套件

以下 SHA-2 CBC 加密套件已从平台中移除:

  • TLS_RSA_WITH_AES_128_CBC_SHA256
  • TLS_RSA_WITH_AES_256_CBC_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384

这些加密套件不如使用 GCM 的类似加密套件安全,并且大多数服务器要么同时支持这些加密套件的 GCM 变体和 CBC 变体,要么二者均不支持。

应用使用情况

Android Q 引入了与应用使用情况相关的以下行为变更:

  • UsageStats 应用使用情况的改进 – 当在分屏或画中画模式下使用应用时,Android Q 现在能够使用 UsageStats准确地跟踪应用使用情况。此外,Android Q 现在可以跟踪免安装应用的使用情况。
  • 按应用开启灰度模式 – Android Q 现在可以将应用设为灰度显示模式。
  • 按应用开启干扰模式 – Android Q现在可以选择性地将应用设为“干扰模式”,此时系统会禁止显示其通知,并且不会将其显示为推荐的应用。
  • 暂停和播放 – 在 Android Q 中,暂停的应用无法再播放音频。

android.preference 库现已弃用

android.preference 库现已弃用。开发者应该改为使用 AndroidX preference 库,这是Android Jetpack 的一部分。如需获取其他资源以帮助进行迁移和开发,请查看经过更新的设置指南以及我们的公开示例应用和参考文档。

摄像头变更

很多使用摄像头的应用都会假定如果设备采用纵向配置,则物理设备也会处于纵向,正如摄像头方向中所述。在过去可以做出这样的假定,但在推出新型设备(例如可折叠设备)后,这发生了变化。针对这些设备做出这样的假定可能导致相机取景器的显示产生错误的旋转和/或缩放。

以 API 级别 24 或更高级别为目标平台的应用应该明确设置 android:resizeableActivity,并提供必要的功能来处理多窗口操作。

电池用量跟踪

从 Android Q 开始,只要在发生重大充电事件之后拔下设备电源插头,SystemHealthManager 就会重置其电池用量统计信息。一般来说,重大充电事件指的是设备电池已充满,或者设备电量经历从几乎耗尽到即将充满。

在 Android Q 之前,无论何时拔下设备电源插头,无论电池电量有多微小的变化,电池用量统计信息都会重置。

Android 9.0 行为变更

电源管理

Android 9 引入了新功能以改善设备电源管理。 这些变更连同 Android 9 之前已存在的功能可帮助确保系统资源被提供给最需要它们的应用。

详情请参阅电源管理

隐私权变更

为了增强用户隐私,Android 9 引入了若干行为变更,如限制后台应用访问设备传感器、限制通过 Wi-Fi 扫描检索到的信息,以及与通话、手机状态和 Wi-Fi 扫描相关的新权限规则和权限组。

无论采用哪一种目标 SDK 版本,这些变更都会影响运行于 Android 9 上的所有应用。

后台对传感器的访问受限
Android 9 限制后台应用访问用户输入和传感器数据的能力。 如果您的应用在运行 Android 9 设备的后台运行,系统将对您的应用采取以下限制:

  • 您的应用不能访问麦克风或摄像头。
  • 使用连续报告模式的传感器(例如加速度计和陀螺仪)不会接收事件。
  • 使用变化或一次性报告模式的传感器不会接收事件。

如果您的应用需要在运行 Android 9 的设备上检测传感器事件,请使用前台服务

限制访问通话记录
Android 9 引入 CALL_LOG 权限组并将 READ_CALL_LOG、WRITE_CALL_LOG 和 PROCESS_OUTGOING_CALLS 权限移入该组。 在之前的 Android 版本中,这些权限位于 PHONE 权限组。

对于需要访问通话敏感信息(如读取通话记录和识别电话号码)的应用,该 CALL_LOG 权限组为用户提供了更好的控制和可见性。

如果您的应用需要访问通话记录或者需要处理去电,则您必须向 CALL_LOG 权限组明确请求这些权限。 否则会发生 SecurityException。

如果您的应用已经遵循运行时权限最佳做法,则可以处理权限组的变更。

限制访问电话号码
在未首先获得 READ_CALL_LOG 权限的情况下,除了应用的用例需要的其他权限之外,运行于 Android 9 上的应用无法读取电话号码或手机状态。

与来电和去电关联的电话号码可在手机状态广播(比如来电和去电的手机状态广播)中看到,并可通过 PhoneStateListener 类访问。 但是,如果没有 READ_CALL_LOG 权限,则 PHONE_STATE_CHANGED 广播和 PhoneStateListener 提供的电话号码字段为空。

要从手机状态中读取电话号码,请根据您的用例更新应用以请求必要的权限:

  • 要通过 PHONE_STATE Intent 操作读取电话号码,同时需要 READ_CALL_LOG 权限和
    READ_PHONE_STATE 权限。
  • 要从 onCallStateChanged() 中读取电话号码,只需要 READ_CALL_LOG 权限。 不需要
    READ_PHONE_STATE 权限。

限制访问 Wi-Fi 位置和连接信息
在 Android 9 中,应用进行 Wi-Fi 扫描的权限要求比之前的版本更严格。 详情请参阅 Wi-Fi 扫描限制。

类似的限制也适用于 getConnectionInfo() 函数,该函数返回描述当前 Wi-Fi 连接的 WifiInfo 对象。 如果调用应用具有以下权限,则只能使用该对象的函数来检索 SSID 和 BSSID 值:

  • ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION
  • ACCESS_WIFI_STATE

检索 SSID 或 BSSID 还需要在设备上启用位置服务(在 Settings > Location 下)。

从 Wi-Fi 服务函数中移除的信息
在 Android 9 中,下列事件和广播不接收用户位置或个人可识别数据方面的信息:

  • WifiManager 中的 getScanResults() 和 getConnectionInfo() 函数。
  • WifiP2pManager 中的 discoverServices() 和 addServiceRequest() 函数。
  • NETWORK_STATE_CHANGED_ACTION 广播。

Wi-Fi 的 NETWORK_STATE_CHANGED_ACTION系统广播不再包含 SSID(之前为 EXTRA_SSID)、BSSID(之前为 EXTRA_BSSID)或连接信息(之前为 EXTRA_NETWORK_INFO)。 如果应用需要此信息,请改为调用 getConnectionInfo()。

电话信息现在依赖设备位置设置
如果用户在运行 Android 9 的设备上停用设备定位,则以下函数不提供结果:

  • getAllCellInfo()
  • listen()
  • getCellLocation()
  • getNeighboringCellInfo()

对使用非 SDK 接口的限制

为帮助确保应用稳定性和兼容性,此平台对某些非 SDK 函数和字段的使用进行了限制;无论您是直接访问这些函数和字段,还是通过反射或 JNI 访问,这些限制均适用。 在 Android 9 中,您的应用可以继续访问这些受限的接口;该平台通过 toast 和日志条目提醒您注意这些接口。 如果您的应用显示这样的 toast,则必须寻求受限接口之外的其他实现策略。 如果您认为没有可行的替代策略,您可以提交错误以请求重新考虑此限制。

对非 SDK 接口的限制包含了更多重要信息。 您应查阅该信息以确保您的应用继续正常工作。

安全行为变更

设备安全性变更

无论应用的目标平台版本如何,Android 9 添加的若干功能均可令应用的安全性得到改善。

传输层安全协议 (TLS) 实现变更
系统的传输层安全协议 (TLS) 实现在 Android 9 中经历了若干次变更:

如果 SSLSocket 的实例在创建时连接失败,系统会引发 IOException 而非 NullPointerException。
SSLEngine 类可正常处理出现的任何 close_notify 提醒。
如需了解有关在 Android 应用中进行安全网络请求的更多信息,请参阅一个 HTTPS 示例。

更严格的 SECCOMP 过滤器
Android 9 对可供应用使用的系统调用做了进一步限制。 此行为是 Android 8.0(API 级别 26)包含的 SECCOMP 过滤器的扩展。

注:此更改仅影响使用授权的系统调用的应用。

加密变更

Android 9 针对加密算法的实现和处理引入了几项变更。

参数和算法的 Conscrypt 实现
Android 9 在 Conscrypt 中实现了更多的算法参数。 这些参数包括: AES、DESEDE、OAEP 和 EC。 这些参数和许多算法的 Bouncy Castle 版本自 Android 9 起已被弃用。

注:EC 参数的 Conscrypt 实现仅支持已命名的曲线。

如果您的应用以 Android 8.1(API 级别 27)或更低版本为目标,则在请求这些已弃用算法之一的 Bouncy Castle 实现时,您将收到一条警告消息。 然而,如果您以 Android 9 为目标平台,则这些请求会各自引发 NoSuchAlgorithmException。

其他变更

Android 9 引入了多项与加密有关的其他变更:

  • 使用 PBE 密钥时,如果 Bouncy Castle 需要初始化矢量 (IV),而您的应用未提供 IV,则会收到一条警告消息。
  • ARC4 加密的 Conscrypt 实现允许您指定 ARC4/ECB/NoPadding 或 ARC4/NONE/NoPadding。
    Crypto Java 加密架构 (JCA) 提供程序现已被移除。 因此,如果您的应用调用
    SecureRandom.getInstance(“SHA1PRNG”, “Crypto”),将会发生NoSuchProviderException。
  • 如果您的应用从大于密钥结构的缓冲区中解析 RSA 密钥,将不会再发生异常。

如需了解有关使用 Android 的加密功能的更多信息,请参阅加密

不再支持 Android 安全加密文件

Android 9 完全取消了对 Android 安全加密文件 (ASEC) 的支持。

在 Android 2.2(API 级别 8)中,Android 引入了 ASEC 以支持 SD 卡加载应用功能。 在 Android 6.0(API 级别 23)上,平台引入了一个可采用的存储设备 技术,开发者可用它来代替 ASEC。

ICU 库更新

Android 9 使用 ICU 库版本 60。 Android 8.0(API 级别 26)和 Android 8.1(API 级别 27)使用 ICU 58。

ICU 用于提供 android.icu package 下的公开 API, 供 Android 平台内部用来提供国际化支持。 例如,它用于实现 java.util、java.text 和 android.text.format 格式的 Android 类。

对 ICU 60 进行的更新包含许多细微但很有用的变更,这包括 Emoji 5.0 数据支持,改进了日期/时间格式,详见 ICU 59 和 ICU 60 版本说明中的介绍。

本次更新中的显著变更:

  • 平台处理时区的方式已发生更改。
  • 平台能够更好地处理 GMT 和 UTC,不再将 UTC 与 GMT 混为一谈。 ICU 现在提供 GMT 和 UTC 的翻译版时区名称。此变更会影响“GMT”、“Etc/GMT”、“Etc/UTC”、“UTC”和“Zulu”之类时区的 android.icu格式和解析行为。
  • java.text.SimpleDateFormat 现在使用 ICU 提供 UTC /GMT 的显示名称,这意味着:
    • 对于许多语言区域而言,设置 zzzz 的格式将会生成很长的本地化字符串。之前,对于 UTC 时区,它会生成“UTC”,而对于GMT,则会生成“GMT+00:00”之类的字符串。
    • 解析 zzzz 可识别“Universal Coordinated Time”和“Greenwich Mean Time”之类的字符串。
    • 在所有语言里,如果应用接受“UTC”或“GMT+00:00”作为 zzzz 的输出,则可能会遇到兼容性问题。
  • java.text.DateFormatSymbols.getZoneStrings() 的行为已变更:
    • 与 SimpleDateFormat 类似,现在,UTC 和 GMT 也有长名称。对于 UTC 时区,DST类型的时区名称(例如“UTC”、“Etc/UTC”和“Zulu”)变为 GMT+00:00(而不是硬编码字符串UTC),这是在没有其他名称可用时的标准回退。
    • 某些时区 ID 被正确地识别为其他地区的同义词,因此,Android 能够查找过时时区 ID(例如
      Eire)对应的字符串,而之前无法解决此问题。
    • 亚洲/河内不再是可识别的时区。 因此,java.util.TimeZones.getAvailableIds()不再返回此值,java.util.TimeZone.getTimeZone() 也不再识别它。此行为与现有的 android.icu行为相符。
  • android.icu.text.NumberFormat.getInstance(ULocale,PLURALCURRENCYSTYLE).parse(String) 函数甚至在解析合法相应币种文本时也会引发ParseException。 通过对PLURALCURRENCYSTYLE 类型的相应币种文本使用自 Android 7.0(API级别 24)以来所提NumberFormat.parseCurrency,可避免此问题。

Android Test 变更

Android 9 引入了多项针对 Android Test 框架库和类结构的更改。 这些变更可帮助开发者使用支持框架的公共 API,此外,在使用第三方库或自定义逻辑构建和运行测试时,这些变更还可提供更大的灵活性。

从框架移除的内容库
Android 9 将基于 JUnit 的类重新整理成三个内容库: android.test.base、android.test.runner 和 android.test.mock。 此变更允许您针对与您的项目依赖项搭配效果最好的 JUnit 版本运行测试。 此版本的 JUnit 可能不同于 android.jar 提供的版本。

如需了解有关如何将基于 JUnit 的类组织到这些内容库中,以及如何准备您的应用项目以编写和运行测试,请参阅针对 Android 测试设置项目。

测试套件版本号变更
移除了 TestSuiteBuilder 类中的 addRequirements() 函数,TestSuiteBuilder 类本身也已弃用。 addRequirements() 函数要求开发者提供类型为隐藏 API 的参数,结果令 API 失效。

Java UTF 解码器

UTF-8 是 Android 中的默认字符集。 UTF-8 字节序列可由 String(byte[] bytes) 之类的 String 构造函数解码。

Android 9 中的 UTF-8 解码器遵循比以前版本中更严格的 Unicode 标准: 这些变更包括:

  • 非最短形式的 UTF-8(例如 <C0, AF>)被视为格式不正确。
  • 替代形式的 UTF-8(例如 U+D800…U+DFFF)被视为格式不正确。
  • 最大的子部分被单个 U+FFFD 取代。 例如,在字节序列“41 C0 AF 41 F4 80 80 41”中,最大子部分为“C0”、“AF”和“F4 80 80”。其中“F4 80 80”可以是“F4 80 80 80”的初始子序列,但“C0”不能是任何形式正确的代码单位序列的初始子序列。因此,输出应为“A\ufffd\ufffdA\ufffdA”。
  • 要在 Android 9 或更高版本中解码修改后的 UTF-8/CESU-8 序列,请使用
    DataInputStream.readUTF() 函数或 NewStringUTF() JNI 函数。

使用证书的主机名验证

RFC 2818中介绍了两种对照证书匹配域名的方法—使用 subjectAltName (SAN) 扩展程序中的可用名称,或者在没有 SAN 扩展程序的情况下,回退到 commonName (CN)。

然而,在 RFC 2818 中,回退到 CN 已被弃用。因此,Android 不再回退到使用 CN。 要验证主机名,服务器必须出示具有匹配 SAN 的证书。 不包含与主机名匹配的 SAN 的证书不再被信任。

网络地址查询可能会导致网络违规

要求名称解析的网络地址查询可能会涉及网络 I/O,因此会被视为阻塞性操作。 对于主线程的阻塞性操作可能会导致停顿或卡顿。

StrictMode 类是一个有助于开发者检测代码问题的开发工具。

在 Android 9 及更高版本中,StrictMode 可以检测需要名称解析的网络地址查询所导致的网络违规。

您在交付应用时不应启用 StrictMode。 否则,您的应用可能会遭遇异常,例如,在使用 detectNetwork() 或 detectAll() 函数获取用于检测网络违规的政策时,会出现NetworkOnMainThreadException。

解析数字 IP 地址不被视为阻塞性操作。 数字 IP 地址解析的工作方式与 Android 9 以前的版本中所采用的方式相同。

套接字标记

在低于 Android 9 的平台版本上,如果使用 setThreadStatsTag() 函数标记某个套接字,则当使用带 ParcelFileDescriptor 容器的 binder 进程间通信将其发送给其他进程时,套接字会被取消标记。

在 Android 9 及更高版本中,利用 binder 进程间通信将套接字发送至其他进程时,其标记将得到保留。 此变更可能影响网络流量统计,例如,使用 queryDetailsForUidTag() 函数时。

如果您要保留以前版本的行为,即取消已发送至其他进程的套接字的标记,您可以在发送此套接字之前调用 untagSocket()。

报告的套接字中可用字节数

在调用 shutdownInput() 函数后,available() 函数会在调用时返回 0。

更详尽的 VPN 网络功能报告
在 Android 8.1(API 级别 28)及更低版本中,NetworkCapabilities 类仅报告 VPN 的有限信息,例如 TRANSPORT_VPN,但会省略 NET_CAPABILITY_NOT_VPN。 信息有限导致难以确定使用 VPN 是否会导致对应用的用户收费。 例如,检查 NET_CAPABILITY_NOT_METERED 并不能确定底层网络是否按流量计费。

从 Android 9 及更高版本开始,当 VPN 调用 setUnderlyingNetworks() 函数时,Android 系统将会合并任何底层网络的传输和能力并返回 VPN 网络的有效网络能力作为结果。

在 Android 9 及更高版本中,已经检查NET_CAPABILITY_NOT_METERED 的应用将收到关于 VPN 网络能力和底层网络的信息。

应用不再能访问 xt_qtaguid 文件夹中的文件

从 Android 9 开始,不再允许应用直接读取 /proc/net/xt_qtaguid 文件夹中的文件。 这样做是为了确保与某些根本不提供这些文件的设备保持一致。

依赖这些文件的公开 API TrafficStats 和 NetworkStatsManager 继续按照预期方式运行。 然而,不受支持的 cutils 函数(例如 qtaguid_tagSocket())在不同设备上可能不会按照预期方式运行 — 甚至根本不运行。

现在强制执行 FLAG_ACTIVITY_NEW_TASK 要求

在 Android 9 中,您不能从非 Activity 环境中启动 Activity,除非您传递 Intent 标志 FLAG_ACTIVITY_NEW_TASK。 如果您尝试在不传递此标志的情况下启动 Activity,则该 Activity 不会启动,系统会在日志中输出一则消息。

注:在 Android 7.0(API 级别 24)之前,标志要求一直是期望的行为并被强制执行。 Android 7.0 中的一个错误会临时阻止实施标志要求。

屏幕旋转变更

从 Android 9 开始,对纵向旋转模式做出了重大变更。 在 Android 8.0(API 级别 26)中,用户可以使用 Quicksettings 图块或 Display 设置在自动屏幕旋转和纵向旋转模式之间切换。 纵向模式已重命名为旋转锁定,它会在自动屏幕旋转关闭时启用。 自动屏幕旋转模式没有任何变更。

当设备处于旋转锁定模式时,用户可将其屏幕锁定到顶层可见 Activity 所支持的任何旋转。 Activity 不应假定它将始终以纵向呈现。 如果顶层 Activity 可在自动屏幕旋转模式下以多种旋转呈现,则应在旋转锁定模式下提供相同的选项,根据 Activity 的 screenOrientation 设置,允许存在一些例外情况(见下表)。

请求特定屏幕方向(例如,screenOrientation=landscape)的 Activity 会忽略用户锁定首选项,并且行为与 Android 8.0 中的行为相同。

可在 Android Manifest 中,或以编程方式通过 setRequestedOrientation() 在 Activity 级别设置屏幕方向首选项。

旋转锁定模式通过设置 WindowManager 在处理 Activity 旋转时使用的用户旋转首选项来发挥作用。 用户旋转首选项可能在下列情况下发生变更。 请注意,恢复设备的自然旋转存在偏差,对于外形与手机类似的设备通常设置为纵向:

  • 当用户接受旋转建议时,旋转首选项变为建议方向。
  • 当用户切换到强制纵向应用(包括锁定屏幕或启动器)时,旋转首选项变为纵向。

下表总结了常见屏幕方向的旋转行为:

屏幕方向行为
未指定、user在自动屏幕旋转和旋转锁定下,Activity 可以纵向或横向(以及颠倒纵向或横向)呈现。 预期同时支持纵向和横向布局。
userLandscape在自动屏幕旋转和旋转锁定下,Activity 可以横向或颠倒横向呈现。 预期只支持横向布局。
userPortrait在自动屏幕旋转和旋转锁定下,Activity 可以纵向或颠倒纵向呈现。 预期只支持纵向布局。
fullUser在自动屏幕旋转和旋转锁定下,Activity 可以纵向或横向(以及颠倒纵向或横向)呈现。 预期同时支持纵向和横向布局。旋转锁定用户将可选择锁定到颠倒纵向,通常为 180º。
sensor、fullSensor、sensorPortrait、sensorLandscape在自动屏幕旋转和旋转锁定下,Activity 可以纵向或横向(以及颠倒纵向或横向)呈现。 预期同时支持纵向和横向布局。

未指定、user 在自动屏幕旋转和旋转锁定下,Activity 可以纵向或横向(以及颠倒纵向或横向)呈现。 预期同时支持纵向和横向布局。
userLandscape 在自动屏幕旋转和旋转锁定下,Activity 可以横向或颠倒横向呈现。 预期只支持横向布局。
userPortrait 在自动屏幕旋转和旋转锁定下,Activity 可以纵向或颠倒纵向呈现。 预期只支持纵向布局。
fullUser 在自动屏幕旋转和旋转锁定下,Activity 可以纵向或横向(以及颠倒纵向或横向)呈现。 预期同时支持纵向和横向布局。

Apache HTTP 客户端弃用影响采用非标准 ClassLoader 的应用

在 Android 6.0 中,我们取消了对 Apache HTTP 客户端的支持。

此变更对大多数不以 Android 9 或更高版本为目标的应用没有任何影响。 不过,此变更会影响使用非标准 ClassLoader 结构的某些应用,即使这些应用不以 Android 9 或更高版本为目标平台。

如果应用使用显式委托到系统 ClassLoader 的非标准 ClassLoader,则应用会受到影响。 在 org.apache.http.* 中查找类时,这些应用需要委托给应用 ClassLoader。 如果它们委托给系统 ClassLoader,则应用在 Android 9 或更高版本上将失败并显示 NoClassDefFoundError,因为系统 ClassLoader 不再识别这些类。 为防止将来出现类似问题,一般情况下,应用应通过应用 ClassLoader 加载类,而不是直接访问系统 ClassLoader。

枚举相机

在 Android 9 设备上运行的应用可以通过调用 getCameraIdList() 发现每个可用的摄像头。 应用不应假定设备只有一个后置摄像头或只有一个前置摄像头。

例如,如果您的应用有一个用来切换前置和后置摄像头的按钮,则设备可能有多个前置或后置摄像头可供选择。 您应浏览一下摄像头列表,检查每个摄像头的特征,然后决定向用户显示哪些摄像头。

Android 8.0 行为变更

后台执行限制

Android 8.0 为提高电池续航时间而引入的变更之一是,当您的应用进入已缓存状态时,如果没有活动的组件,系统将解除应用具有的所有唤醒锁。

此外,为提高设备性能,系统会限制未在前台运行的应用的某些行为。具体而言:

  • 现在,在后台运行的应用对后台服务的访问受到限制。
  • 应用无法使用其清单注册大部分隐式广播(即,并非专门针对此应用的广播)。

默认情况下,这些限制仅适用于针对 O 的应用。不过,用户可以从 Settings 屏幕为任意应用启用这些限制,即使应用并不是以 O 为目标平台。

Android 8.0 还对特定函数做出了以下变更:

  • 如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况下使用 startService() 函数,则该函数将引发一个 IllegalStateException。
  • 新的 Context.startForegroundService() 函数将启动一个前台服务。现在,即使应用在后台运行,系统也允许其调用Context.startForegroundService()。不过,应用必须在创建服务后的五秒内调用该服务的startForeground() 函数。

Android 后台位置限制

为节约电池电量、保持良好的用户体验和确保系统健康运行,在运行 Android 8.0 的设备上使用后台应用时,降低了后台应用接收位置更新的频率。此行为变更会影响包括 Google Play 服务在内的所有接收位置更新的应用。

此类变更会影响以下 API:

  • Fused Location Provider (FLP)
  • Geofencing
  • GNSS Measurements
  • Location Manager
  • Wi-Fi Manager

为确保您的应用按预期方式运行,请完成以下步骤:

  • 查看您的应用的逻辑,并确保您使用的是最新的位置 API。
  • 测试您的应用是否在每个用例中都表现出预期行为。
  • 考虑使用 Fused Location Provider (FLP) 或地理围栏来处理依赖于用户当前位置的用例。

应用快捷键

Android 8.0 对应用快捷方式做出了以下变更:

  • com.android.launcher.action.INSTALL_SHORTCUT广播不再会对您的应用有任何影响,因为它现在是私有的隐式广播。相反,您应使用 ShortcutManager 类中的requestPinShortcut() 函数创建应用快捷方式。
  • 现在,ACTION_CREATE_SHORTCUT Intent 可以创建可使用 ShortcutManager类进行管理的应用快捷方式。此 Intent 还可以创建不与 ShortcutManager 交互的旧版启动器快捷方式。在以前,此
    Intent 只能创建旧版启动器快捷方式。
  • 现在,使用 requestPinShortcut() 创建的快捷方式和在处理
    ACTION_CREATE_SHORTCUT Intent的操作组件中创建的快捷方式均已转换为功能齐全的应用快捷方式。因此,应用现在可以使ShortcutManager中的函数来更新这些快捷方式。
  • 旧版快捷方式仍然保留了它们在旧版 Android 中的功能,但您必须在应用中手动将它们转换成应用快捷方式。

语言区域和国际化

Android 7.0(API 级别 24)引入能指定默认类别语言区域的概念,但是某些 API 在本应使用默认 DISPLAY 类别语言区域时,仍然使用不带参数的通用 Locale.getDefault() 函数。现在,在 Android 8.0 中,以下函数使用 Locale.getDefault(Category.DISPLAY) 来代替 Locale.getDefault():

  • Currency.getDisplayName()
  • Currency.getSymbol()
  • Locale.getDisplayScript()

当为 Locale 参数指定的 displayScript 值不可用时,Locale.getDisplayScript(Locale) 同样回退到 Locale.getDefault()。

与语言区域和国际化有关的其他变更如下:

  • 调用 Currency.getDisplayName(null) 会引发 NullPointerException,以与文档规定的行为保持一致。
  • 时区名称的分析方法发生变化。之前,Android设备使用在启动时取样的系统时钟值,缓存用于分析日期时间的时区名称。因此,如果在启动时或其他较为罕见的情况下系统时钟出错,可能对分析产生负面影响。现在,一般情况下,在分析时区名称时分析逻辑将使用ICU 和当前系统时钟值。此项变更可提供更加准确的结果,如果您的应用使用 SimpleDateFormat 等类,此结果可能与之前Android 版本不同。
  • Android 8.0 将 ICU 的版本更新至版本 58。

提醒窗口

如果应用使用 SYSTEM_ALERT_WINDOW 权限并且尝试使用以下窗口类型之一来在其他应用和系统窗口上方显示提醒窗口:

  • TYPE_PHONE
  • TYPE_PRIORITY_PHONE
  • TYPE_SYSTEM_ALERT
  • TYPE_SYSTEM_OVERLAY
  • TYPE_SYSTEM_ERROR

…那么,这些窗口将始终显示在使用 TYPE_APPLICATION_OVERLAY 窗口类型的窗口下方。如果应用针对的是 Android 8.0,则应用会使用 TYPE_APPLICATION_OVERLAY 窗口类型来显示提醒窗口。

输入和导航

随着 Android 应用出现在 Chrome 操作系统和平板电脑等其他大尺寸设备上,我们看到,用户在 Android 应用中又重新开始使用键盘导航。在 Android 8.0 中,我们又再次使用键盘作为导航输入设备,从而为基于箭头键和 Tab 键的导航构建了一种更可靠并且可预测的模型。

尤其要指出的是,我们对元素焦点行为做出以下变更:

  • 现在,如果您没有为 View 对象(前景或背景图片)定义任何焦点状态颜色,框架会为 View
    设置默认的焦点突出显示颜色。此焦点突出显示标志是基于操作组件主题背景的涟漪图片。

如果您不希望 View 对象在接收焦点时使用此默认突出显示标志,请在包含 View 的布局 XML 文件中将 android:defaultFocusHighlightEnabled 属性设置为 false,或者将 false 传递至应用界面逻辑中的 setDefaultFocusHighlightEnabled()。

  • 要测试键盘输入对界面元素焦点有何影响,您可以启用 Drawing > Show layout bounds 开发者选项。在 Android8.0 中,此选项在当前具有焦点的元素上显示一个“X”图标。

另外,Android 8.0 中的所有工具栏元素自动组成键盘导航键区,用户可以更加轻松地导航进入和离开每个作为一个整体的工具栏。

网页表单自动填充

现在,Android 自动填充框架提供对自动填充功能的内置支持,对于安装到运行 Android 8.0 的设备上的应用,与 WebView 对象相关的下列函数已经发生变化:

WebSettings

  • getSaveFormData() 函数现在返回 false。之前,此函数返回 true。
  • 调用 setSaveFormData() 不再有任何效果。

WebViewDatabase

  • 调用 clearFormData() 不再有任何效果。
  • hasFormData() 函数现在返回 false。之前,当表单包含数据时,此函数返回 true。

无障碍功能

现在,无障碍服务可识别应用的 TextView 对象内部的所有 ClickableSpan 实例。

如需了解有关如何让您的应用更便于访问的更多信息,请参阅无障碍功能。

网络连接和 HTTP(S) 连接

Android 8.0 对网络连接和 HTTP(S) 连接行为做出了以下变更:

  • 无正文的 OPTIONS 请求具有 Content-Length: 0 标头。之前,这些请求没有 Content-Length 标头。

  • HttpURLConnection 在包含斜线的主机或颁发机构名称后面附加一条斜线,使包含空路径的网址规范化。例如,它将http://example.com 转化为 http://example.com/。

  • 通过 ProxySelector.setDefault()设置的自定义代理选择器仅针对所请求的网址(架构、主机和端口)。因此,仅可根据这些值选择代理。传递至自定义代理选择器的网址不包含所请求的网址的路径、查询参数或片段。

  • URI 不能包含空白标签。 之前,平台支持一种权宜方法,即允许主机名称中包含空白标签,但这是对 URI的非法使用。此权宜方法只是为了确保与旧版 libcore 兼容。开发者如果对 API 使用不当,将会看到一条 ADB 消息:“URIexample…com 的主机名包含空白标签。此格式不正确,将不被未来的 Android 版本所接受。”Android 8.0废除了此权宜方法;系统对格式错误的 URI 会返回 null。

  • Android 8.0 在实现 HttpsURLConnection 时不会执行不安全的 TLS/SSL 协议版本回退。

  • 对隧道 HTTP(S) 连接处理进行了如下变更:

    • 在通过连接建立隧道 HTTP(S) 连接时,系统会在 Host 行中正确放置端口号 (:443)并将此信息发送至中间服务器。之前,端口号仅出现在 CONNECT 行中。
  • 系统不再将隧道连接请求中的 user-agent 和 proxy-authorization标头发送至代理服务器。在建立隧道时,系统不再将隧道 Http(s)URLConnection 中的proxy-authorization标头发送至代理。相反,由系统生成 proxy-authorization标头,在代理响应初始请求发送 HTTP 407后将其发送至此代理。

    同样地,系统不再将 user-agent 标头由隧道连接请求复制到建立隧道的代理请求。相反,库为此请求生成 user-agent 标头。

  • 如果之前执行的 connect() 函数失败,send(java.net.DatagramPacket) 函数将会引发
    SocketException。

    • 如果存在内部错误,DatagramSocket.connect() 会引发 pendingSocketException。对于Android 8.0 之前的版本,即使 send() 调用成功,后续的 recv() 调用也会引发 SocketException。为确保一致性,现在这两个调用均会引发 SocketException。
  • 在回退到 TCP Echo 协议之前,InetAddress.isReachable() 会尝试执行 ICMP。

    • 对于某些屏蔽端口 7 (TCP Echo) 的主机(例如 google.com),如果它们接受 ICMP Echo
      协议,现在也许能够访问它们。
    • 对于确实无法访问的主机,此项变更意味着调用需要两倍的时间才能返回结果。

蓝牙

Android 8.0 对 ScanRecord.getBytes() 函数检索的数据长度做出以下变更:

  • getBytes()
    函数对于所接收的字节数不作任何假定。因此,应用不应受所返回的任何最小或最大字节数的影响。相反,应用应当计算所返回数组的长度。
  • 兼容蓝牙 5 的设备返回的数据长度可能会超出之前最大约 60 个字节的限制。
  • 如果远程设备未提供扫描响应,则也可能返回少于 60 个字节的数据。

无缝连接

Android 8.0 对 WLAN 设置进行了多项改进,这样可以更轻松地选择能够提供最佳用户体验的 WLAN 网络。具体变更包括:

  • 稳定性和可靠性改进。
  • 更加直观的界面。
  • 一个合并的 WLAN 首选项菜单。
  • 当附近存在优质的已保存网络时在兼容设备上自动激活 WLAN。

安全性

Android 8.0 包含以下与安全性有关的变更:

  • 此平台不再支持 SSLv3。
  • 在与未正确实现 TLS 协议版本协商的服务器建立 HTTPS 连接时,HttpsURLConnection 不再尝试回退到之前的 TLS协议版本并重试的权宜方法。
  • Android 8.0 将使用安全计算 (SECCOMP) 过滤器来过滤所有应用。允许的系统调用列表仅限于通过 bionic公开的系统调用。此外,还提供了其他几个后向兼容的系统调用,但我们不建议使用这些系统调用。
  • 现在,您的应用的 WebView 对象将在多进程模式下运行。网页内容在独立的进程中处理,此进程与包含应用的进程相隔离,以提高安全性。
  • 您无法再假定 APK 驻留在名称以 -1 或 -2 结尾的目录中。应用应使用 sourceDir 获取此目录,而不能直接使用目录格式。

隐私性

Android 8.0 对平台做出了以下与隐私性有关的变更。

  • 现在,平台改变了标识符的处理方式。

    • 对于在 OTA 之前安装到某个版本 Android 8.0(API 级别 26)的应用,除非在 OTA 后卸载并重新安装,否则ANDROID_ID 的值将保持不变。要在 OTA 后在卸载期间保留值,开发者可以使用密钥/值备份关联旧值和新值。
  • 对于安装在运行 Android 8.0 的设备上的应用,ANDROID_ID的值现在将根据应用签署密钥和用户确定作用域。应用签署密钥、用户和设备的每个组合都具有唯一的 ANDROID_ID值。因此,在相同设备上运行但具有不同签署密钥的应用将不会再看到相同的 Android ID(即使对于同一用户来说,也是如此)。

  • 只要签署密钥相同(并且应用未在 OTA 之前安装到某个版本的 O),ANDROID_ID 的值在软件包卸载或重新安装时就不会发生变化。

  • 即使系统更新导致软件包签署密钥发生变化,ANDROID_ID 的值也不会变化。

    要借助一个简单的标准系统实现应用获利,请使用广告 ID。广告 ID 是 Google Play 服务针对广告服务提供的唯一 ID,此 ID 可由用户重置。

  • 查询 net.hostname 系统属性返回的结果为空。

记录未捕获的异常

如果某个应用安装的 Thread.UncaughtExceptionHandler 未移交给默认的 Thread.UncaughtExceptionHandler,则当出现未捕获的异常时,系统不会终止应用。从 Android 8.0 开始,在此情况下系统将记录异常堆栈跟踪情况;在之前的平台版本中,系统不会记录异常堆栈跟踪情况。

我们建议,自定义 Thread.UncaughtExceptionHandler 实现始终移交给默认处理程序处理;遵循此建议的应用不受 Android 8.0 此项变更的影响。

联系人提供程序使用情况统计方法的变更
在之前版本的 Android 中,联系人提供程序组件允许开发者获取每个联系人的使用情况数据。此使用情况数据揭示了与某个联系人相关联的每个电子邮件地址和每个电话号码的信息,包括与该联系人联系的次数以及上次联系该联系人的时间。请求 READ_CONTACTS 权限的应用可以读取此数据。

如果应用请求 READ_CONTACTS 权限,它们仍可以读取此数据。从 Android 8.0 开始,使用情况数据查询会返回近似值,而不是精确值。不过,Android 系统内部仍然会保留精确值,因此,此变更不会影响 auto-complete API。

此行为变更会影响以下查询参数:

  • TIMES_CONTACTED
  • TIMES_USED
  • LAST_TIME_CONTACTED
  • LAST_TIME_USED

集合的处理

现在,AbstractCollection.removeAll() 和 AbstractCollection.retainAll() 始终引发 NullPointerException;之前,当集合为空时不会引发 NullPointerException。此项变更使行为符合文档要求。

Android 企业版

Android 8.0 更改了企业应用(包括设备规范控制器 (DPC))的某些 API 和功能的行为。这些变更包括:

  • 新增多种行为,帮助应用支持完全托管设备中的工作资料。
  • 变更系统更新处理、应用验证和身份验证方式,以提高设备和系统的完整性。
  • 改进用户在配置、通知、“最近使用的应用”屏幕和 Always on VPN 方面的体验。

针对 Android 8.0 的应用

这些行为变更专门应用于针对 O 平台或更高平台版本的应用。针对 Android 8.0 或更高平台版本进行编译,或将 targetSdkVersion 设为 Android 8.0 或更高版本的应用开发者必须修改其应用以正确支持这些行为(如果适用)。

提醒窗口

使用 SYSTEM_ALERT_WINDOW 权限的应用无法再使用以下窗口类型来在其他应用和系统窗口上方显示提醒窗口:

  • TYPE_PHONE
  • TYPE_PRIORITY_PHONE
  • TYPE_SYSTEM_ALERT
  • TYPE_SYSTEM_OVERLAY
  • TYPE_SYSTEM_ERROR

相反,应用必须使用名为 TYPE_APPLICATION_OVERLAY 的新窗口类型。

使用 TYPE_APPLICATION_OVERLAY 窗口类型显示应用的提醒窗口时,请记住新窗口类型的以下特性:

  • 应用的提醒窗口始终显示在状态栏和输入法等关键系统窗口的下面。
  • 系统可以移动使用 TYPE_APPLICATION_OVERLAY 窗口类型的窗口或调整其大小,以改善屏幕显示效果。
  • 通过打开通知栏,用户可以访问设置来阻止应用显示使用 TYPE_APPLICATION_OVERLAY 窗口类型显示的提醒窗口。

内容变更通知

Android 8.0 更改了 ContentResolver.notifyChange() 和 registerContentObserver(Uri, boolean, ContentObserver) 在针对 Android 8.0 的应用中的行为方式。

现在,这些 API 需要在所有 URI 中为颁发机构定义一个有效的 ContentProvider。使用相关权限定义一个有效的 ContentProvider 可帮助您的应用防范来自恶意应用的内容变更,并防止将可能的私密数据泄露给恶意应用。

视图焦点

可点击的 View 对象现在默认也可以成为焦点。如果您希望 View 对象可点击但不可成为焦点,请在包含 View 的布局 XML 文件中将 android:focusable 属性设置为 false,或者将 false 传递至应用界面逻辑中的 setFocusable()。

安全性

如果您的应用的网络安全性配置选择退出对明文流量的支持,那么您的应用的 WebView 对象无法通过 HTTP 访问网站。每个 WebView 对象必须转而使用 HTTPS。

有关提升应用安全性的其他准则,请参阅面向 Android 开发者的安全性。

帐号访问和可检测性

除非身份验证器拥有用户帐号或用户授予访问权限,否则,应用将无法再访问用户帐号。仅拥有 GET_ACCOUNTS 权限尚不足以访问用户帐号。要获得帐号访问权限,应用应使用 AccountManager.newChooseAccountIntent() 或特定于身份验证器的函数。获得帐号访问权限后,应用可以调用 AccountManager.getAccounts() 来访问帐号。

Android 8.0 已弃用 LOGIN_ACCOUNTS_CHANGED_ACTION。相反,应用在运行时应使用 addOnAccountsUpdatedListener() 获取帐号更新信息。

有关新增 API 和增加的帐号访问和可检测性函数的信息,请参阅此文档的“新增 API”部分中的帐号访问和可检测性。

隐私性

以下变更影响 Android 8.0 的隐私性。

  • 系统属性 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权限。
  • LauncherApps API 不再允许工作资料应用获取有关主个人资料的信息。当某个用户在托管配置文件中时,LauncherAppsAPI 的行为就像同一配置文件组的其他配置文件中未安装任何应用一样。和之前一样,尝试访问无关联的个人资料会引发SecurityExceptions。

权限

在 Android 8.0 之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用。

对于针对 Android 8.0 的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而,一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。

例如,假设某个应用在其清单中列出 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。应用请求 READ_EXTERNAL_STORAGE,并且用户授予了该权限。如果该应用针对的是 API 级别 24 或更低级别,系统还会同时授予 WRITE_EXTERNAL_STORAGE,因为该权限也属于同一 STORAGE 权限组并且也在清单中注册过。如果该应用针对的是 Android 8.0,则系统此时仅会授予 READ_EXTERNAL_STORAGE;不过,如果该应用后来又请求 WRITE_EXTERNAL_STORAGE,则系统会立即授予该权限,而不会提示用户。

媒体

框架会执行音频闪避。进行 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 时,应用不会失去焦点。新的 API 适用于需要暂停而不是闪避的应用。请注意,此行为无法在 Android 8.0 1 版本中实现。
当用户打电话时,活动的媒体流将在通话期间静音。
所有与音频相关的 API 都应使用 AudioAttributes 而不是音频流类型来说明音频播放用例。仅为音量控制继续使用音频流类型。流类型(例如,已弃用的 AudioTrack constructor)的其他用途仍然有效,但是系统会将其记录为错误。
使用 AudioTrack 时,如果应用请求了足够大的音频缓冲区,则框架将尝试使用深度缓冲区输出(如果可用)。

原生库

在针对 Android 8.0 的应用中,如果原生库包含任何可写且可执行的加载代码段,则不会再加载原生库。倘若某些应用的原生库包含不正确的加载代码段,则此变更可能会导致这些应用停止工作。这是一种安全加强措施。

如需了解详细信息,请参阅可写且可执行的代码段。

与早期的开发者预览版相同,Android 8.0 还有助于更轻松地发现所有与链接器有关的问题。链接器的变更绑定到应用的目标 API 级别。如果应用的目标 API 级别发生链接器变更,则该应用无法加载该库。如果您的目标 API 级别低于发生链接器变更的 API 级别,则 logcat 会显示一条警告消息。在预览版期间,与链接器有关的问题不仅会显示在 logcat 中,也会以 toast 的形式显示。对于特定的 API 级别,警告可能会变成错误,此变更有助于提前发现此类问题。

集合的处理

在 Android 8.0 中,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.toArray()、Arrays.sort() 和
    ListIterator.set() 的基础上构建的实现重写 List.sort()。例如:

@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);
  }
}

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);
  }
}

在大多数情况下,您也可以使用根据 API 级别委托给其他默认实现的实现重写 List.sort()。例如:

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

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

如果您选择后者只是因为您希望开发一种适用于所有 API 级别的 sort() 函数,可以考虑赋予其一个唯一的名称,例如 sortCompat(),而不是重写 sort()。

  • 现在,Collections.sort() 只是对调用 sort() 的 List 实现进行的一项结构性修改。例如,在 Android
    8.0 之前的平台版本中,如果通过调用 List.sort() 进行排序,则当迭代处理 ArrayList 以及在迭代过程中调用 sort()时,会引发 ConcurrentModificationException。而 Collections.sort() 则不会引发异常。

    此项变更使平台行为更加一致:现在,两种方法都会引发 ConcurrentModificationException。

类加载行为

Android 8.0 检查确保类加载器在加载新类时不会违反运行时假设条件。不论类引用自 Java(来自 forName())、Dalvik 字节码还是 JNI,都会执行这些检查。平台不会拦截 Java 对 loadClass() 函数的直接调用,也不会检查此类调用的结果。此行为不应影响运行良好的类加载器的正常运行。

平台将检查类加载器返回的类描述符是否与预期的描述符一致。如果返回的描述符与预期不符,平台会引发 NoClassDefFoundError 错误,并在异常日志中存储一条注明不一致之处的详细错误消息。

平台还检查请求的类描述符是否有效。此检查捕获间接加载诸如 GetFieldID() 等类的 JNI 调用,向这些类传递无效的描述符。例如,找不到包含 java/lang/String 签名的字段,是因为此签名无效;它应为 Ljava/lang/String;。

这与 JNI 对 FindClass() 的调用不同,其中 java/lang/String 是一个有效的完全限定名称。

Android 8.0 不支持多个类加载器同时尝试使用相同的 DexFile 对象来定义类。尝试进行此操作,会导致 Android 运行时引发 InternalError 错误,同时显示消息“Attempt to register dex file with multiple class loaders”。

DexFile API 现已弃用,强烈建议您改为使用此平台的类加载器之一,包括 PathClassLoader 或 BaseDexClassLoader。

注: 您可以创建多个引用文件系统中同一个 APK 或 JAR 文件容器的类加载器。这样做通常不会占用大量内存:如果存储而不压缩容器中的 DEX 文件,平台可以对此类文件执行 mmap 操作,而不直接提取它们。但是,如果平台必须从容器中提取 DEX 文件,以这种方式引用 DEX 文件可能占用大量内存。

在 Android 中,所有类加载器都被视为支持并行运行。当多个线程争用同一个类加载器加载相同的类时,第一个完成此操作的线程胜出,而操作结果将用于其他线程。无论类加载器是返回同一个类、返回不同的类还是引发异常,都将发生此行为。该平台静默忽略此类异常。

Android 7.0 行为变更

电池和内存

Android 7.0 包括旨在延长设备电池寿命和减少 RAM 使用的系统行为变更。这些变更可能会影响您的应用访问系统资源,以及您的应用通过特定隐式 intent 与其他应用交互的方式。

  • 低电耗模式

    Android 6.0(API 级别 23)引入了低电耗模式,当用户设备未插接电源、处于静止状态且屏幕关闭时,该模式会推迟 CPU和网络活动,从而延长电池寿命。而 Android 7.0则通过在设备未插接电源且屏幕关闭状态下、但不一定要处于静止状态(例如用户外出时把手持式设备装在口袋里)时应用部分 CPU和网络限制,进一步增强了低电耗模式。
    在这里插入图片描述

    图1,低电耗模式如何应用第一级系统活动限制以延长电池寿命的图示。

当设备处于充电状态且屏幕已关闭一定时间后,设备会进入低电耗模式并应用第一部分限制:关闭应用网络访问、推迟作业和同步。如果进入低电耗模式后设备处于静止状态达到一定时间,系统则会对 PowerManager.WakeLock、AlarmManager 闹铃、GPS 和 WLAN 扫描应用余下的低电耗模式限制。无论是应用部分还是全部低电耗模式限制,系统都会唤醒设备以提供简短的维护时间窗口,在此窗口期间,应用程序可以访问网络并执行任何被推迟的作业/同步。
在这里插入图片描述
图2,低电耗模式如何在设备处于静止状态达到一定时间后应用第二级系统活动限制的图示。

请注意,激活屏幕或插接设备电源时,系统将退出低电耗模式并移除这些处理限制。此项新增的行为不会影响有关使您的应用适应 Android 6.0(API 级别 23)中所推出的旧版本低电耗模式的建议和最佳做法,如对低电耗模式和应用待机模式进行针对性优化中所讨论。您仍应遵循这些建议(例如使用 Google 云消息传递 (GCM) 发送和接收消息)并开始安排更新计划以适应新增的低电耗模式行为。

Project Svelte 后台优化

Android 7.0 移除了三项隐式广播,以帮助优化内存使用和电量消耗。此项变更很有必要,因为隐式广播会在后台频繁启动已注册侦听这些广播的应用。删除这些广播可以显著提升设备性能和用户体验。

移动设备会经历频繁的连接变更,例如在 WLAN 和移动数据之间切换时。目前,可以通过在应用清单中注册一个接收器来侦听隐式 CONNECTIVITY_ACTION 广播,让应用能够监控这些变更。由于很多应用会注册接收此广播,因此单次网络切换即会导致所有应用被唤醒并同时处理此广播。

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

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

  • 面向 Android 7.0 开发的应用不会收到 CONNECTIVITY_ACTION广播,即使它们已有清单条目来请求接受这些事件的通知。在前台运行的应用如果使用 BroadcastReceiver请求接收通知,则仍可以在主线程中侦听 CONNECTIVITY_CHANGE。
  • 应用无法发送或接收 ACTION_NEW_PICTURE 或 ACTION_NEW_VIDEO广播。此项优化会影响所有应用,而不仅仅是面向 Android 7.0 的应用。

如果您的应用使用任何 intent,您仍需要尽快移除它们的依赖关系,以正确适配 Android 7.0 设备。Android 框架提供多个解决方案来缓解对这些隐式广播的需求。例如,JobScheduler API 提供了一个稳健可靠的机制来安排满足指定条件(例如连入无限流量网络)时所执行的网络操作。您甚至可以使用 JobScheduler 来适应内容提供程序变化。

如需了解有关 Android N 中后台优化以及如何改写应用的详细信息,请参阅后台优化。

权限更改

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

系统权限更改

为了提高私有文件的安全性,面向 Android 7.0 或更高版本的应用私有目录被限制访问 (0700)。此设置可防止私有文件的元数据泄漏,如它们的大小或存在性。此权限更改有多重副作用:

  • 私有文件的文件权限不应再由所有者放宽,为使用 MODE_WORLD_READABLE 和/或MODE_WORLD_WRITEABLE而进行的此类尝试将触发 SecurityException。
  • 传递软件包网域外的 file:// URI 可能给接收器留下无法访问的路径。因此,尝试传递 file:// URI 会触发FileUriExposedException。分享私有文件内容的推荐方法是使用 FileProvider。
  • DownloadManager 不再按文件名分享私人存储的文件。旧版应用在访问 COLUMN_LOCAL_FILENAME时可能出现无法访问的路径。面向 Android 7.0 或更高版本的应用在尝试访问 COLUMN_LOCAL_FILENAME 时会触发SecurityException。通过使用
    DownloadManager.Request.setDestinationInExternalFilesDir() 或
    DownloadManager.Request.setDestinationInExternalPublicDir()将下载位置设置为公共位置的旧版应用仍可以访问 COLUMN_LOCAL_FILENAME 中的路径,但是我们强烈反对使用这种方法。对于由DownloadManager 公开的文件,首选的访问方式是使用ContentResolver.openFileDescriptor()。

在应用间共享文件

对于面向 Android 7.0 的应用,Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file:// URI。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现故障,并出现 FileUriExposedException 异常。

要在应用间共享文件,您应发送一项 content:// URI,并授予 URI 临时访问权限。进行此授权的最简单方式是使用 FileProvider 类。

Android 6.0 行为变更

运行时权限

此版本引入了一种新的权限模式,如今,用户可直接在运行时管理应用权限。这种模式让用户能够更好地了解和控制权限,同时为应用开发者精简了安装和自动更新过程。用户可为所安装的各个应用分别授予或撤销权限。

对于以 Android 6.0(API 级别 23)或更高版本为目标平台的应用,请务必在运行时检查和请求权限。要确定您的应用是否已被授予权限,请调用新增的 checkSelfPermission() 方法。要请求权限,请调用新增的 requestPermissions() 方法。即使您的应用并不以 Android 6.0(API 级别 23)为目标平台,您也应该在新权限模式下测试您的应用。

如需了解有关在您的应用中支持新权限模式的详情,请参阅使用系统权限。如需了解有关如何评估新模式对应用的影响的提示,请参阅权限最佳做法。

低电耗模式和应用待机模式

此版本引入了针对空闲设备和应用的最新节能优化技术。这些功能会影响所有应用,因此请务必在这些新模式下测试您的应用。

  • 低电耗模式:如果用户拔下设备的电源插头,并在屏幕关闭后的一段时间内使其保持不活动状态,设备会进入低电耗模式,在该模式下设备会尝试让系统保持休眠状态。在该模式下,设备会定期短时间恢复正常工作,以便进行应用同步,还可让系统执行任何挂起的操作。
  • 应用待机模式:应用待机模式允许系统判定应用在用户未主动使用它时处于空闲状态。当用户有一段时间未触摸应用时,系统便会作出此判定。如果拔下了设备电源插头,系统会为其视为空闲的应用停用网络访问以及暂停同步和作业。

要详细了解这些节能变更,请参阅对低电耗模式和应用待机模式进行针对性优化。

取消支持 Apache HTTP 客户端

Android 6.0 版移除了对 Apache HTTP 客户端的支持。如果您的应用使用该客户端,并以 Android 2.3(API 级别 9)或更高版本为目标平台,请改用 HttpURLConnection 类。此 API 效率更高,因为它可以通过透明压缩和响应缓存减少网络使用,并可最大限度降低耗电量。要继续使用 Apache HTTP API,您必须先在 build.gradle 文件中声明以下编译时依赖项:

android {
    useLibrary 'org.apache.http.legacy'
}

BoringSSL

Android 正在从使用 OpenSSL 库转向使用 BoringSSL 库。如果您要在应用中使用 Android NDK,请勿链接到并非 NDK API 组成部分的加密库,如 libcrypto.so 和 libssl.so。这些库并非公共 API,可能会在不同版本和设备上毫无征兆地发生变化或出现故障。此外,您还可能让自己暴露在安全漏洞的风险之下。请改为修改原生代码,以通过 JNI 调用 Java 加密 API,或静态链接到您选择的加密库。

硬件标识符访问权

为给用户提供更严格的数据保护,从此版本开始,对于使用 WLAN API 和 Bluetooth API 的应用,Android 移除了对设备本地硬件标识符的编程访问权。WifiInfo.getMacAddress() 方法和 BluetoothAdapter.getAddress() 方法现在会返回常量值 02:00:00:00:00:00。

现在,要通过蓝牙和 WLAN 扫描访问附近外部设备的硬件标识符,您的应用必须拥有 ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION 权限。

  • WifiManager.getScanResults()
  • BluetoothDevice.ACTION_FOUND
  • BluetoothLeScanner.startScan()

注:当运行 Android 6.0(API 级别 23)的设备发起后台 WLAN 或蓝牙扫描时,在外部设备看来,该操作的发起来源是一个随机化 MAC 地址。

通知

此版本移除了 Notification.setLatestEventInfo() 方法。请改用 Notification.Builder 类来构建通知。要重复更新通知,请重复使用 Notification.Builder 实例。调用 build() 方法可获取更新后的 Notification 实例。

adb shell dumpsys notification 命令不再打印输出您的通知文本。请改用 adb shell dumpsys notification --noredact 命令打印输出 notification 对象中的文本。

音频管理器变更

不再支持通过 AudioManager 类直接设置音量或将特定音频流静音。setStreamSolo() 方法已弃用,您应该改为调用 requestAudioFocus() 方法。类似地,setStreamMute() 方法也已弃用,请改为调用 adjustStreamVolume() 方法并传入方向值 ADJUST_MUTE 或 ADJUST_UNMUTE。

文本选择

现在,当用户在您的应用中选择文本时,您可以在一个浮动工具栏中显示“剪切”、“复制”和“粘贴”等文本选择操作。其在用户交互实现上与为单个视图启用上下文操作模式中所述的上下文操作栏类似。

要实现可用于文本选择的浮动工具栏,请在您的现有应用中做出以下更改:

  1. 在 View 对象或 Activity 对象中,将 ActionMode 调用从 startActionMode(Callback)
    更改为 startActionMode(Callback, ActionMode.TYPE_FLOATING)。
  2. 改为使用 ActionMode.Callback 的现有实现扩展 ActionMode.Callback2。
  3. 替代onGetContentRect() 方法,用于提供 Rect 内容对象(如文本选择矩形)在视图中的坐标。
  4. 如果矩形的定位不再有效,并且这是唯一需要声明为无效的元素,请调用 invalidateContentRect() 方法。

请注意,如果您使用 Android 支持库 22.2 修订版,浮动工具栏不向后兼容,默认情况下 appcompat 会获得对 ActionMode 对象的控制权。这会禁止显示浮动工具栏。要在 ActionMode 中启用 AppCompatActivity 支持,请调用 getDelegate(),然后对返回的 setHandleNativeActionModesEnabled() 对象调用 AppCompatDelegate,并将输入参数设置为 false。此调用会将 ActionMode 对象的控制权交还给框架。在运行 Android 6.0(API 级别 23)的设备上,框架可以支持 ActionBar 模式或浮动工具栏模式;而在运行 Android 5.1(API 级别 22)或之前版本的设备上,框架仅支持 ActionBar 模式。

浏览器书签变更

此版本移除了对全局书签的支持。android.provider.Browser.getAllBookmarks() 和 android.provider.Browser.saveBookmark() 方法现已移除。同样,READ_HISTORY_BOOKMARKS 权限和 WRITE_HISTORY_BOOKMARKS 权限也已移除。如果您的应用以 Android 6.0(API 级别 23)或更高版本为目标平台,请勿从全局提供程序访问书签或使用书签权限。您的应用应改为在内部存储书签数据。

Android 密钥库变更

从此版本开始,Android 密钥库提供程序不再支持 DSA。但仍支持 ECDSA。

停用或重置安全锁定屏幕时(例如,由用户或设备管理员执行此类操作时),系统将不再删除需要闲时加密的密钥,但在上述事件期间会删除需要闲时加密的密钥。

WLAN 和网络连接变更

此版本对 WLAN API 和 Networking API 引入了以下行为变更。

  • 现在,您的应用只能更改由您创建的 WifiConfiguration 对象的状态。系统不允许您修改或删除由用户或其他应用创建的WifiConfiguration 对象。
  • 在之前的版本中,如果应用利用带有 disableAllOthers=true 设置的 enableNetwork() 强制设备连接特定WLAN 网络,设备将会断开与移动数据网络等其他网络的连接。在此版本中,设备不再断开与上述其他网络的连接。如果您的应用的 targetSdkVersion 为 “20” 或更低,则会固定连接所选 WLAN 网络。如果您的应用的 targetSdkVersion为 “21” 或更高,请使用多网络 API(如 openConnection()、bindSocket() 和新增的bindProcessToNetwork() 方法)来确保通过所选网络传送网络流量。

相机服务变更

在此版本中,相机服务中共享资源的访问模式已从之前的“先到先得”访问模式更改为高优先级进程优先的访问模式。对服务行为的变更包括:

  • 根据客户端应用进程的“优先级”授予对相机子系统资源的访问权,包括打开和配置相机设备。带有对用户可见 Activity 或前台
    Activity 的应用进程一般会被授予较高的优先级,从而使相机资源的获取和使用更加可靠;
  • 当高优先级的应用尝试使用相机时,系统可能会“驱逐”正在使用相机客户端的低优先级应用。在已弃用的 Camera API
    中,这会导致系统为被驱逐的客户端调用 onError()。在 Camera2 API 中,这会导致系统为被驱逐的客户端调用 onDisconnected();
  • 在配备相应相机硬件的设备上,不同的应用进程可同时独立打开和使用不同的相机设备。但现在,如果在多进程用例中同时访问相机会造成任何打开的相机设备的性能或能力严重下降,相机服务会检测到这种情况并禁止同时访问。即使并没有其他应用直接尝试访问同一相机设备,此变更也可能导致低优先级客户端被“驱逐”。
  • 更改当前用户会导致之前用户帐户拥有的应用内活动相机客户端被驱逐。对相机的访问仅限于访问当前设备用户拥有的用户个人资料。举例来说,这意味着,当用户切换到其他帐户后,“来宾”帐户实际上无法让使用相机子系统的进程保持运行状态。

运行时

ART 运行时环境现在可正确实现 newInstance() 方法的访问规则。此变更修正了之前版本中 Dalvik 无法正确检查访问规则的问题。如果您的应用使用 newInstance() 方法,并且您想重写访问检查,请调用 setAccessible() 方法(将输入参数设置为 true)。如果您的应用使用 v7 appcompat 库或 v7 recyclerview 库,则您必须更新应用以使用这些库的最新版本。否则,请务必更新从 XML 引用的任何自定义类,以便能够访问它们的类构造函数。

此版本更新了动态链接程序的行为。动态链接程序现在可以识别库的 soname 与其路径之间的差异(公开错误 6670),并且现在已实现了按 soname 搜索。之前包含错误的 DT_NEEDED 条目(通常是开发计算机文件系统上的绝对路径)却仍工作正常的应用,如今可能会出现加载失败。

现已正确实现 dlopen(3) RTLD_LOCAL 标记。请注意,RTLD_LOCAL 是默认值,因此不显式使用 RTLD_LOCAL 的 dlopen(3) 调用将受到影响(除非您的应用显式使用 RTLD_GLOBAL)。使用 RTLD_LOCAL 时,在随后通过调用 dlopen(3) 加载的库中并不能使用这些符号(这与由 DT_NEEDED 条目引用的情况截然不同)。

在之前版本的 Android 上,如果您的应用请求系统加载包含文本重定位信息的共享库,系统会显示警告,但仍允许加载共享库。从此版本开始,如果您的应用的目标 SDK 版本为 23 或更高,则系统会拒绝加载该库。为帮助您检测库是否加载失败,您的应用应该记录 dlopen(3) 失败日志,并在日志中加入 dlerror(3) 调用返回的问题描述文本。要详细了解如何处理文本重定位,请参阅此指南。

APK 验证

该平台现在执行的 APK 验证更为严格。如果在清单中声明的文件在 APK 中并不存在,该 APK 将被视为已损坏。移除任何内容后必须重新签署 APK。

USB 连接

默认情况下,现在通过 USB 端口进行的设备连接设置为仅充电模式。要通过 USB 连接访问设备及其内容,用户必须明确地为此类交互授予权限。如果您的应用支持用户通过 USB 端口与设备进行交互,请将必须显式启用交互考虑在内。

Android for Work 变更

此版本包含下列针对 Android for Work 的行为变更:

  • 个人上下文中的工作联系人。Google 拨号器通话记录现在会在用户查看通话记录时显示工作联系人。将setCrossProfileCallerIdDisabled() 设置为 true 可在 Google拨号器通话记录中隐藏托管配置文件联系人。仅当您将 setBluetoothContactSharingDisabled() 设置为false 时,才可以通过蓝牙将工作联系人随个人联系人一起显示给设备。默认情况下,它设置为 true。
  • WLAN 配置删除:现在,当删除某个托管配置文件时,将会移除由配置文件所有者添加的 WLAN 配置(例如,通过调用addNetwork() 方法添加的配置)。
  • WLAN 配置锁定:如果 WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN
    不为零,则用户无法再修改或删除任何由活动设备所有者创建的 WLAN 配置。用户仍可创建和修改其自己的 WLAN配置。活动设备所有者拥有编辑或删除任何 WLAN 配置(包括并非由其创建的配置)的权限。
  • 通过添加 Google 帐户下载设备规范控制器:向托管环境以外的设备添加需要通过设备规范控制器 (DPC) 应用管理的 Google帐户时,帐户添加流程现在会提示用户安装相应的 WPC。在设备初始设置向导中通过 Settings > Accounts添加帐户时,也会出现此行为。
  • 对特定 DevicePolicyManager API 行为的变更:
    • 调用 setCameraDisabled() 方法只会影响调用该方法的用户的相机;从托管配置文件调用它不会影响主用户运行的相机应用。
    • 此外,setKeyguardDisabledFeatures() 方法现在除了可供设备所有者使用外,还可供配置文件所有者使用。
    • 配置文件所有者可设置以下键盘锁限制:
      • KEYGUARD_DISABLE_TRUST_AGENTS 和KEYGUARD_DISABLE_FINGERPRINT,它们影响配置文件上级用户的键盘锁设置。
      • KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS,它只影响应用在托管配置文件中生成的通知。
    • DevicePolicyManager.createAndInitializeUser() 方法和
      DevicePolicyManager.createUser() 方法已弃用。
    • 当给定用户的应用在前台运行时,setScreenCaptureDisabled() 方法现在也会屏蔽辅助结构。
    • EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM 现在默认为SHA-256。出于向后兼容性考虑,仍然支持SHA-1,但未来将会取消该支持。EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM现在只接受 SHA-256。
    • Android 6.0(API 级别 23)中曾经存在的 Device initializer API 现已删除
    • EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS 已删除,因此 NFC
      占位配置无法通过编程解锁受恢复出厂设置保护的设备
    • 您现在可以使用 EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE extra 在对托管设备进行 NFC配置期间向设备所有者应用传递数据。
    • Android for Work API 针对 M 运行时权限(包括 Work 配置文件、辅助层及其他内容)进行了优化。新增的DevicePolicyManager 权限 API 不会影响 M 之前版本的应用。
    • 当用户退出通过 ACTION_PROVISION_MANAGED_PROFILE 或
      ACTION_PROVISION_MANAGED_DEVICE intent 发起的设置流程的同步部分时,系统现在会返回RESULT_CANCELED 结果代码。
  • 对其他 API 的变更:
    • 流量消耗:android.app.usage.NetworkUsageStats 类已重命名为 NetworkStats。
  • 对全局设置的变更:
    • 这些设置不再通过 setGlobalSettings() 进行设置:
      • BLUETOOTH_ON
      • DEVELOPMENT_SETTINGS_ENABLED
      • MODE_RINGER
      • NETWORK_PREFERENCE
      • WIFI_ON
    • 这些全局设置现在可通过 setGlobalSettings() 进行设置:
      • WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN

Android 5.0 行为变更

Android Runtime (ART)

在 Android 5.0 中,ART 运行时取代 Dalvik 成为平台默认设置。Android 4.4 中已引入处于实验阶段的 ART 运行时。

有关 ART 的新功能概述,请参阅 ART 简介。部分主要的新功能包括:

  • 预先 (AOT) 编译
  • 改进的垃圾回收 (GC)
  • 改进的调试支持

大多数 Android 应用无需任何更改就可以在 ART 下工作。不过,部分适合 Dalvik 的技术并不适用于 ART。如需了解有关最重要问题的信息,请参阅在 Android Runtime (ART) 上验证应用行为。如存在以下情况,应特别注意:

  • 您的应用使用 Java 原生接口 (JNI) 运行 C/C++ 代码。
  • 您使用生成非标准代码的开发工具(例如,一些代码混淆工具)。
  • 您使用与压缩垃圾回收不兼容的技术。

Material Design 样式

在白色(或非常浅)的背景上使用深色文本绘制通知,以便与新的 Material Design 小部件匹配。请确保您的所有通知都与新的配色方案协调一致。如果您的通知看上去不协调,请进行修正:

  • 使用 setColor() 在您的图标图像后面的圆形中设置重点色彩。
  • 更新或移除使用色彩的资源。系统在操作图标和主要通知图标中忽略所有非阿尔法通道。您应假设这些图标仅支持阿尔法通道。系统用白色绘制通知图标,用深灰色绘制操作图标。

声音和振动

如果您当前使用 Ringtone、MediaPlayer 或 Vibrator 类向通知中添加声音和振动,则移除此代码,以便系统可以在“优先”模式中正确显示通知。取而代之的是,使用 Notification.Builder 方法添加声音和振动。

将设备设为 RINGER_MODE_SILENT 可使设备进入新的优先模式。如果您将设备设为 RINGER_MODE_NORMAL 或 RINGER_MODE_VIBRATE,则设备将退出优先模式。

以前,Android 使用 STREAM_MUSIC 作为主流式传输来控制平板电脑设备上的音量。在 Android 5.0 中,手机和平板电脑设备的主音量流式传输现已合并,由 STREAM_RING 或 STREAM_NOTIFICATION 进行控制。

锁定屏幕可见性

默认情况下,在 Android 5.0 中,通知现在显示在用户的锁定屏幕上。用户可以选择保护敏感信息不被公开,在此情况下,系统会自动删减通知显示的文本。要自定义此删减的通知,请使用 setPublicVersion()。

如果通知不包含个人信息,或者您想允许媒体播放控件显示在通知上,则调用 setVisibility() 方法并将通知的可见性级别设为 VISIBILITY_PUBLIC。

媒体播放

如果您要实现显示媒体播放状态或传输控件的通知,请考虑使用新的 Notification.MediaStyle 模板,而不是自定义 RemoteViews.RemoteView 对象。无论您选择使用哪个方法,请务必将通知的可见性设为 VISIBILITY_PUBLIC,以便可通过锁定屏幕访问您的控件。请注意,从 Android 5.0 开始,系统不再将 RemoteControlClient 对象显示在锁定屏幕上。如需了解详细信息,请参阅如果您的应用使用 RemoteControlClient。

浮动通知

现在,当设备处于活动状态时(即,设备未锁定且其屏幕已打开),通知可以显示在小型浮动窗口中(也称为“浮动通知”)。这些通知看上去类似于精简版的通知,只是浮动通知还显示操作按钮。用户可以在不离开当前应用的情况下处理或清除浮动通知。

可能触发浮动通知的条件示例包括:

  • 用户的 Activity 处于全屏模式中(应用使用 fullScreenIntent)
  • 通知具有较高的优先级并使用铃声或振动

如果您的应用在以上任何情形下实现通知,请确保系统正确显示浮动通知。

媒体控件和 RemoteControlClient

RemoteControlClient 类现已弃用。请尽快切换到新的 MediaSession API。

Android 5.0 中的锁定屏幕不会为 MediaSession 或 RemoteControlClient 显示传输控件。不过,您的应用可以通过一个通知从锁定屏幕提供媒体播放控件。这让您的应用可以对媒体按钮的显示进行更多控制,同时为使用锁定设备和未锁定设备的用户提供一致的体验。

为实现此目的,Android 5.0 引入了一个新的 Notification.MediaStyle 模板。Notification.MediaStyle 将您使用 Notification.Builder.addAction() 添加的通知操作转换为精简按钮,嵌入到应用的媒体播放通知中。将您的会话令牌传递到 setSession() 方法以告知系统该通知控制进行中的媒体会话。

请务必将通知的可见性设为 VISIBILITY_PUBLIC,以将通知标记为安全,从而显示在任何锁定屏幕上(以安全方式或其他方式)。如需了解详细信息,请参阅锁定屏幕通知。

要让应用在 Android TV 或 Wear 平台上运行时显示媒体播放控件,则实现 MediaSession 类。如果您的应用需要在 Android 设备上接收媒体按钮事件,您还应实现 MediaSession。

getRecentTasks()

Android 5.0 中引入新的“并发文档和 Activity 任务”功能后(请参阅下文最近使用的应用屏幕中的并发文档和 Activity),为提升用户隐私的安全性,现已弃用 ActivityManager.getRecentTasks() 方法。对于向后兼容性,此方法仍会返回它的一小部分数据,包括调用应用自己的任务和可能的一些其他非敏感任务(如首页)。如果您的应用使用此方法检索它自己的任务,则改用 getAppTasks() 检索该信息。

Android NDK 中的 64 位支持

Android 5.0 引入了对 64 位系统的支持。64 位增强功能可增加地址空间和提升性能,同时仍完全支持现有的 32 位应用。64 位支持也可改进用于加密的 OpenSSL 的性能。此外,该版本还引入了新的原生媒体 NDK API,以及原生 OpenGL ES (GLES) 3.1 支持。

要使用 Android 5.0 中提供的 64 位支持,请从 Android NDK 页面下载和安装 NDK Revision 10c。有关对 NDK 进行的重要变更和问题修复的更多信息,请参阅 Revision 10c 版本说明。

绑定到服务

Context.bindService() 方法现在需要显式 Intent,如果提供隐式 intent,将引发异常。为确保应用的安全性,请使用显式 intent 启动或绑定 Service,且不要为服务声明 intent 过滤器。

WebView

Android 5.0 更改了应用的默认行为。

  • 如果您的应用是面向 API 级别 21 或更高级别:
    • 默认情况下,系统会阻止混合内容和第三方 Cookie。要允许混合内容和第三方 Cookie,请分别使用setMixedContentMode() 和 setAcceptThirdPartyCookies() 方法。
    • 系统现在可以智能地选择要绘制的 HTML 文档部分。这个新的默认行为有助于减少内存占用和提升性能。如果您要一次渲染整个文档,可通过调用enableSlowWholeDocumentDraw() 停用此优化。
  • 如果您的应用是面向低于 21 的 API级别:系统允许混合内容和第三方 Cookie,并始终一次渲染整个文档。

自定义权限唯一性要求

根据权限概述中所述,Android 应用可以定义以专有方式管理组件访问权限的自定义权限,无需使用平台预定义的系统权限。应用在其清单文件中声明的 元素中定义自定义权限。

少数情况下定义自定义权限是合规且安全的方法。不过,创建自定义权限有时并无必要,甚至可能会给应用带来潜在风险,具体取决于分配给权限的保护级别。

Android 5.0 其中一项行为变更确保只有一个应用可以定义给定自定义权限,除非使用与定义权限的其他应用相同的密钥进行签名。

使用重复的自定义权限的应用

任何应用都可以定义它需要的任何自定义权限,因此,可能会出现多个应用定义相同的自定义权限的情况。例如,如果两个应用提供相似的功能,它们可能会为其自定义权限派生出相同的逻辑名称。应用可能还纳入了本身包含相同自定义权限定义的通用公共库或代码示例。

在 Android 4.4 和更早的版本中,用户可以在给定设备上安装多个此类应用,不过系统会分配由第一个安装的应用指定的保护级别。

从 Android 5.0 开始,对于使用不同密钥签名的应用,系统会强制执行新的自定义权限唯一性限制。现在,设备上只有一个应用可以定义给定的自定义权限(按其名称确定),除非定义此权限的其他应用使用相同密钥签名。如果用户尝试安装的应用具有重复自定义权限且签名密钥不同于定义此权限的驻留应用,则系统将阻止安装。

您的应用需要注意的事项

在 Android 5.0 和更新的版本中,应用可以和以前一样继续定义自己的自定义权限,并通过 机制请求其他应用的自定义权限。不过,对于 Android 5.0 中引入的新要求,您应仔细评估可能给您的应用带来的影响。

下面是一些需要考虑的因素:

  • 您的应用是否在其清单文件中声明任何
    元素?如果是,那么这些权限是否确实是您的应用或服务正常运行不可或缺的?或者,能否使用系统默认权限代替它们?
  • 如果您的应用中具有 元素,您是否知道它们来自哪里?
  • 您实际上是否打算让其他应用通过 请求您的自定义权限?
  • 您是否在您包含 元素的应用中使用样板文件或示例代码?那些权限元素确实是不可或缺的吗?
  • 您的自定义权限使用的名称是简单名称还是基于其他应用可能共享的通用术语?

新安装和更新

如上所述,在运行 Android 4.4 或更早版本的设备上新安装和更新您的应用不会受影响,且行为没有任何变化。在运行 Android 5.0 或更新版本的设备上进行新安装和更新时,如果应用定义一个已由现有驻留应用定义的自定义权限,则系统会阻止安装您的应用。

使用 Android 5.0 系统更新的现有安装

如果您的应用使用自定义更新且已广泛分发和安装,那么,当用户收到将设备升级到 Android 5.0 的更新时,您的应用可能会受影响。在安装系统更新后,系统重新验证已安装的应用,包括检查它们的自定义权限。如果您的应用定义一个已由另一个通过验证的应用定义的自定义权限,且您的应用没有使用与该应用相同的密钥签名,则系统不会重新安装您的应用。

建议

在运行 Android 5.0 或更新版本的设备上,我们建议您立即检查您的应用,进行任何所需的调整,并尽快向您的用户发布更新版本。

  • 如果您在应用中使用自定义权限,则考虑它们的来源以及您是否确实需要它们。从您的应用中移除所有 元素,除非您确定它们是应用正常运行所必需的元素。
  • 尽可能考虑使用系统默认权限替代您的自定义权限。
  • 如果您的应用需要自定义权限,则重命名您的自定义权限,使其成为您的应用独有的权限,例如,将它们追加到应用的完整软件包名称。
  • 如果您有一组使用不同密钥签名的应用,且这些应用通过自定义权限访问共享组件,则确保此自定义权限在共享组件中仅定义一次。使用共享组件的应用不应自己定义自定义权限,而应通过
    机制请求访问权限。
  • 如果您有一组使用相同密钥签名的应用,则每个应用都可以根据需要定义相同的 自定义权限, 系统允许以常规方式安装这些应用。

TLS/SSL 默认配置变更

Android 5.0 针对 HTTPS 和其他 TLS/SSL 通信引入了对应用使用的默认 TLS/SSL 配置的变更:

  • TLSv1.2 和 TLSv1.1 协议现已启用,
  • AES-GCM (AEAD) 加密套件现已启用,
  • MD5、3DES、导出和静态密钥 ECDH 加密套件现已停用,
  • 首选使用 Forward Secrecy 加密套件(ECDHE 和 DHE)。

在下面列出的少数情况下,这些变更可能会导致 HTTPS 或 TLS/SSL 连接断开。

请注意,来自 Google Play 服务的安全性 ProviderInstaller 自 Android 2.3 开始就已在 Android 平台版本上提供这些变更。

服务器不支持任何已启用的加密套件

例如,服务器可能仅支持 3DES 或 MD5 加密套件。首选的修复方法是改进服务器的配置,以启用更强更现代的加密套件和协议。理想情况下,应启用 TLSv1.2 和 AES-GCM 以及 Forward Secrecy 加密套件(ECDHE、DHE),且最好使用后者。

也可以修改应用以使用自定义 SSLSocketFactory 与服务器通信。出厂时应精心设计以创建 SSLSocket 实例,除默认加密套件外,此实例还应启用服务器所需的部分加密套件。

应用对用于连接服务器的加密套件做出错误的假设

例如,某些应用包含中断的自定义 X509TrustManager,因为它预计 authType 参数将成为 RSA,但出现了 ECDHE_RSA 或 DHE_RSA。

服务器不支持 TLSv1.1、TLSv1.2 或新的 TLS 扩展
例如,与服务器握手的 TLS/SSL 被错误地拒绝或出现停顿。首选的修复方法是升级服务器以符合 TLS/SSL 协议。这使服务器可以成功地协商这些更新的协议或协商 TLSv1 或更早的协议,并忽略它不理解的传输层安全协议扩展程序。在某些情况下,在服务器上禁用 TLSv1.1 和 TLSv1.2 可以作为权宜之计,直到升级服务器软件。

也可以修改应用以使用自定义 SSLSocketFactory 与服务器通信。出厂时应精心设计以创建 SSLSocket 实例,该实例仅包含已启用且服务器可以正确为其提供支持的协议。

支持托管配置文件

设备管理员可以向设备添加托管配置文件。此配置文件由管理员所有,让管理员控制托管配置文件的同时,允许由用户控制其自己的个人配置文件及其存储空间。此变更会通过下列方式影响您的现有应用的行为。

处理 Intent

设备管理员可以从托管配置文件限制对系统应用的访问权限。在此情况下,如果应用从托管文件触发一个通常由该应用处理的 intent,且托管文件上没有适合此 intent 的处理程序,则此 intent 会引发异常。例如,设备管理员可以限制托管配置文件上的应用访问系统的相机应用。如果您的应用在托管配置文件上运行,并为 MediaStore.ACTION_IMAGE_CAPTURE 调用 startActivityForResult(),且托管配置文件上没有可以处理此 intent 的应用,则会导致 ActivityNotFoundException。

为防止出现此情况,您可以在触发任何 intent 之前检查是否至少有一个适合此 intent 的处理程序。要检查是否存在有效的处理程序,请调用 Intent.resolveActivity()。您可以在轻松拍照:使用相机应用拍摄照片中查看执行上述操作的示例。

在各个配置文件中共享文件

每个配置文件都有自己的文件存储空间。文件 URI 指的是文件存储空间中的特定位置,这意味着在一个配置文件上有效的文件 URI 在另一个文件上是无效的。对于只访问自己创建的文件的应用而言,这通常不是什么问题。不过,如果应用向某个 intent 附加文件,则附加文件 URI 并不安全,因为在某些情况下,可能会在其他配置文件上处理该 intent。例如,设备管理员可能会指定图像采集事件应由个人配置文件上的相机应用处理。如果此 intent 由托管配置文件上的应用触发,则相机需要能够将图像写入托管配置文件的应用可以读取的位置。

为安全起见,如果您需要将文件附加到某个可能会从一个配置文件移动到另一个配置文件的 intent,您应为该文件创建并使用内容 URI。有关共享文件及内容 URI 的更多信息,请参阅共享文件。例如,设备管理员可能会制定将由个人配置文件中的相机处理的 ACTION_IMAGE_CAPTURE 白名单。触发的 intent 的 EXTRA_OUTPUT 应包含指定照片应存储在何处的内容 URI。相机应用可以将图像写入该 URI 指定的位置,触发 intent 的应用将能够读取该文件,即使应用位于其他配置文件上。

已移除锁定屏幕小部件支持

Android 5.0 移除了对锁定屏幕小部件的支持;它继续为主屏幕上的小组件提供支持。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值