【问题标题】:Gradle Android Build System NDK issuesGradle Android 构建系统 NDK 问题
【发布时间】:2014-05-21 02:49:38
【问题描述】:

我将是第一个承认我对 Gradle 和新的 Android 构建系统并不是非常熟悉的人,但不幸的是,由于问题 21479 (https://code.google.com/p/android/issues/detail?id=21479),我不得不(从 ant)转向它(来自 ant)和评论 “这不会被修复。我们正专注于完成将取代 Ant 的基于 Gradle 的构建系统。” 不幸的是,我无法得到这些东西添加 Millenium Media 广告库后再次构建。这是除了 Android OpenCV 库、Chilkat 的加密库和支持 v4 库之外的,但 MMedia 库打破了这一切。

所以,我认为,迁移到基于 Gradle 的新构建系统是一个很好的理由。不幸的是,尽管留下了一个损坏的 ant 构建系统,但新系统似乎还没有完成。特别是 ndk 支持。

我正在处理的应用程序使用 OpenCV 进行一些图像处理,并且一些操作运行速度太慢,除非它们被编译为本机(大量内存移动和比较,它们在 JNI 边界上太慢了)。因此,我没有尝试在 VM 和本机代码之间来回传递数据,而是将所有这些东西留在本机端,只需从 VM 调用一次即可获得结果。

第一个问题是编译 ndk 的东西。我无法让 ndk 闭包中的设置正常工作,因此我不得不求助于使用 ndk-build 命令并将其作为任务执行:

task ndkBuild(type: Exec) {
    String MainDirectory = System.getProperty("user.dir") + '/app/src/main'
    println "Main app directory for NDK build " + MainDirectory
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
       commandLine 'gradle-ndk-build.cmd', MainDirectory, '-j'
    }
    else {
       commandLine 'gradle-ndk-build', MainDirectory, '-j'
    }
}

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

这很好用;它编译 ndk 代码并生成 .so 库而不会出错。不幸的是,它不会将生成的 .so 文件放入最终包中。它使所有其他本机库都正常,但不是这个 - 不知道为什么。

我发现了很多针对此问题的所谓修复方法,例如:

tasks.withType(com.android.build.gradle.tasks.PackageApplication) { pkgTask ->
    pkgTask.jniFolders = new HashSet<File>()
    pkgTask.jniFolders.add(new File(buildDir, 'native-libs'))
}

但是添加它只会导致一个 apk 文件根本没有本机库。我见过其他人有同样的问题(例如https://groups.google.com/forum/#!msg/adt-dev/QbDHM41QT2E/J4jHCC_RuIEJ),但我尝试了所有建议的解决方案,但没有一个适合我。

由于我不经常更改本机代码,所以我刚刚做了一个 hack,将生成的本机库 (libndklib.so) 从 app/src/main/libs 复制到 /app/src/main/jni编译后;然后它最终出现在 apk 包中。显然,这有点令人讨厌,因为如果有人接管此代码,他们会想知道为什么他们对本机代码的更改从未出现在应用程序中。

所以,我的问题是: 在我运行 ndk 命令 (gradle-ndk-build) 后,我可以在 gradle 脚本中运行一些东西,该命令将从 app/src/main/libs/armeabi/libndklib 复制生成的文件。所以到 /app/src/main/jni/armeabi/libndklib.so (对于每个架构 - armeabi、armeabi-v7、x86、mips)所以它最终在 apk 包中?

有什么方法可以让 Gradle ndk 闭包正确处理以下 ndk make 文件: Application.mk

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := all
APP_PLATFORM := android-8

Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# OpenCV
OPENCV_CAMERA_MODULES:=on
OPENCV_INSTALL_MODULES:=on
OPENCV_LIB_TYPE:=SHARED
include /home/myname/tools/OpenCV-2.4.8-android-sdk/sdk/native/jni/OpenCV.mk
LOCAL_MODULE    := ndklib
LOCAL_SRC_FILES := ndklib.cpp motion.cpp
LOCAL_LDLIBS +=  -lm -llog
include $(BUILD_SHARED_LIBRARY)
# Add prebuilt chilkat library
include $(CLEAR_VARS)
LOCAL_MODULE := lib-chilkat
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libchilkatemail.so
include $(PREBUILT_SHARED_LIBRARY)

我查看了插件的 Gradle 源代码,但我看不到许多支持的指令。

我可以添加某种在 Gradle 脚本末尾运行的 hack,将 libndklib.so 的适当副本(对于正确的架构)强制到生成的 apk 中吗?我可以忍受后者,直到为 gradle 构建的 Android 插件完成 ndk 东西。

========================================

编辑 - 在 ph0b 的回答之后 这是最终的 build.gradle 文件,其中包含建议的 mod。完美创建.apk build.gradle(在应用目录中)

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.9.+'
    }
}

