如何使用 Android NDK 和 Gradle 构建单个 APK

How to build single APK with Android NDK and Gradle

我正在使用 OpenCV(C++ 而不是 Java)开发一个新的 android 应用程序,我是 opencv 和 NDK 的新手。我使用下面的 Gradle 文件成功构建(和 运行),在 Android Studio 中我可以 select 一个变体并点击构建(例如 x86)。

我有两个问题:

  1. 有没有一种方法可以构建支持所有架构的 APK 变体? (我知道文件会更大)
  2. 我可以在不为每个变体指定所有相同库的情况下实现构建吗?有什么方法可以让构建系统每次都只获取库,因为它们都在项目中并按体系结构名称组织?

    apply plugin: 'com.android.application'
    
    android {
    compileSdkVersion 21
    buildToolsVersion "21.0.0"
    
    defaultConfig {
        applicationId "uk.co.xxx.androidcppimagereader"
        minSdkVersion 17
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    
    def libsDir = projectDir.path + "/libs/"
    
    productFlavors {
        x86 {
            versionCode Integer.parseInt("3" + defaultConfig.versionCode)
            ndk {
                abiFilter "x86"
                moduleName "Processing"
                stl "gnustl_static"
                cFlags "-I/opt/local/include/opencv -I/opt/local/include"
                ldLibs libsDir + "x86/libopencv_core.a"
                ldLibs libsDir + "x86/libopencv_ts.a"
                ldLibs libsDir + "x86/libopencv_contrib.a"
                ldLibs libsDir + "x86/libopencv_ml.a"
                ldLibs libsDir + "x86/libopencv_java.so"
                ldLibs "log"
                ldLibs "z", "jnigraphics"
            }
        }
        armv7 {
            versionCode Integer.parseInt("2" + defaultConfig.versionCode)
            ndk {
                abiFilter "armeabi-v7a"
                moduleName "Processing"
                stl "gnustl_static"
                cFlags "-I/opt/local/include/opencv -I/opt/local/include"
                ldLibs libsDir + "armeabi-v7a/libopencv_core.a"
                ldLibs libsDir + "armeabi-v7a/libopencv_ts.a"
                ldLibs libsDir + "armeabi-v7a/libopencv_contrib.a"
                ldLibs libsDir + "armeabi-v7a/libopencv_ml.a"
                ldLibs libsDir + "armeabi-v7a/libopencv_java.so"
                ldLibs "log"
                ldLibs "z", "jnigraphics"
            }
        }
        arm {
            versionCode Integer.parseInt("1" + defaultConfig.versionCode)
            ndk {
                abiFilter "armeabi"
                moduleName "Processing"
                stl "gnustl_static"
                cFlags "-I/opt/local/include/opencv -I/opt/local/include"
                ldLibs libsDir + "armeabi/libopencv_core.a"
                ldLibs libsDir + "armeabi/libopencv_ts.a"
                ldLibs libsDir + "armeabi/libopencv_contrib.a"
                ldLibs libsDir + "armeabi/libopencv_ml.a"
                ldLibs libsDir + "armeabi/libopencv_java.so"
                ldLibs "log"
                ldLibs "z",  "jnigraphics"
            }
        }
    
    }
    
        buildTypes {
             release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:appcompat-v7:21.0.3'
    }
    

在 build.gradle 文件中定义了计划包含在 apk 中的所有架构后,请使用关键字 "fat",如下面代码中突出显示的那样。这将构建一个 FAT 二进制文件。观看此视频了解更多详情 https://www.youtube.com/watch?v=kFtxo7rr2HQ

arm {
    versionCode Integer.parseInt("1" + defaultConfig.versionCode)
    ndk {
        abiFilter "armeabi"
        moduleName "Processing"
        stl "gnustl_static"
        cFlags "-I/opt/local/include/opencv -I/opt/local/include"
        ldLibs libsDir + "armeabi/libopencv_core.a"
        ldLibs libsDir + "armeabi/libopencv_ts.a"
        ldLibs libsDir + "armeabi/libopencv_contrib.a"
        ldLibs libsDir + "armeabi/libopencv_ml.a"
        ldLibs libsDir + "armeabi/libopencv_java.so"
        ldLibs "log"
        ldLibs "z",  "jnigraphics"
    }
    fat // <- HERE
}

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

为了正确链接您的库,您必须像当前所做的那样为每个体系结构声明不同的 ldLib。

不幸的是,如果您同时为所有架构构建,则无法从 gradle 使用当前插件执行此操作。

在我看来,最好的解决方案是停用内置(目前已弃用)ndk 支持并改用常规 Android.mk / Application.mk 文件。您的 build.gradle 将如下所示:

import org.apache.tools.ant.taskdefs.condition.Os

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.1"

    defaultConfig {
        applicationId "uk.co.xxx.androidcppimagereader"
        minSdkVersion 17
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }

    sourceSets.main {
        jniLibs.srcDir 'src/main/libs' //set libs as default directory for .so files inclusion, instead of jniLibs
        jni.srcDirs = [] //disable automatic ndk-build call
    }

    // call regular ndk-build(.cmd) script from app directory
    task ndkBuild(type: Exec) {
        if (Os.isFamily(Os.FAMILY_WINDOWS)) {
            commandLine 'ndk-build.cmd', '-C', file('src/main').absolutePath
        } else {
            commandLine 'ndk-build', '-C', file('src/main').absolutePath
        }
    }

    tasks.withType(JavaCompile) {
        compileTask -> compileTask.dependsOn ndkBuild
    }
}