前言
Google自 android L (5.0) 以来就持续对安装系统进行 安全 以及 性能上的升级,此次的 android P (9.0)也不例外, 更大程度上对之前一些版本一些警告的具体落实,无论你的 Target Api 是否是 28 都将受到影响。
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;-><init>(Ljava/io/File;Ljava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)V
Ldalvik/system/DexFile;-><init>(Ljava/lang/String;Ljava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)V
Ldalvik/system/DexFile;-><init>(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)V
Ldalvik/system/DexFile;-><init>(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/