tips-android-version-addaptive

Android 7.0 (API 24) 适配

电池和内存

低电耗模式

此项新增的行为不会影响有关使您的应用适应Android 6.0(API级别23)中所推出的旧版本低电耗模式的建议和最佳做法,如对低电耗模式和应用待机模式进行针对性优化中所讨论

后台优化

移除了三项隐式广播,以帮助优化内存使用和电量消耗,详见后台优化

  1. CONNECTIVITY_ACTION:影响target 7.0的应用,替代方案JobScheduler
  2. ACTION_NEW_PICTURE:影响所有Android版本的应用,不允许收发
  3. ACTION_NEW_VIDEO:影响所有Android版本的应用,不允许收发

权限变更

系统权限变更
  1. 私有文件权限限制,MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE会引起SecurityException
  2. file:// 受限制,会引起FileUriExposedException,推荐使用FileProvider
  3. DownloadManager受限制,7.0以前访问COLUMN_LOCAL_FILENAME可能会失败,7.0及以后会引起SecurityException,推荐使用ContentResolver.openFileDescriptor()

应用间共享文件

  1. target 7.0的App,Android框架禁止以file:// 形式分享文件,会引起FileUriExposedException,推荐使用content://,并授予URI临时访问权限,详情参阅共享文件

无障碍改进

屏幕缩放

屏幕尺寸放生变化时: |target <= 23| target > 23| |—|—| |前台| 配置变更通知 |配置变更通知| |后台 |Terminated |配置变更通知|

Setting中的视觉设置

Android 7.0在“Welcome”屏幕中加入了“Vision Settings”,用户可以在新设备上设置以下无障碍功能设置:Magnification gesture、Font size、Display size 和话语提示。此项变更让您可以更容易发现与不同屏幕设置有关的错误。要评估此功能的影响,您应在启用这些设置的状态下测试应用。您可以在 Settings > Accessibility 中找到这些设置。

NDK

Android 7.0开始,系统阻止应用动态链接非公开NDK库

检查
  1. target <= 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

  2. target > 23, 运行在Android 7.0,且尝试访问私有库时,可能崩溃:

    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)

  3. 静态检测工具readelf:

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

更新
  1. 如果您的应用使用私有平台库,您应更新它,以添加该应用自己的库副本或使用公开NDK API。

  2. 如果您的应用使用访问私有符号的第三方库,则联系库作者以更新库。

  3. 请确保将您的所有非NDK库与您的APK打包在一起。

  4. 使用标准JNI函数而非来自 libandroid_runtime.so 的 getJavaVM 和 getJNIEnv:

    1
    2
    3
    AndroidRuntime::getJavaVM -> GetJavaVM from <jni.h>
    AndroidRuntime::getJNIEnv -> JavaVM::GetEnv or
    JavaVM::AttachCurrentThread from <jni.h>.
  5. 使用 __system_property_get 而非来自 libcutils.so 的私有 property_get 符号。为此,请使用 __system_property_get 及以下 include 函数:

    1
    #include <sys/system_properties.h>
  6. 使用来自 libcrypto.so 的 SSL_ctrl 符号的本地版本。例如,您应在您的 .so 文件中静态链接 libcyrpto.a,或从 BoringSSL/OpenSSL 添加一个动态链接的 libcrypto.so 版本,并将其打包到您的APK中。

注解

Android 7.0修复了一个注解可见性被忽略的错误。这种问题会导致应用可在运行时访问原本不允许访问的注解。这些注解包括: VISIBILITY_BUILD:仅应编译时可见。 VISIBILITY_SYSTEM:运行时应可见,但仅限底层系统。 如果您的应用依赖这种行为,请为运行时必须可用的注解添加保留政策。您可通过使用@Retention(RetentionPolicy.RUNTIME)来执行此操作。

