从 Android 7.0 开始,系统将阻止应用动态链接非公开 NDK 库,这种库可能会导致您的应用崩溃。
(1) 检查应用是否使用第三方库:
利用 Android 7.0DK 中的 readelf 工具,您可以通过运行以下命令生成给定 .so
文件的所有动态链接的共享库列表:
aarch64-linux-android-readelf -dW libMyLibrary.so
通过下面的一些步骤,您可以修复上述类型的错误并确保您的应用不会在将来的更新版平台上崩溃:
(2)如果您的应用使用私有平台库,您应更新它,以添加该应用自己的库副本或使用公开 NDK API。
(3)如果您的应用使用访问私有符号的第三方库,则联系库作者以更新库。
(4)请确保将您的所有非 NDK 库与您的 APK 打包在一起。
(5)使用标准 JNI 函数而非来自 libandroid_runtime.so
的 getJavaVM
和 getJNIEnv
:
-
AndroidRuntime::getJavaVM -> GetJavaVM from <jni.h> AndroidRuntime::getJNIEnv -> JavaVM::GetEnv or JavaVM::AttachCurrentThread from <jni.h>.
- (6)使用
__system_property_get
而非来自libcutils.so
的私有property_get
符号。为此,请使用__system_property_get
及以下 include 函数: -
#include <sys/system_properties.h>
注:系统属性的可用性和内容未通过 CTS 进行测试。应执行进一步修复以避免同时使用这些属性。
- (7)使用来自
libcrypto.so
的SSL_ctrl
符号的本地版本。例如,您应在您的.so
文件中静态链接libcyrpto.a
,或从 BoringSSL/OpenSSL 添加一个动态链接的libcrypto.so
版本,并将其打包到您的 APK 中
您的应用可通过以下三种方式尝试访问私有平台 API:
- 您的应用直接访问私有平台库。您应更新您的应用以添加该应用的库副本,或使用公开 NDK API。
- 您的应用使用一个可访问私有平台库的第三方库。即使您确定您的应用不会直接访问私有库,您仍应针对此情景测试您的应用。
- 您的应用引用一个其 APK 中未包含的库。例如,如果您尝试使用您自己的 OpenSSL 副本,但忘记将它与应用的 APK 进行捆绑,则可能会出现此情况。正常情况下,此应用可在包含
libcrypto.so
的 Android 平台版本上运行。不过,此应用在不包含此库的新版 Android(例如,Android 6.0 和更高的版本)上会崩溃。为修复此问题,请确保您的 APK 捆绑您的所有非 NDK 库。
为降低此限制可能对当前发布的应用的影响,面向 API 级别 23 或更低级别的应用在 Android N 上可暂时访问颇为常用的一组库,例如 libandroid_runtime.so
、libcutils.so
、libcrypto.so
和 libssl.so
。
所有应用在调用既非公开又不可暂时访问的 API 时都会生成一个运行时错误。结果就是 System.loadLibrary
和 dlopen(3)
同时返回 NULL
,并可能导致您的应用崩溃。您应检查应用代码以移除对私有平台 API 的使用,并使用预览版设备或模拟器全面测试应用。如果您不确定您的应用是否使用私有库,您可以检查 logcat 以识别运行时错误。
下表描述的是根据应用使用的私有原生库及其目标 API 级别 (android:targetSdkVersion
),应用预期显示的行为。
库 | 目标 API 级别 | 通过动态链接器进行运行时访问 | N Developer Preview 行为 | 最终 Android N 版本行为 | 未来的 Android 平台行为 |
---|---|---|---|---|---|
公开 NDK | 任意 | 可访问 | 合乎预期 | 合乎预期 | 合乎预期 |
私有(暂时可访问的私有库) | 23 或更低 | 暂时可访问 | 合乎预期,但您会在目标设备上收到一个 logcat 警告和一条消息。 | 合乎预期,但您会收到一个 logcat 警告。 | 运行时错误 |
私有(暂时可访问的私有库) | 24 或更高 | 受限 | 运行时错误 | 运行时错误 | 运行时错误 |
私有(其他) | 任意 | 受限 | 运行时错误 | 运行时错误 | 运行时错误 |
检查您的应用是否使用私有库
为帮助您识别加载私有库的问题,logcat 可能会生成一个警告或运行时错误。例如,如果您的应用面向 API 级别 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 as a workaround for http://b/26394120
这些 logcat 警告通知您哪个库正在尝试访问私有平台 API,但不会导致您的应用崩溃。但是,如果应用面向 API 级别 24 或更高级别,logcat 会生成以下运行时错误,您的应用可能会崩溃:
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)