apply plugin: 'android'

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

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.3"

    signingConfigs {
        debug {
            storeFile file("dbgkeystore")
            storePassword "nopass"
            keyAlias "mainkeyname"
            keyPassword "nopass"
        }

        release {
            storeFile file("keystore")
            storePassword "xxxxxxxx"
            keyAlias "mainkeyname"
            keyPassword "yyyyyyyy"
        }
    }

    // Autoincrement the version properties file
    // ******************************************
    def versionPropsFile = file('version.properties')
    def code = 1

    def majorversion = 1
    def minorversion = 1

    defaultConfig {
        versionCode code
        versionName "${majorversion}.${minorversion}.${code}"
        minSdkVersion 10
        targetSdkVersion 19
    }

    buildTypes {
        release {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
            signingConfig signingConfigs.release
        }

        debug {
           runProguard false
           proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
           packageNameSuffix ".debug"
           versionNameSuffix "-debug"
           signingConfig signingConfigs.debug
        }
    }

    sourceSets {
        main {
            jni.srcDirs = []
            // jniLibs.srcDir 'src/main/jni' // - Doesn't work, leaves out the .so files generated by ndk-build
            jniLibs.srcDir 'src/main/libs'
        }
    }

    flavorDimensions "version", "abi"

    productFlavors {
        pro {
            flavorDimension "version"
            packageName "org.somedomainname.myAppPro1"
        }

        lite {
            flavorDimension "version"
            packageName "org.somedomainname.myAppLite1"
        }

        arm {
            flavorDimension "abi"

            ndk {
                abiFilter "armeabi"
            }

            if (versionPropsFile.canRead()){
                def Properties versionProps = new Properties()

                versionProps.load(new FileInputStream(versionPropsFile))

                code = versionProps['VERSION_CODE'].toInteger() + 1

                versionProps['VERSION_CODE']=code.toString()
                versionProps.store(versionPropsFile.newWriter(), null)

                versionCode code
                versionName "${majorversion}.${minorversion}.${code}"
            }
            else {
                throw new GradleException("Could not read version.properties!")
            }

        }

        armv7 {
            flavorDimension "abi"

            ndk {
                abiFilter "armeabi-v7a"
            }

            if (versionPropsFile.canRead()){
                def Properties versionProps = new Properties()

                versionProps.load(new FileInputStream(versionPropsFile))

                code = versionProps['VERSION_CODE'].toInteger() + 1

                versionProps['VERSION_CODE']=code.toString()
                versionProps.store(versionPropsFile.newWriter(), null)

                versionCode code
                versionName "${majorversion}.${minorversion}.${code}"
            }
            else {
                throw new GradleException("Could not read version.properties!")
            }
        }

        x86 {
            flavorDimension "abi"

            ndk {
                abiFilter "x86"
            }

            if (versionPropsFile.canRead()){
                def Properties versionProps = new Properties()

                versionProps.load(new FileInputStream(versionPropsFile))

                code = versionProps['VERSION_CODE'].toInteger() + 1

                versionProps['VERSION_CODE']=code.toString()
                versionProps.store(versionPropsFile.newWriter(), null)

                versionCode code
                versionName "${majorversion}.${minorversion}.${code}"
            }
            else {
                throw new GradleException("Could not read version.properties!")
            }
        }
    }

    lintOptions {
        checkReleaseBuilds false
        // Or, if you prefer, you can continue to check for errors in release builds,
        // but continue the build even when errors are found:
        abortOnError false
    }

repositories {
    mavenCentral()
    flatDir {
        dirs '/home/myname/maindrive/work/dynamic/android/UtilLib/aarlib'
    }
}

dependencies {
    compile 'com.android.support:appcompat-v7:+'
    compile fileTree(dir: 'libs', include: ['*.jar'])

    // Note: org.somedomainname.UtilLib on the depency below is ignored when usng flatdir
    compile 'org.somedomainname.UtilLib:library:1.0.0@aar'
}

task ndkBuild(type: Exec) {
    String MainDirectory = System.getProperty("user.dir") + '/app/src/main'
    println '************************************************************************'
    println "Main app directory for NDK build " + MainDirectory
    println '************************************************************************'

    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
       commandLine 'gradle-ndk-build.cmd', MainDirectory, '-j'
    }
    else {
       commandLine 'gradle-ndk-build', MainDirectory, '-j'
    }
}

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


