热门问题 升级64位架构指南
一、检验是否包含64位架构



APK构建打包时,原生库会根据ABI存储到不同的目录下:



在兼容64位和32位的硬件上,仅包含32位原生库的APP也可以正常运行,但是无法享受到64位架构带来的性能提升。
比如只包含armeabi-v7a或者armeabi原生库的APP都可以正常运行,但是如果包含arm64-v8a目录,则APP运行的原生库都会从arm64-v8a目录下加载。因此arm64-v8a和armeabi-v7a都存在的情况下,两个目录中的原生库数量要保持一致,如果APP加载armeabi-v7a目录中存在但是arm64-v8a目录中不存在的原生库会报错。


您可以通过以下几种方法评估是否需要适配64位架构:
1.1、检查代码
从代码角度检查是否需要进行64位架构适配,只有APP中使用了原生代码(C/C++)才需要进行64位适配,因为原生代码会被编译成运行在目标处理器的机器码。如果只有Java或者Kotlin代码则会自动适配好64位架构,不需要任何修改。
需要检查的原生代码包括APP中直接使用的以及引用的第三方库中使用的原生代码,如果第三方库中使用了原生代码,则需要和库提供方沟通进行64位适配。


1.2、使用APK分析器查看原生库
通过APK分析器可以查看APK中的目录结构和文件:
1)打开 Android Studio,然后打开任一项目;
2)从菜单中依次选择Build > Analyze APK:

3)选择您的 APK;
4)查看lib文件夹,您可以在其中找到“.so”文件。如果在您的应用中找不到任何“.so” 文件,则说明该应用不需要适配。如果有armeabi-v7a或x86目录,则说明您有 32 位 库;
5)检查是否arm64-v8a或x86_64文件夹中有类似的“.so”文件,如果32位的库都存 在对应的64位库则已经适配完成,否则需要进行开发适配。




1.3、解压APK查看原生库
您也可以直接解压apk,查看lib下的32位原生库是否有对应的64位原生库。比如通过下面zip命令查看apk中的so文件:
:: Command Line
zipinfo -1 YOUR_APK_FILE.apk | grep .so$
lib/armeabi-v7a/libmain.so
lib/armeabi-v7a/libmono.so
lib/armeabi-v7a/libunity.so
lib/arm64-v8a/libmain.so
lib/arm64-v8a/libmono.so
lib/arm64-v8a/libunity.so




二、如何使用 64 位库构建应用




下面针对构建 64 位库做出了相关的说明。不过,需要指出的是,以下内容仅介绍了如何构建在源代码的基础上可构建的代码和库。