其他

  1. 显示尺寸变化时,target < 7.0的后台App进程会被终止。用户从最近使用记录中恢复应用时,会出现崩溃现象。
  2. Debug.startMethodTracing()方法不再需要WRITE_EXTERNAL_STORAGE权限。
  3. 许多平台API现在开始检查在 Binder 事务间发送的大负载,会引发TransactionTooLargeExceptions。常见例子:Activity.onSaveInstanceState() 上存储过多数据,导致 ActivityThread.StopInfo 引发RuntimeException。
  4. 如果应用向 View 发布 Runnable 任务,并且 View 未附加到窗口,系统会用 View 为 Runnable 任务排队;在 View 附加到窗口之前,不会执行 Runnable 任务
  5. 如果Android 7.0上一项有 DELETE_PACKAGES 权限的应用尝试删除一个软件包,但另一项应用已经安装了这个软件包,则系统需要用户进行确认。在这种情况下,应用在调用 PackageInstaller.uninstall() 时预计的返回状态应为 STATUS_PENDING_USER_ACTION。
  6. 名为 Crypto 的 JCA 提供程序已弃用,因为它仅有的 SHA1PRNG 算法为弱加密。应用无法再使用 SHA1PRNG(不安全地)派生密钥。

Android 8.0 (API 26) 适配

针对所有API级别的应用

后台执行限制
  1. 为了提供续航时间,对于收入后台(进入已缓存状态)、没有活动组件的App,系统会解除App所具有的所有唤醒锁
    • 后台运行的应用对后台服务的访问受限
    • 应用无法使用清单文件注册大部分隐式广播

      默认只针对target 8.0的App,但是用户可以在Settings中手动启动这些限制,针对所有API级别的App

  2. 对特定API做了限制
    • target 8.0的App在不允许创建后台服务的情况下调用startService()时,会引发IllegalStateException
    • Context.startForegroundService()函数允许在后台运行的时候被调用,但是创建服务后要在5秒内调用该服务的startForeground()

      详情参阅后台执行限制

后台位置限制

为节约电量和用户体验,限制后台应用接受位置更新的频率,受影响的API:

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

    详情参阅后台位置限制

应用快捷键

  1. com.android.launcher.action.INSTALL_SHORTCUT变为隐式广播,替代方案ShortcutManager
  2. ACTION_CREATE_SHORTCUT Intent功能增强

详情参见固定快捷方式和微件预览功能指南

语言区域和国际化

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

Alert window

如果App使用SYSTEM_ALERT_WINDOW权限并且尝试下面某种窗口类型来显示Alert Window:

  • TYPE_PHONE
  • TYPE_PRIORITY_PHONE
  • TYPE_SYSTEM_ALERT
  • TYPE_SYSTEM_OVERLAY
  • TYPE_SYSTEM_ERROR
  1. Target < 8.0,Alert始终显示在TYPE_APPLICATION_OVERLAY类型窗口的下方;
  2. Target >= 8.0,App会使用TYPE_APPLICATION_OVERLAY类型显示Alert

输入和导航

详情参阅支持键盘导航

网页表单自动填充

WebSettings:

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

无障碍功能

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

详情参阅无障碍功能

网络连接和HTTPS

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 消息:“URI example..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 个字节的数据。

无缝连接

WLAN体验优化

安全性

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

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

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

隐私性

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 实现始终移交给默认处理程序处理。

联系人Provider关于使用情况统计变更

从Android 8.0开始,具有READ_CONTACTS权限的应用任然可以读取每个联系人的使用情况数据,但是获取的都是近似值而不是精确值(系统内部保留精确值),受影响的查询类型:

  • TIMES_CONTACTED
  • TIMES_USED
  • LAST_TIME_CONTACTED
  • LAST_TIME_USED

集合

现在,AbstractCollection.removeAll() 和 AbstractCollection.retainAll() 始终引发 NullPointerException;之前,当集合为空时不会引发 NullPointerException。

Android企业版

详情参阅企业中的 Android

参考

https://www.jianshu.com/p/d6c2cd72500d

Android 9.0 (API 28) 适配

针对所有API级别的应用

电源管理

帮助确保系统资源被提供给最需要它们的应用,详情参阅电源管理