android.applicationVariants.all { variant ->
    variant.assemble.doLast {
           rename_and_moveout_apk(variant)
    }
}

// allprojects {
//     tasks.withType(Compile) {
//         options.compilerArgs << "-Xlint:deprecation"
//     }
// }

def rename_and_moveout_apk(targetVariant) {
    // replace output apk name to <product>-<version>-<buildtype>-<githash>.apk
    def versionSuffix = targetVariant.buildType.versionNameSuffix ? targetVariant.buildType.versionNameSuffix : ""
    def versionName = targetVariant.mergedFlavor.versionName + versionSuffix;

    if (targetVariant.zipAlign) {
        def apkFinal = targetVariant.outputFile;
        def apkFinalNewName = "myApp-" + apkFinal.name.replace(targetVariant.buildType.name, versionName);
        copy {
            from "$apkFinal"
            into "$rootProject.projectDir/apk_release"
            rename ("$apkFinal.name", "$apkFinalNewName")
            println "*************** Renaming zipalign apk file from: ${apkFinal.name} to ${apkFinalNewName}"
        }
    }

}

gradle-ndk-build(用于调试参数的ndk-build的修改版本)

#!/bin/bash
export NDK_PROJECT_PATH=$1
export NDK_PROJECT_DIRECTORY=$1
bash -c "ndk-build"

目录结构

------ apk_release
------ app
-- -- ---- src
--     ------ lite
--     --  -- ---- java
--     --      -- ---- org
--     --          -- ---- somedomainname
--     --              -- ---- myApp
--     ------ main
--     --  ------ assets
--     --  ------ java
--     --  --  -- ---- org
--     --  --      ------ chilkatsoft
--     --  --      -- ---- somedomainname
--     --  --          -- ---- myApp
--     --  ------ jni
--     --  --  ------ armeabi
--     --  --  ------ armeabi-v7a
--     --  --  ------ mips
--     --  --  -- ---- x86
--     --  ------ libs
--     --  --  ------ armeabi
--     --  --  ------ armeabi-v7a
--     --  --  ------ mips
--     --  --  -- ---- x86
--     --  ------ obj
--     --  --  -- ---- local
--     --  --      ------ armeabi
--     --  --      --  -- ---- objs
--     --  --      --      -- ---- ndklib
--     --  --      ------ armeabi-v7a
--     --  --      --  -- ---- objs
--     --  --      --      -- ---- ndklib
--     --  --      ------ mips
--     --  --      --  -- ---- objs
--     --  --      --      -- ---- ndklib
--     --  --      -- ---- x86
--     --  --          -- ---- objs
--     --  --              -- ---- ndklib
--     --  -- ---- res
--     --      ------ drawable
--     --      ------ drawable-hdpi
--     --      ------ drawable-ldpi
--     --      ------ drawable-mdpi
--     --      ------ drawable-xhdpi
--     --      ------ drawable-xxhdpi
--     --      ------ layout
--     --      ------ raw
--     --      ------ values
--     --      -- ---- xml
--     -- ---- pro
--         -- ---- java
--             -- ---- somedomainname
--                 -- ---- myApp

【问题讨论】:

  • commandLine 'gradle-ndk-build', MainDirectory, '-j' a.为此工作的冷杉是---/app b。这会导致以下错误:任务':app:ndkBuild'的执行失败。 > 启动进程'command 'gradle_ndk_build'' 出现问题 解决方法:commandLine './gradle-ndk-build', MainDirectory, '-j' 脚本不是程序本身
  • “我不得不求助于使用 ndk-build 命令并将其作为一项任务执行:...这非常有效...” 怎么样?为什么?我收到此错误:“启动进程'命令'ndk-build''时出现问题”。我无法弄清楚如何让该行正确执行。

标签: android opencv android-ndk


【解决方案1】:

gradle 会自动在 jniLibs/ABI/ 中寻找 .so 文件。

您可以通过将其设置在 build.gradle 文件中来更改此行为,使其使用常规的 libs 目录:

android {
    sourceSets.main {
        jniLibs.srcDir 'src/main/libs'
    }
}

【讨论】:

  • 谢谢 - 工作得很好。我将编辑我的原始问题并添加最终的 build.gradle,以防它对其他人有用。
  • 我收到“jniLibs 无法解析”错误...首先。
  • 你使用的是哪个版本的 gradle 和 gradle 插件?
猜你喜欢
  • 2012-11-27
  • 2011-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-16
  • 2016-01-19
相关资源
最近更新 更多