2.1使用Android Studio或Gradle 进行构建
大多数Android Studio项目都使用Gradle作为底层构建系统,因此本部分适用于使用这两种工具进行构建的情况。针对原生代码进行构建很简单,只需将arm64-v8a和/或x86_64(视您要支持的架构而定)添加到应用“build.gradle”文件中的 ndk.abiFilters 设置中即可:
// Your app's build.gradle
apply plugin: 'com.android.app'
android {
compileSdkVersion 27
defaultConfig {
appId "com.google.example.64bit"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
ndk.abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
// ...



2.2 使用CMake进行构建
如果您的应用是使用CMake构建的,那么您可以通过将 arm64-v8a 传递到“-DANDROID_ABI”参数来针对 64 位 ABI 进行构建:
cmake -DANDROID_ABI=arm64-v8a … or
cmake -DANDROID_ABI=x86_64 …
在使用externalNativeBuild 时,此方法无效。请参阅使用Gradle进行构建部分。



2.3 使用ndk-build 进行构建
如果您的应用是使用 ndk-build 构建的,那么您可以使用 APP_ABI 变量修改“Application.mk”文件,从而针对 64 位 ABI 进行构建:
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
在使用 externalNativeBuild 时,此方法无效。请参阅使用 Gradle 进行构建部分。



2.4 将32位代码移植到 64 位架构
如果您的代码已经可以在桌面或 iOS 平台上运行,那么您无需针对 Android 做额外的工作。如果这是第一次针对 64 位系统构建您的代码,那么您需要解决的主要问题是指针不再适合 int 这样的 32 位整数类型。您将需要更新以 int、unsigned 或 uint32_t 等类型存储指针的代码。在 Unix 系统上,long 对应的是指针大小,但在 Windows 上并非如此,因此您应该改用释意类型 uintptr_t 或 intptr_t。使用 ptrdiff_t 类型来存储两个指针之间的差异。您应该始终选择使用 <stdint.h> 中定义的特定固定宽度整数类型,而不是 int 或 long 等传统类型,即便对于非指针也应如此。使用以下编译器标记来捕捉代码在指针和整数之间转换不正确的情况:
-Werror=pointer-to-int-cast
-Werror=int-to-pointer-cast
-Werror=shorten-64-to-32、
具有 int 字段(包含指向 C/C++ 对象的指针)的 Java 类也有同样的问题。在 JNI 源代码中搜索 jint,并确保切换到 long(Java 端)和 jlong(C++ 端)。
注意:因指针被截断而引起的崩溃将表现为 SIGSEGV,其中错误地址的前 32 位全部为零。对于 64 位代码而言,隐式函数声明的危险性要高得多。C/C++ 假定隐式声明的函数(即编译器未检测到声明的函数)的返回值类型为 int。如果函数的实际返回值类型是指针,那么在 32 位系统上是可行的,因为在 32 位系统中指针的类型为 int,但在 64 位系统中,编译器会丢弃指针的前半部分。例如:
// This function returns a pointer:
// extern char* foo();
// If you don't include a header that declares it,
// when the compiler sees this:
char* result = foo();
// Instead of compiling that to:
result = foo();
// It compiles to something equivalent to:
result = foo() & 0xffffffff;
// Which will then cause a SIGSEGV if you try to dereference result.
以下编译器标记会将隐式函数声明警告变成错误,以便您能够更轻松地查找和解决此问题:-Werror=implicit-function-declaration如果您有内联汇编程序,您需要重新编写该程序或使用普通的 C/C++ 实现。如果您对类型大小进行了硬编码(例如,8 或 16 字节),请使用等效的 sizeof(T) 表达式(例如 sizeof(void*))来替换它们。如果需要有条件地编译不同于 64 位的 32 位代码,则对于一般性的 32/64 差异,您可以使用 #if defined(LP64);对于 Android 支持的具体架构,可以使用 arm、aarch64 (arm64)、i386 (x86) 和 x86_64。您需要调整类似 printf 或 scanf 的函数的格式字符串,因为如果使用传统的格式说明符,您无法以一种对 32 位和 64 位设备都正确的方式来指定 64 位类型。您可利用 <inttypes.h> 中的 PRI 和 SCN 宏来解决此问题,PRIxPTR 和 SCNxPTR 分别用于写入/读取十六进制指针,PRId64 和 SCNd64 分别用于以可移植的方式写入/读取 64 位值。在移位时,您可能需要使用 1ULL 来获取要移位的 64 位常数,而不能使用仅支持 32 位的 1。



2.5 利用 Android App Bundle 减少大小增加量
为您的应用添加 64 位架构支持可能会导致 APK 的大小增加。我们强烈建议您利用 Android APP Bundle 功能,以尽量减小因在同一 APK 中同时包含 32 位和 64 位原生代码而对 APK 大小产生的影响。实际上,将应用改为使用 Android App Bundle 可以缩减 APK 的大小,使其比现在更小。



2.6   RenderScript的64位适配
如果您的应用使用 RenderScript 并且是通过较低版本的Android工具构建的,该应用可能会需要进行64位架构适配。使用版本低于21.0.0的构建工具时,编译器可能会将生成的位码放到外部.bc文件中。64 位架构不再支持这些旧的.bc文件,因此,如果您的APK中有这类文件,就会进行适配处理。
要解决此问题,请移除项目中的所有.bc文件,将环境升级到build-tools-21.0.0或更高版本,并将Android Studio中的renderscriptTargetApi设为21+,以指示编译器不要生成.bc文件。然后,重新构建您的应用,检查是否有.bc文件,再将应用上架。


2.7 Unity 开发者Unity自版本2018.2和2017.4.16开始提供 64 位支持。
如果您发现自己使用的Unity版本不支持64位架构,请确定要升级到的版本,并按照 Unity 提供的指南迁移您的环境,确保将您的应用升级到可构建 64 位库的版本。Unity 建议您升级到该编辑器的最新 LTS 版本,以获取最新的功能和更新。
下面的图表概述了 Unity 的各个版本以及您应该采取的措施:




如果您使用的 Unity 版本支持 64 位的 Android 库,那么您可以通过调整构建设置来生成 64 位版本的应用。您还需要使用 IL2CPP 后端作为 Scripting Backend。要为构建 64 位架构而设置 Unity 项目,请按以下步骤操作:
1)转到 Build Settings,然后确认 Unity 标志是否显示在 Platform 下的 Android 旁边,以确保您是在针对 Android 进行构建。
a.如果 Unity 标志未显示在 Android 平台旁边,请选择 Android,然后点击 Switch Platform。
2)点击 Player Settings



3)依次转到Player Settings Panel > Settings for Android > Other Settings > Configuration
4)将Scripting Backend设为IL2CPP。
5)依次选择Target Architecture > ARM64复选框。
6)启动构建
针对 ARM64 进行构建需要您专门针对该平台构建您的所有资源。请按照 Unity 的指南来缩减 APK 大小。




三、测试


在64位硬件上安装
首先验证APP在64位硬件上可以正常安装,执行如下adb install命令

:: Command Line
# A successful install:
> adb install --abi armeabi-v7a YOUR_APK_FILE.apk
Success
# If your APK does not have the 64-bit libraries:
> adb install --abi arm64-v8a YOUR_APK_FILE.apk
adb: failed to install YOUR_APK_FILE.apk: Failure [INSTALL_FAILED_NO_MATCHING_ABIS: Failed to extract native libraries, res=-113]
# If your device does not support 64-bit, an emulator, for example:
> adb install --abi arm64-v8a YOUR_APK_FILE.apk
ABI arm64-v8a not supported on this device

智能解答

联系我们

投诉建议

限时填问卷吐槽赢话费

TOP