隐私权

  1. 后台对传感器的访问受限 App后台运行时,以下行为受限
  • 访问麦克风和摄像头
  • 使用连续报告模式的传感器(加速度计、陀螺仪等)不会接收事件
  • 使用变化或一次性报告模式的传感器不会接收事件
  1. 限制访问通话记录 引入CALL_LOG权限组,把READ_CALL_LOG、WRIT_CALL_LOG、PROCESS_OUTGOING_CALLS移入该组(以前在PHONE权限组)。
  2. 限制访问电话号码 要从手机状态中读取电话号码,需要:
  • 要通过PHONE_STATE Intent读取,需要READ_CALL_LOG和READ_PHONE_STATE权限
  • 要通过 onCallStateChanged() 读取,只需要READ_CALL_LOG权限
  1. 限制访问WiFi位置和连接信息
  • WiFi扫描限制更严格,详情参阅Wi-Fi 扫描限制
  • getConnectionInfo() 函数返回的 WifiInfo 对象受限,只有当App具有以下权限时,才能获得SSID和BSSID:
  • ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION
  • ACCESS_WIFI_STATE
  • 还需要在设备上启用位置服务(Settings > Location)
  1. WiFi服务函数移除多余信息

    在 Android 9 中,下列事件和广播不接收用户位置或个人可识别数据方面的信息: WifiManager 中的 getScanResults() 和 getConnectionInfo() 函数。 WifiP2pManager 中的 [discoverServices()](https://developer.android.com/reference/android/net/wifi/p2p/WifiP2pManager.html?hl=zh-cn#discoverServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener)) 和 [addServiceRequest()](https://developer.android.com/reference/android/net/wifi/p2p/WifiP2pManager.html?hl=zh-cn#addServiceRequest(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceRequest, android.net.wifi.p2p.WifiP2pManager.ActionListener)) 函数。 NETWORK_STATE_CHANGED_ACTION 广播。 Wi-Fi 的 NETWORK_STATE_CHANGED_ACTION系统广播不再包含 SSID(之前为 EXTRA_SSID)、BSSID(之前为 EXTRA_BSSID)或连接信息(之前为 EXTRA_NETWORK_INFO)。 如果应用需要此信息,请改为调用getConnectionInfo()。

  2. 电话信息依赖设备定位设置 如果停用设备定位,则以下函数不提供结果:
non-sdk api的限制

为帮助确保应用稳定性和兼容性,此平台对某些非 SDK 函数和字段的使用进行了限制;无论您是直接访问这些函数和字段,还是通过反射或 JNI 访问,这些限制均适用。 在 Android 9 中,您的应用可以继续访问这些受限的接口;该平台通过 toast 和日志条目提醒您注意这些接口。 如果您的应用显示这样的 toast,则必须寻求受限接口之外的其他实现策略。 详情参阅对非 SDK 接口的限制 |列表|说明| |—|—| |黑名单|无论您应用的目标 API 级别是什么,您都无法使用此列表中的非 SDK 接口。如果您的应用尝试访问其中任何一个接口,系统就会抛出错误。| |灰名单|只要在您应用的目标 API 级别不限制此列表中的非 SDK 接口,您就可以使用它们。从 Android 9(API 级别 28)开始,我们在每个 API 级别分别会限制某些非 SDK 接口。如果您应用的目标 API 级别较低,您可以访问灰名单中的受限 API,但如果您的应用尝试访问在您的目标 API 级别受限的非 SDK 接口,系统就会假定此 API 已列入黑名单。注意:在 Android 9(API 级别 28)中,非受限灰名单中的非 SDK 接口称为浅灰名单,而受限灰名单中的非 SDK 接口称为深灰名单。| |白名单|此列表中的接口已在 Android 框架软件包索引中正式记录,它们是受支持的接口,您可以自由使用。|

  1. 使用veridex工具测试 veridex 工具会扫描 APK 的整个代码库(包括所有第三方库),并报告发现的所有使用非 SDK 接口的行为。但是也有局限性:
  • 无法检测到通过 JNI 实现的调用。
  • 只能检测到一部分通过反射实现的调用。
  • 对非活动代码路径的分析仅限于 API 级别的检查。
  1. 运行StrictMode API测试 使用 detectNonSdkApiUsage 方法来启用此 API。启用 StrictMode API 后,您可以使用 [penaltyListener](https://developer.android.com/reference/android/os/StrictMode.VmPolicy.BuilderpenaltyListener(java.util.concurrent.Executor, android.os.StrictMode.OnVmViolationListener)?hl=zh-cn) 来接收每次使用非 SDK 接口的行为所对应的回调,并且您可以在其中实现自定义处理。回调中提供的 Violation 对象派生自 Throwable,并且封闭式堆栈轨迹会提供相应使用行为的上下文。
  1. 使用可调试的应用测试 通过在搭载 Android 9(API 级别 28)或更高版本的设备或模拟器上构建和运行可调试应用来测试该应用是否使用非 SDK 接口。请确保您使用的设备或模拟器与您应用的目标 API 级别相匹配。 在您的应用上运行测试时,如果该应用访问了某些非 SDK 接口,系统就会输出一条日志消息。您可以检查应用的日志消息,查找以下详细信息:
  • 声明的类、名称和类型(采用 Android 运行时所使用的格式)。
  • 访问方式:链接、反射或 JNI
  • 所访问的非 SDK 接口属于哪个列表。

您可以使用 adb logcat 来查看这些日志消息,这些消息显示在所运行应用的 PID 下。举例而言,日志中可能包含如下条目:

Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI)

安全行为变更
设备安全性变更

传输层安全协议(TLS)实现变更

1
######      更严格的SECCOMP过滤器
加密变更

参数和算法的 Conscrypt 实现 其他变更

不再支持Android安全加密文件

详情参阅安全行为变更

ICU更新

升级ICU库到60。受影响内容:

  • 更好的区分GMT和UTC
  • java.text.SimpleDateFormat的使用
  • java.text.DateFormatSymbols.getZoneStrings()的使用
  • 亚洲/河内不再是可识别的时区
  • 使用NumberFormat.parseCurrency代替android.icu.text.NumberFormat.getInstance(ULocale, PLURALCURRENCYSTYLE).parse(String)去解析币种文本

详情参阅ICU更新

Android Test变更

详情参阅Android Test变更

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 的证书不再被信任。

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

不运行在UI Thread中做网络请求,任何时候!

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

​在调用 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 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 旋转时使用的用户旋转首选项来发挥作用。 用户旋转首选项可能在下列情况下发生变更。 请注意,恢复设备的自然旋转存在偏差,对于外形与手机类似的设备通常设置为纵向:

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

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() 发现每个可用的摄像头。 应用不应假定设备只有一个后置摄像头或只有一个前置摄像头。

针对 Target >= 9.0 的应用

前台服务

​ 针对 Android 9 或更高版本并使用前台服务的应用必须请求 FOREGROUND_SERVICE 权限。 这是普通权限,因此,系统会自动为请求权限的应用授予此权限。未请求权限就创建服务会引起SecurityException

隐私权变更

构建序列号弃用 ​ 在 Android 9 中,Build.SERIAL 始终设置为 “UNKNOWN” 以保护用户的隐私。如果您的应用需要访问设备的硬件序列号,您应改为请求 READ_PHONE_STATE 权限,然后调用 getSerial()。

DNS 隐私

​ 以 Android 9 为目标平台的应用应采用私有 DNS API。 具体而言,当系统解析程序正在执行 DNS-over-TLS 时,应用应确保任何内置 DNS 客户端均使用加密的 DNS 查找与系统相同的主机名,或停用它而改用系统解析程序。

框架安全性变更
默认情况下启用网络传输层安全协议 (TLS)

​ 如果您的应用以 Android 9 或更高版本为目标平台,则默认情况下 isCleartextTrafficPermitted() 函数返回 false。 如果您的应用需要为特定域名启用明文,您必须在应用的网络安全性配置中针对这些域名将 cleartextTrafficPermitted 显式设置为 true 按进程分设基于网络的数据目录

为改善 Android 9 中的应用稳定性和数据完整性,应用无法再让多个进程共用同一 WebView 数据目录。 此类数据目录一般存储 Cookie、HTTP 缓存以及其他与网络浏览有关的持久性和临时性存储。 在大多数情况下,您的应用只应在一个进程中使用 android.webkit 软件包中的类,例如 WebView 和 CookieManager。 例如,您应该将所有使用 WebView 的 Activity 对象移入同一进程。 您可以通过在应用的其他进程中调用 disableWebView(),更严格地执行“仅限一个进程”规则。 该调用可防止 WebView 在这些其他进程中被错误地初始化,即使是从依赖内容库进行的调用也能防止。 如果您的应用必须在多个进程中使用 WebView 的实例,则必须先利用 WebView.setDataDirectorySuffix() 函数为每个进程指定唯一的数据目录后缀,然后再在该进程中使用 WebView 的给定实例。 该函数会将每个进程的网络数据放入其在应用数据目录内自己的目录中。 注:即使您使用 setDataDirectorySuffix(),系统也不会跨应用的进程界限共享 Cookie 以及其他网络数据。 如果应用中的多个进程需要访问同一网络数据,您需要自行在这些进程之间复制数据。 例如,您可以调用 getCookie() 和 [setCookie()](https://developer.android.com/reference/android/webkit/CookieManager.html?hl=zh-cn#setCookie(java.lang.String, java.lang.String, android.webkit.ValueCallback)),在不同进程之间手动传输 Cookie 数据。

以应用为单位的 SELinux 域名

以 Android 9 或更高版本为目标平台的应用无法利用可全球访问的 Unix 权限与其他应用共享数据。 此变更可改善 Android 应用沙盒的完整性, 具体地讲,就是要求应用的私有数据只能由该应用访问。要与其他应用共享文件,请使用 content provider。

连接变更

连接数据计数和多路径 在以 Android 9 或更高版本为目标平台的应用中,系统计算并非当前默认网络的网络流量,例如,当设备连接 WLAN 时的蜂窝流量,并在 NetworkStatsManager 类中提供函数以查询该流量。 具体而言,getMultipathPreference() 现在将返回一个基于上述网络流量的值。 从 Android 9 开始,此函数针对蜂窝数据返回 true,但当超过一天内累积的特定流量时,它将开始返回 false。 在 Android 9 上运行的应用必须调用此函数并采用此提示。 ConnectivityManager.NetworkCallback 类现在将有关 VPN 的信息发送到应用。 此变更让应用侦听连接事件变得更容易,而无需混用同步和异步调用,也无需使用有限的 API。 此外,它还意味着将设备同时连接至多个 WLAN 网络或多个蜂窝网络时,信息传输可按预期工作。

Apache HTTP 客户端弃用

在 Android 6.0 中,我们取消了对 Apache HTTP 客户端的支持。 从 Android 9 开始,默认情况下该内容库已从 bootclasspath 中移除且不可用于应用。 要继续使用 Apache HTTP 客户端,以 Android 9 及更高版本为目标的应用可以向其 AndroidManifest.xml 添加以下内容:

1
<uses-library android:name="org.apache.http.legacy" android:required="false"/>

注:拥有最低 SDK 版本 23 或更低版本的应用需要 android:required=”false” 属性,因为在 API 级别低于 24 的设备上,org.apache.http.legacy 库不可用。 (在这些设备上,Apache HTTP 类在 bootclasspath 中提供。) 作为使用运行时 Apache 库的替代,应用可以在其 APK 中绑定自己的 org.apache.http 库版本。 如果进行此操作,您必须将该库重新打包(使用一个类似 Jar Jar 的实用程序)以避免运行时中提供的类存在类兼容性问题。

界面变更
View Focus

​ 0 面积的视图(即宽度或高度为 0)再也不能被聚焦。Activity 不再隐式分配触摸模式下的初始焦点,需要的话要显式请求初始焦点。

CSS RGBA 十六进制值处理

以 Android 9 或更高版本为目标的应用必须支持草案版 CSS 颜色模块级别 4 的行为,用于处理 4 和 8 个十六进制数字 CSS 颜色。 Chrome 自版本 52 以来便一直支持 CSS 颜色模块级别 4,但 WebView 目前停用此功能,因为现有 Android 应用被发现包含 Android ordering (ARGB) 中的 32 位十六进制颜色,这会导致渲染错误。 例如,对于以 API 级别 27 或更低版本为目标平台的应用,颜色 #80ff8080 目前在 WebView 中被渲染为不透明浅红色 (#ff8080)。 先导部分(Android 会将其解读为 Alpha 部分)目前被忽略。 如果某个应用以 API 级别 28 或更高版本为目标,则 #80ff8080 将被解读为 50% 透明浅绿 (#80ff80)。 文档滚动标签 Android 9 可正确处理文档的根标签是滚动标签的案例。 在之前的版本中,滚动位置在 body 标签上设置,根标签的滚动值为零。 Android 9 支持符合标准的行为,在这种行为中,滚动标签是根标签。 此外,直接访问 document.body.scrollTop、document.body.scrollLeft、document.documentElement.scrollTop 或 document.documentElement.scrollLeft 会因目标 SDK 的不同而具有不同的行为。 要访问视口滚动值,请使用 document.scrollingElement(若有)。 来自已暂停应用的通知 ​ 在 Android 9 之前,暂停的应用发出的通知会被取消。 从 Android 9 开始,暂停的应用发出的通知将被隐藏,直至应用继续运行。

坚持原创技术分享,您的支持将鼓励我继续创作!