How to build single APK with Android NDK and Gradl

2019-03-31 00:51发布

问题:

I am working on a new android app using OpenCV (C++ not Java) and I am new to both opencv and the NDK. I have it building (and running) succesfully using the Gradle file below and in Android Studio I can select a variant and hit build (e.g. x86).

I have 2 questions:

  1. Is there a way I can have a variant that builds an APK supporting all architectures? (I know file size will be bigger)
  2. Can i achieve a build without specifying all the same libs for each variant. Is there any way I can make the build system just pick up the libraries each time since they are all inside the project and organised by architecture name?

    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'
    }
    

回答1:

Use the keyword "fat" after you have defined all architectures that you plan to include in your apk in the build.gradle file as highlighted in the code below . This will build a FAT binary. Watch this video for more details 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'
    }
}


回答2:

In order to have your libs correctly linked, you have to declare different ldLibs per architecture as you're currently doing.

Unfortunately this isn't possible to do this from gradle with the current plugin if you're build for all architectures at once.

In my view, the best solution would be to deactivate the built-in (currently deprecated) ndk support and use regular Android.mk / Application.mk files instead. Your build.gradle would look like this:

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
    }
}