影响范围最大的当属 深灰名单, 因为官方强烈不推荐使用,但是为了给开发者缓冲时间,只有 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_LOG、WRITE_CALL_LOG 和 PROCESS_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 对象。 如果调用应用具有以下权限,则只能使用该对象的函数来检索 SSID 和 BSSID 值:
- ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION
- ACCESS_WIFI_STATE 检索 SSID 或 BSSID 还需要在设备上启用位置服务(在 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
一致。
bootclasspath 是 linux 系统变量,是系统在启动时会预先加载的类,以提高系统性能,这是 小米 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:/ $
是没有 apache 的 http 库的, 但是 他们有一个共同特点,就是系统内置了 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 添加。
不过用户可以通过配置 低电耗 白名单来摆脱分组的限制,具体配置方法看这里。
以下是各分组对应的活动限制:
Setting | Jobs * | Alarms | Network | Firebase Cloud Messaging § |
---|---|---|---|---|
User Restricts Background Activity | ||||
Restrictions enabled: | Never | Never | Never | No restriction |
Doze | ||||
Doze active: | Deferred to window | Regular alarms: Deferred to window While-idle alarms: Deferred up to 9 minutes | Deferred to window | High priority: No restriction Normal priority: Deferred to window |
App Standby Buckets (by bucket) | ||||
Active: | No restriction | No restriction | No restriction | No restriction |
Working set: | Deferred up to 2 hours | Deferred up to 6 minute | No restriction | No restriction |
Frequent: | Deferred up to 8 hours | Deferred up to 30 minutes | No restriction | High priority: 10/day |
Rare: | Deferred up to 24 hours | Deferred up to 2 hours | Deferred up to 24 hours | High 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 P SDK
api:
/**
- A hardware serial number, if available. Alphanumeric only, case-insensitive.
- For apps targeting SDK higher than {@link Build.VERSION_CODES#O_MR1} this
- field is set to {@link Build#UNKNOWN}.
- @deprecated Use {@link #getSerial()} instead.
**/
@Deprecated
// IMPORTANT: This field should be initialized via a function call to
// prevent its value being inlined in the app during compilation because
// we will later set it to the value based on the app’s target SDK.
public static final String SERIAL = getString(“no.such.thing”);
DNS 隐私
以 Android 9 为目标平台的应用应采用私有 DNS API
。 具体而言,当系统解析程序正在执行 DNS-over-TLS
时,应用应确保任何内置 DNS 客户端均使用加密的 DNS 查找与系统相同的主机名,或停用它而改用系统解析程序。 停用路径: Settings -> private dns
:
private DNS 设置
2.3 安全
默认情况下启用网络传输层安全协议 (TLS)
如果您的应用 Target 28+,则默认情况下 isCleartextTrafficPermitted()) 函数返回 false。 如果您的应用需要为特定域名启用明文,您必须在应用的网络安全性配置中针对这些域名将 cleartextTrafficPermitted
显式设置为 true
。
否认会出现以下日志错误输出:
W/Glide: Load failed for http://xxx-99billxx.ufile.ucloud.cn/online/image/A1707101417703-349-0xxhdpi
java.io.IOException: Cleartext HTTP traffic to xxx-99billxx.ufile.ucloud.cn not permitted
注意:该api是 api 23 才引入的。 所以,由于一些历史原因无法及时把服务器变更为 https 的应用,应该通过配置文件针对特定域名允许使用明文传输,也
就是 http 服务。
<?xml version="1.0" encoding="utf-8"?> secure.example.com定义配置文件 res/xml/network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>然后在manifest.xml 中申明
<manifest … >
<application android:networkSecurityConfig=“@xml/network_security_config”
… >
…
按进程分设基于网络的数据目录
为改善 Android 9 中的应用稳定性和数据完整性,应用无法再让多个进程共用同一 WebView 数据目录。 此类数据目录一般存储 Cookie、HTTP 缓存以及其他与网络浏览有关的持久性和临时性存储
如果您的应用必须在多个进程中使用 WebView 的实例,则必须先利用 WebView.setDataDirectorySuffix()) 函数为每个进程指定唯一的数据目录后缀,然后再在该进程中使用 WebView 的给定实例。 该函数会将每个进程的网络数据放入其在应用数据目录内自己的目录中。
注:即使您使用 setDataDirectorySuffix(),系统也不会跨应用的进程界限共享 Cookie 以及其他网络数据。 如果应用中的多个进程需要访问同一网络数据,您需要自行在这些进程之间复制数据。 例如,您可以调用 getCookie() 和 setCookie(),在不同进程之间手动传输 Cookie 数
3. 新功能
Kotlin 友好
改进了 dexer 以及协调Jetbranins改进 Kotlin Compier 使用 kotlin 编写的App在android P 上运行得更快,并且和 Jetbrains 团队沟通,提供了新的 kotlin 插件。
ImageDecoder
替代 BitmapFactory 可以从 流、file、byte buffer、 uRL 加载 Bitmap 和 Drawable 支持精确尺寸缩放, 并且支持加载 gif 、 Webp, 以及圆角等样式设置。
wifi RTT
在室内,IEEE 802.11 MC WI-FI protocol 测量与附近wifi 链接点的距离(2~3个),通过 RTT,来测量距离, 能精确到 1~2米 在提供硬件支持的 Android P 设备上,应用可以使用全新的 RTT API 来测量与附近支持 RTT 的 Wi-Fi 接入点 (AP) 的距离,设备不需要连接至 AP 即可使用 RTT
使用uses-feature来标注:
PrecomputedText
最后
小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
资料⬅专栏获取
ure来标注:
PrecomputedText
最后
小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
[外链图片转存中…(img-yG9XEzSF-1718820052879)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
资料⬅专栏获取