Android P 适配指南

1.1 non-sdk 接口限制

此限制不仅仅局限于sdk层 ( 直接引用 或者 反射 ),而触及到了 JNI 层,其实早在 android N 的时候就限制了 C / C++ 使用的符号集合,一旦NDK有无法通知的变更,毫无疑问会引起程序的 crash 。

1.1.1 SDK 接口和非 SDK 接口

SDK 接口指在 Android 框架 软件包索 中记录的接口, Google为了让开发者有过渡的时间并且起到警示的作用, 针对 non-sdk 接口设定了不同级别的名单类型:

  • 白名单:SDK
  • 浅灰名单:仍可以访问的非 SDK 函数/字段。
  • 深灰名单: * 对于 Target Api SDK 低于 API 28 的应用,允许使用深灰名单接口。
  • 对于 Target Api SDK 为 API 28 或更高级别的应用:行为与黑名单相同。
  • 黑名单:无论 Target Api SDK 如何。 平台将表现为似乎接口并不存在。 例如,无论应用何时尝试使 用接口,平台都会引发 NoSuchMethodError/NoSuchFieldException。

我们平时开发需要注意的也就是 深灰名单黑名单,不用太在意 浅灰名单 ,因为前面说到过可以直接引用 non-sdk 接口,这里基本上是指直接引用 浅灰名单的接口

这边先举个例子:在官方的浅灰名单中,其中列举可很多我们平时用的接口,例如 Intent 获取资源:

