如何制作同时支持 32 位和 64 位架构的 Android 应用程序?

How to make Android apps which support both 32-bit and 64-bit architecture?

我刚刚收到并阅读 a newsletter from Google Play 提到从明年开始,商店 "will require that new apps and app updates with native libraries provide 64-bit versions in addition to their 32-bit versions"。

对于那些还没有阅读它的人,它指出:

64-bit support requirement in 2019

Platform support for 64-bit architectures was introduced in Android 5.0. Today, over 40% of Android devices coming online have 64-bit support, while still maintaining 32-bit compatibility. For apps that use native libraries, 64-bit code typically offers significantly better performance, with additional registers and new instructions.

In anticipation of future Android devices that support 64-bit code only, the Play Console will require that new apps and app updates with native libraries provide 64-bit versions in addition to their 32-bit versions. This can be within a single APK or as one of the multiple APKs published.

We are not removing 32-bit support. Google Play will continue to support 32-bit apps and devices. Apps that do not include native code are unaffected.

This change will come into effect in August 2019. We're providing advance notice today to allow plenty of time for developers who don't yet support 64-bit to plan the transition. Stay tuned for a future post in which we'll take an in-depth look at the performance benefits of 64-bit native libraries on Android, and check out the CPUs and Architectures guide of the NDK for more info.

我们需要进行哪些实际更改才能在适用时完全符合这一新要求?

如果您没有原生 (NDK) 代码,即您只编写了 Java/Dex 代码,那么您无需执行任何操作。

如果您有本机代码(或库),则需要提供它们的 64 位版本。

根据 Google Play Team 发送的官方电子邮件,需要执行的操作是:

If you haven't yet, we encourage you to begin work for the 64-bit requirement as soon as possible. Many apps are written entirely in non-native code (e.g. the Java programming language or Kotlin) and will not need code changes.

Please note that we are not making changes to our policy on 32-bit support. Google Play will continue to deliver apps with 32-bit native code to 32-bit devices. The requirement means that those apps will need to have a 64-bit version as well.

To help you make the transition, we've prepared documentation on how to check whether your app already supports 64-bit and how to become 64-bit compliant.

We're also providing a high-level timeline below.

因此,链接的文档解释了:

If your app uses only code written in the Java programming language or Kotlin, including any libraries or SDKs, your app is already ready for 64-bit devices. If your app uses any native code, or you are unsure if it does, you will need to assess your app and take action.

[...]

The simplest way to check for 64-bit libraries is to inspect the structure of your APK file. When built, the APK will be packaged with any native libraries needed by the app. Native libraries are stored in various folders based on the ABI. It is not required to support every 64-bit architecture, but for each native 32-bit architecture you support you must include the corresponding 64-bit architecture.

For the ARM architecture, the 32-bit libraries are located in armeabi-v7a. The 64-bit equivalent is arm64-v8a.

For the x86 architecture, look for x86 for 32-bit and x86_64 for 64-bit.

The first thing to do is ensure that you have native libraries in both of these folders.[...]

而且,要构建 64 位库,您基本上需要按照以下说明操作:

Most Android Studio projects use Gradle as the underlying build system, so this section applies to both cases. Enabling builds for your native code is as simple as adding the arm64-v8a and/or x86_64, depending on the architecture(s) you wish to support, to the ndk.abiFilters setting in your app's 'build.gradle' file:

// 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'
// ...

最后,快速说明:

The 64-bit version of your app should offer the same quality and feature set as the 32-bit version.

顺便this official video稍微说一下

根据文档here,如果您的应用使用本机代码或外部库,例如基于本机的 realm(下图),则应支持 64 位假如。如果您的应用程序中使用任何 C/C++(本机)的任何外部库应该同时支持 32 位和 64 位架构,否则您应该与库所有者联系。在 Android Studio 中,我们可以通过 Build > Analyze APK 检查两种架构的版本是否可用,并出现以下 window:

如果您正在使用 NDK 并创建本机代码,您应该通过将它们纳入 gradle 来为所有架构提供支持:

defaultConfig {  
   ndk.abiFilters = 'armeabi-v7a' 'arm64-v8a' 'x86' 'x86_64'
   }

本机代码: 指直接编译为 运行 所在计算机的 CPU 指令的可执行程序。

Non-native 代码: 是指编译为 1970 年代后期和 1980 年代原始 Tandem 架构的 CPU 指令的可执行程序。当这样的程序是 运行 时,它不能直接在它 运行 所在的计算机的 CPU 上执行。 NonStop 操作系统包括用于原始 Tandem 架构的解释器,用于 运行 此类 non-native 代码。

如果您的应用仅使用以 Java 编程语言或 Kotlin 编写的代码,包括任何库或 SDK,则您的应用已准备好用于 64 位设备。如果您的应用使用任何本机代码,或者您不确定它是否使用,您将需要评估您的应用并采取行动。

您的应用是否使用本机代码?

首先要做的是检查您的应用程序是否使用任何本机代码。如果您的应用程序使用本机代码:

  • 在您的应用中使用任何 C/C++(本机)代码。
  • 与任何第三方本机库的链接。
  • 由使用本机库的第三方应用程序构建器构建。

更多,visit the docs.

如果您的 Android APK 不包括 64 位支持,您不必担心。 在 Android Studio 中转到构建 -> 分析 APK。您可以看到 APK 结构。在 lib 下,如果您看到 armeabi-v7a 库并且没有任何 arm64-v8ax86_64 库,那么您的 APK 不支持 64 位架构。

只需转到应用级别 build.gradle 并在 defaultConfig 下的 NDK 中添加 abiFilters,如下所示:

ndk {
    abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
}
  • 选项 1 - 从 APK 中删除库。
    • 第 1 步 - 将 APK 转换为 ZIP 并找到 lib 文件夹;如果你有 lib 文件夹,请查看库依赖项。
    • 第 2 步 - 从构建中删除依赖项 Gradle。
  • 选项 2 - 下载 64 位和 32 位 JAR 文件并添加到应用程序和构建中的 lib 文件夹中。

正在添加

ndk {
    abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
} 

DefaultConfig 下的 build.Gradle 文件中。请注意,push to play store 64 位要求即将到来。

首先打开 build.gradle 模块应用程序并添加这些行以删除 .so 文件并添加 64 位库 删除 apk 库中存在的所有 .so 文件

android {    
    compileSdkVersion 29    
    defaultConfig {    
        -----    
        -----    
        ndk.abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'    
        ndk {    
            abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'    
        }    
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"    
    }    
packagingOptions{     
        packagingOptions {    
            exclude 'lib/armeabi-v7a/libvudroid.so'    
            exclude 'lib/x86/libvudroid.so'    
            exclude 'lib/arm64-v8a/libvudroid.so'    
        }    
    }`

就我而言,我使用的是一个利用 OpenGL C 库的库(Android 的 ESRI ArcGIS)。我不得不使用以下内容,而不是似乎可以解决其他人问题的 ndk.abiFilters... 字符串:

ndk { abiFilters "armeabi-v7a", "arm64-v8a" }

第 1 步:

app=> build.gradle (put below code in build.gradle)

android {
........

 defaultConfig {

 .........

   ndk {
            abiFilters = []
            abiFilters.addAll(PROP_APP_ABI.split(':').collect{it as String})
        }
........        
}
.......
 packagingOptions {
        exclude 'lib/armeabi-v7a/libARM_ARCH.so'
    }

}

步骤:2

gradle.properties

(放在gradle.properties的下一行)

PROP_APP_ABI=armeabi-v7a:arm64-v8a

第 3 步:再次构建项目。尝试将该 apk 上传到 Play 商店。

官方试过Android Docs。工作出色。 在此解决方案中,我构建了多个 APK,您可以在附件中看到... 请确保您的编译 Skd 版本为 29 或构建工具版本为 29.0.3 如下所示:

    Android {
    compileSdkVersion 29
    buildToolsVersion '29.0.3'
    
    defaultConfig {
        applicationId "com.myapp.sk"
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 2
        versionName "1.0"
        multiDexEnabled true
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    
    }
        splits {
            density {
                enable true
                reset()
                include "mdpi", "hdpi"
            }
            abi {
                enable true
                reset()
                include "x86", "x86_64"
            }
        }
    }

// Map for the version code that gives each ABI a value.
ext.abiCodes = ['armeabi-v7a':1, x86:2, x86_64:3]

// For per-density APKs, create a similar map like this:
// ext.densityCodes = ['mdpi': 1, 'hdpi': 2, 'xhdpi': 3]

import com.android.build.OutputFile

// For each APK output variant, override versionCode with a combination of
// ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
android.applicationVariants.all { variant ->

    // Assigns a different version code for each output APK
    // other than the universal APK.
    variant.outputs.each { output ->

        // Stores the value of ext.abiCodes that is associated with the ABI for this variant.
        def baseAbiVersionCode =
                // Determines the ABI for this variant and returns the mapped value.
                project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))

        // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
        // the following code does not override the version code for universal APKs.
        // However, because we want universal APKs to have the lowest version code,
        // this outcome is desirable.
        if (baseAbiVersionCode != null) {

            // Assigns the new version code to versionCodeOverride, which changes the version code
            // for only the output APK, not for the variant itself. Skipping this step simply
            // causes Gradle to use the value of variant.versionCode for the APK.
            output.versionCodeOverride =
                    baseAbiVersionCode * 1000 + variant.versionCode
        }
    }
}

Multi-Aks Attachment

将此添加到您的 build.gradle

ndk.abiFilters 'arm64-v8a','x86_64'