Landroid/content/Intent;->FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT:I
Landroid/content/Intent;->getExtra(Ljava/lang/String;)Ljava/lang/Object;
Landroid/content/Intent;->getExtra(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;

然而这正是我们平时用的很多的 Api,所以不必太在意,浅灰名单属于非常低级别的警告, 客户端适配迫切需要解决的是 黑名单 的列表: 黑名单 基本上是我们平时应用级开发不会用到的Api,例如:

Lsun/util/calendar/BaseCalendar;->getMonthLength(II)I

对应sdk中代码:

//推荐使用的方法,官方不限制
public int getMonthLength(CalendarDate var1) {
BaseCalendar.Date var2 = (BaseCalendar.Date)var1;
int var3 = var2.getMonth();
if (var3 >= 1 && var3 <= 12) {
return this.getMonthLength(var2.getNormalizedYear(), var3);
} else {
throw new IllegalArgumentException("Illegal month value: " + var3);
}
}

//官方黑名单方法,运行在 P 设备上直接crash
private int getMonthLength(int var1, int var2) {
int var3 = DAYS_IN_MONTH[var2];
if (var2 == 2 && this.isLeapYear(var1)) {
++var3;
}

return var3;
}

所以呢,黑名单 虽然听起来 骇人听闻,但是对于存量app的影响倒不是很大,因为基本上都是一些私有的,罕见的方法。

影响范围最大的当属 深灰名单, 因为官方强烈不推荐使用,但是为了给开发者缓冲时间,只有 Target Api 28+ 才会出现异常,代表性的 Api 有 DexFile 类:

Ldalvik/system/DexFile;->(Ljava/io/File;Ljava/lang/ClassLoader;[Ldalvik/system/DexPathList E l e m e n t ; ) V L d a l v i k / s y s t e m / D e x F i l e ; − > < i n i t > ( L j a v a / l a n g / S t r i n g ; L j a v a / l a n g / C l a s s L o a d e r ; [ L d a l v i k / s y s t e m / D e x P a t h L i s t Element;)V Ldalvik/system/DexFile;-><init>(Ljava/lang/String;Ljava/lang/ClassLoader;[Ldalvik/system/DexPathList Element;)VLdalvik/system/DexFile;><init>(Ljava/lang/String;Ljava/lang/ClassLoader;[Ldalvik/system/DexPathListElement;)V
Ldalvik/system/DexFile;->(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)V
Ldalvik/system/DexFile;->(Ljava/nio/ByteBuffer;)V
Ldalvik/system/DexFile;->DEX2OAT_FOR_BOOT_IMAGE:I
Ldalvik/system/DexFile;->DEX2OAT_FOR_FILTER:I
Ldalvik/system/DexFile;->DEX2OAT_FOR_RELOCATION:I
Ldalvik/system/DexFile;->DEX2OAT_FROM_SCRATCH:I
Ldalvik/system/DexFile;->NO_DEXOPT_NEEDED:I
Ldalvik/system/DexFile;->closeDexFile(Ljava/lang/Object;)Z
Ldalvik/system/DexFile;->createCookieWithArray([BII)Ljava/lang/Object;
Ldalvik/system/DexFile;->createCookieWithDirectBuffer(Ljava/nio/ByteBuffer;II)Ljava/lang/Object;
Ldalvik/system/DexFile;->defineClass(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Object;Ldalvik/system/DexFile;Ljava/util/List;)Ljava/lang/Class;
Ldalvik/system/DexFile;->defineClassNative(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Object;Ldalvik/system/DexFile;)Ljava/lang/Class;

以及 AssetManager 相关:

Landroid/content/res/AssetManager;->TAG:Ljava/lang/String;
Landroid/content/res/AssetManager;->addAssetPathInternal(Ljava/lang/String;Z)I
Landroid/content/res/AssetManager;->addAssetPathNative(Ljava/lang/String;Z)I
Landroid/content/res/AssetManager;->addAssetPaths([Ljava/lang/String;)[I
Landroid/content/res/AssetManager;->addOverlayPathNative(Ljava/lang/String;)I
Landroid/content/res/AssetManager;->applyStyle(JIIJ[IIJJ)V

这两个类被列入深灰名单基本上就打翻了市面上一众 热修复 框架, 这意味着如果您的App 是以 28+为目标版本,并且运行在 android P 之上,则这些热修复框架可能无法正常运行

在今年6月份 GMTC(全球大前端技术大会) 的时候,京东架构师发表了演讲 《当插件化遇上android P》 中就提到了,去黑科技化,目前 Android P 的 non-sdk 限制已经影响到京东的 插件框架

1.1.2 获取相关 non-sdk 名单

有两种方法:

  • 如果您的本地有 AOSP 项目的话,在根目录运行

make hiddenapi-aosp-blacklist

然后,可以在以下位置找到文件:

out/target/common/obj/PACKAGING/hiddenapi-aosp-blacklist.txt

  • 另外更好的办法是直接用官方编译好的结果,在 这里获取 查看。
1.1.3 检查项目中的 non-sdk

你大可以在 相应名单中 查找你想查找的类,不过官方提供了自动扫描工具 veridex

下载到本地目录, 找到你的系统对应的脚本目录:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

解压目录

使用命令扫描:

appcompat.sh --dex-file=apk路径

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以看到我们的项目中只有一个 深灰名单的警告。

1.1.4 调用non-sdk 接口

下图是利用各种途径使用 non-sdk 接口的结果:

访问方式结果
Dalvik 指令引用字段引发 NoSuchFieldError
Dalvik 指令引用函数引发 NoSuchMethodError
通过 Class.getDeclaredField() 或 Class.getField() 反射引发 NoSuchFieldException
通过 Class.getDeclaredMethod() 或 Class.getMethod() 反射引发 NoSuchMethodException
通过 Class.getDeclaredFields() 或 Class.getFields() 反射结果中未出现非 SDK 成员
通过 Class.getDeclaredMethods() 或 Class.getMethods().反射结果中未出现非 SDK 成员.
通过 env->GetFieldID() 调用 JNI返回 NULL,引发 NoSuchFieldError
通过 env->GetMethodID() 调用 JNI返回 NULL,引发 NoSuchMethodError

1.2 安全相关

1.2.1 加密变更
Crypto Java 加密架构 (JCA) 提供程序现已被移除

类似写法,将会发生 NoSuchProviderException:

SecureRandom.getInstance(“SHA1PRNG”, “Crypto”)

android P 之前的设备上,使用 Crypto 提供商,如果 target < 24 (N) 能够正常使用,如果target 24+ 则会失败

adnrodi P 设备上 由于彻底移除了 Crypto, 因此无论 target 是何值 都会抛出异常 NoSuchProviderException

许多算法的 Bouncy Castle 版本被弃用

加密功能的 BC 提供者被移除,官方博客中这样说:

Starting in Android P, we plan to deprecate some functionality from the BC provider that's duplicated by the AndroidOpenSSL (also known as Conscrypt) provider

android P开始,BC 提供者变成不推荐,如果targetApi < P 会有日志警告 targetApi >=p (28+) 将会抛出 NoSuchAlgorithmException ,以下写法将会受影响:

Cipher.getInstance(“AES/CBC/PKCS7PADDING”, “BC”) or
Cipher.getInstance(“AES/CBC/PKCS7PADDING”, Security.getProvider(“BC”))

因此,建议不要再指定 provider 而使用默认实现。

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

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

权限组变更

Android P 引入 CALL_LOG 权限组并将 READ_CALL_LOGWRITE_CALL_LOGPROCESS_OUTGOING_CALLS 权限移入该组。 在之前的 Android 版本中,这些权限位于 PHONE 权限组

wifi扫描权限变更:

Android 8.0和Android 8.1:

成功调用 WifiManager.getScanResults()) 需要以下任何一项权限:

  • ACCESS_FINE_LOCATION
  • ACCESS_COARSE_LOCATION
  • CHANGE_WIFI_STATE

如果调用应用程序没有任何这些权限,则调用将失败并显示 SecurityException

Android 9及更高版本

成功调用 WifiManager.startScan()) 需要满足以下所有条件:

  • 您的应用具有 ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION 权限。
  • 您的应用具有 CHANGE_WIFI_STATE 权限。

成功调用 WifiManager.getScanResults()) 需要满足以下所有条件:

  • 您的应用具有 ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION 权限。
  • 您的应用具有 ACCESS_WIFI_STATE 权限。
  • 设备上启用了位置服务(在“设置”>“位置”下)。

如果调用应用程序不满足所有这些要求,则调用将失败并显示 SecurityException

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

  • ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION
  • ACCESS_WIFI_STATE 检索 SSIDBSSID 还需要在设备上启用位置服务(在 Settings > Location 下)。
使用证书的主机名验证

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

1.4 其他限制

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

其实,自 Android 6 发布,就移除了对 Apache HTTP 客户端的支持,而推荐改用 HttpURLConnection 类,因为它可以通过透明压缩和响应缓存减少网络使用,并可最大限度降低耗电量, 从此我们变习惯这样使用 Apache HTTP API,即在 build.geadle 添加:

android {
useLibrary ‘org.apache.http.legacy’
}

androd P 开始,默认情况下该内容库已从 bootclasspath 中移除且不可用于应用。

这句话怎么理解,也就是说默认 Apache HTTP API 不可用,即使在build.geadle申明了该库。 这种说法分两种情况: 运行在 android P 设备上的应用:

  • Target 28 ,默认会报 NoClassDefFoundError,因为此库被禁止使用,要继续使用 Apache HTTP 客户端,以 Android 9 及更高版本为目标的应用可以向其 AndroidManifest.xml 添加以下内容:
  • Target < 28  可以和 android 6.0 一致。

bootclasspathlinux 系统变量,是系统在启动时会预先加载的类,以提高系统性能,这是 小米 MIX(7.0)上的 bootclasspath 变量:

/system/bin/sh: /system/framework/core-oj.jar:/system/framework/core-libart.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/system/framework/apache-xml.jar:/system/framework/org.apache.http.legacy.boot.jar:/system/framework/vivo-framework.jar:/system/framework/tcmiface.jar:/system/framework/telephony-ext.jar:/system/framework/vivo-media.jar:/system/framework/qcrilhook.jar:/system/framework/WfdCommon.jar:/system/framework/com.qti.location.sdk.jar:/system/framework/oem-services.jar:/system/framework/qcom.fmradio.jar: not found

变量中有:/system/framework/org.apache.http.legacy.boot.jar ,因此系统会帮我们加载,默认允许使用。

这是 android P 上的 bootclasspath 变量:

/system/framework/core-oj.jar:/system/framework/core-libart.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/bouncycastle.jar:/system/framework/apache-xml.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/system/framework/android.hidl.base-V1.0-java.jar:/system/framework/android.hidl.manager-V1.0-java.jar:/system/framework/framework-oahl-backward-compatibility.jar:/system/framework/android.test.base.jar
generic_x86_64:/ $

是没有 apachehttp 库的, 但是 他们有一个共同特点,就是系统内置了 apache 包, 在 /system/framework/目录下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

,但是我有一个困惑的地方,就是 同样 是运行在 android P 设备上 和 运行在 低版本上(>M) DexPathList 值确不一样:

  • android P :

PathClassLoader// 这是httpClient的 ClassLoader
DexPathList[[zip file “/system/framework/org.apache.http.legacy.boot.jar”,
zip file “/data/app/com.example.leixiang.demoapp-hOOUC7E0LuRvgmYC38vd5w==/base.apk”
],nativeLibraryDirectories=[/data/app/com.example.leixiang.demoapp-hOOUC7E0LuRvgmYC38vd5w==/lib/x86_64, /system/lib64]]

  • android N:

dalvik.system.PathClassLoader //这是httpClient的 ClassLoader
[DexPathList[[zip file “/data/app/com.example.leixiang.demoapp-1/base.apk”
],nativeLibraryDirectories=[/data/app/com.example.leixiang.demoapp-1/lib/arm64, /system/lib64, /vendor/lib64]]]

他们不同之处在于,android N 设备上的 DexPathList里面多了 apache的包,但是他们的加载器却都还是 PathClassLoader,我想可能是 P 系统上不再预先加载 apache http 相关类,所以把他加入 DexPathList? ,  并且 P 之前的系统加载 bootclasspath 中类也是用的 PathClassLoader?这个有待研究。

1.5 电源管理优化

Android 9 引入了一项新的电池管理功能,即应用待机群组。 应用待机群组可以基于应用最近使用时间和使用频率,帮助系统排定应用请求资源的优先级。 根据使用模式,每个应用都会归类到 五个 优先级群组之一中。 系统将根据应用所属的群组限制每个应用可以访问的设备资源:

活跃

如果用户当前正在使用应用,应用将被归到“活跃”群组中,例如:

  • 应用已启动一个 Activity
  • 应用正在运行前台服务
  • 应用的同步适配器与某个前台应用使用的 content provider 关联
  • 用户在应用中点击了某个通知 如果应用处于“活跃”群组,系统不会对应用的作业、报警或 FCM 消息施加任何限制。

FCM是指google推送啦,国内不要想了,至于长连接和心跳包是否会限制要看国内厂商具体操作了。

工作集

如果应用经常运行,但当前未处于活跃状态,它将被归到“工作集”群组中。 例如,用户在大部分时间都启动的某个社交媒体应用可能就属于“工作集”群组。 如果应用被间接使用,它们也会被升级到“工作集”群组中 。

常用

如果应用会定期使用,但不是每天都必须使用,它将被归到“常用”群组中。 例如,用户在健身房运行的某个锻炼跟踪应用可能就属于“常用”群组。

极少使用

如果应用不经常使用,那么它属于“极少使用”群组。 例如,用户仅在入住酒店期间运行的酒店应用就可能属于“极少使用”群组。

从未使用

安装但是从未运行过的应用会被归到“从未使用”群组中。 系统会对这些应用施加极强的限制。

我们可以利用 UsageStatsManager.getAppStandbyBucket()) 查看我们处于哪一个分组,此 api 是 21 添加。

不过用户可以通过配置 低电耗 白名单来摆脱分组的限制,具体配置方法看这里

以下是各分组对应的活动限制:

SettingJobs *AlarmsNetworkFirebase Cloud Messaging §
User Restricts Background Activity
Restrictions enabled:NeverNeverNeverNo restriction
Doze
Doze active:Deferred to windowRegular alarms: Deferred to window While-idle alarms: Deferred up to 9 minutesDeferred to windowHigh priority: No restriction Normal priority: Deferred to window
App Standby Buckets (by bucket)
Active:No restrictionNo restrictionNo restrictionNo restriction
Working set:Deferred up to 2 hoursDeferred up to 6 minuteNo restrictionNo restriction
Frequent:Deferred up to 8 hoursDeferred up to 30 minutesNo restrictionHigh priority: 10/day
Rare:Deferred up to 24 hoursDeferred up to 2 hoursDeferred up to 24 hoursHigh priority: 5/day

我们可以通过 adb命令 让我们的调试设备处于特定分组来测试相关的行为。

$ adb shell am set-standby-bucket packagename active|working_set|frequent|rare

2. 针对Taget 28+ 的应用

2.1 前台服务

前台服务 可以让你应用处于活跃状态,上面提到过 前台服务 可以让你的应用分组处于 活跃分组

Target 28+ 并使用前台服务的应用必须请求 FOREGROUND_SERVICE 权限。 这是 普通权限,因此,系统会自动为请求权限的应用授予此权限。

2.2 隐私权变更

构建序列号弃用

在 Android 9 中,Build.SERIAL 始终设置为 "UNKNOWN" 以保护用户的隐私。

如果您的应用需要访问设备的硬件序列号,您应改为请求 READ_PHONE_STATE 权限,然后调用 getSerial())。

最后

下面是有几位Android行业大佬对应上方技术点整理的一些进阶资料。希望能够帮助到大家提升技术

高级UI,自定义View

UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。

不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

最后

下面是有几位Android行业大佬对应上方技术点整理的一些进阶资料。希望能够帮助到大家提升技术

[外链图片转存中…(img-JChxqUWy-1714656405014)]

高级UI,自定义View

UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。

不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!

[外链图片转存中…(img-0In1txl8-1714656405014)]

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值