针对不同处理器架构的Gradle android构建

针对不同处理器架构的Gradle android构建,android,opencv,gradle,multiprocessor,android-gradle-plugin,Android,Opencv,Gradle,Multiprocessor,Android Gradle Plugin,我想使用Gradle为4种不同的Android CPU处理器架构(armeabi armeabi-v7a x86 mips)构建4个独立的APK 我在libs文件夹中为4个CPU架构构建了本机OpenCV库 我希望每个apk只包含与正确的CPU架构相对应的OpenCV库 当前生成脚本如下所示: apply plugin: 'android' dependencies { compile fileTree(dir: 'libs', include: '*.jar') compil

我想使用Gradle为4种不同的Android CPU处理器架构(armeabi armeabi-v7a x86 mips)构建4个独立的APK

我在libs文件夹中为4个CPU架构构建了本机OpenCV库

我希望每个apk只包含与正确的CPU架构相对应的OpenCV库

当前生成脚本如下所示:

apply plugin: 'android'

dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
    compile project(':workspace:OpenCV4Android:sdk:java')
}

android {
    compileSdkVersion 11
    buildToolsVersion "18.1.0"

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }

        // Move the tests to tests/java, tests/res, etc...
        instrumentTest.setRoot('tests')

        debug.setRoot('build-types/debug')
        release.setRoot('build-types/release')

        flavorGroups "abi", "version"
        productFlavors {
            x86 {
                flavorGroup "abi"
            }
            arm {
                flavorGroup "abi"
            }
            mips {
                flavorGroup "abi"
            }
        }

    }
}
有人能帮我解决这个问题吗


干杯,

我没有gradle的答案,但我想我现在有了一个适用于任何Android构建工具的通用答案。以下是我关于如何为每个受支持的处理器体系结构创建单独的APK文件的想法:

  • 使用您使用的任何工具构建APK,其中包含您支持的所有本机代码库,例如armeabi、armeabi-v7a、x86和mips。我称之为“原始”APK文件

  • 使用任何zip/Unzip实用程序,最好使用命令行工具,将原始APK解压到一个空文件夹中,以便以后可以使用shell脚本或批处理文件将其自动化

  • 在原始APK解压缩到的文件夹中,删除META-INF子文件夹(其中包含签名,我们需要在所有修改后对APK重新签名,因此必须删除原始META-INF)

  • 更改为lib sub folder,并删除新APK文件中不需要的任何处理器体系结构的子文件夹。例如,只保留“x86”子文件夹,以便为Intel Atom处理器生成APK

  • 重要提示:不同体系结构的每个APK在AndroidManifest.xml中必须有不同的“versionCode”编号,例如armeabi-v7a的版本代码必须略高于armeabi的版本代码(请阅读此处的谷歌创建多个APK的说明:)。不幸的是,清单文件在APK中是编译的二进制形式。我们需要一个特殊的工具来修改那里的版本代码。见下文

  • 使用新版本代码修改清单,删除不必要的目录和文件后,重新压缩、签名并对齐较小的APK(使用Android SDK中的jarsigner和zipalign工具)

  • 对需要支持的所有其他体系结构重复此过程,创建版本代码略有不同(但版本名相同)的较小APK文件

  • 唯一悬而未决的问题是如何修改二进制清单文件中的“versionCode”。我很长一段时间都找不到解决方案,所以最后不得不坐下来,编写自己的代码来完成这项工作。作为起点,我选择了Prasanta Paul用Java编写的APKExtractor。我是老学校,对C++仍然比较满意,所以我的C++实用程序“AMIc”现在在GITHUB上:

    我发布了整个Visual Studio 2012解决方案,但整个程序是一个.cpp文件,可能可以在任何平台上编译。下面是一个示例Windows.bat文件,我使用它将名为atVoice.apk的“胖”apk拆分为4个较小的文件,分别名为atVoice_armeabi.apk、atVoice_armeabi-v7a.apk、atVoice_x86.apk和atVoice_mips.apk。实际上,我将这些文件提交到Google Play(请参阅我的应用程序),所有这些文件都可以完美地工作。请参阅,who发布了一个类似的Linux脚本

    @echo off
    REM    My "fat" apk is named atVoice.apk. Change below to whatever or set from %1
    set apkfile=atVoice
    del *.apk
    
    REM    My tools build atVoice-release.apk in bin project sub-dir. 
    REM    Copy it here for splitting.
    copy ..\bin\%apkfile%-release.apk %apkfile%.apk
    
    zip -d %apkfile%.apk META-INF/*
    
    REM ------------------- armeabi ------------------------
    unzip %apkfile%.apk AndroidManifest.xml
    copy/y %apkfile%.apk %apkfile%.zip
    zip -d %apkfile%.zip lib/armeabi-v7a/* lib/x86/* lib/mips/*
    aminc AndroidManifest.xml 1
    zip -f %apkfile%.zip
    ren %apkfile%.zip %apkfile%_armeabi.apk
    jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore d:\users\greg\.android\Hyperionics.keystore -storepass MyPass %apkfile%_armeabi.apk MyKeyName
    zipalign 4 %apkfile%_armeabi.apk %apkfile%_armeabi-aligned.apk
    del %apkfile%_armeabi.apk
    ren %apkfile%_armeabi-aligned.apk %apkfile%_armeabi.apk
    
    REM ------------------- armeabi-v7a ---------------------
    copy/y %apkfile%.apk %apkfile%.zip
    zip -d %apkfile%.zip lib/armeabi/* lib/x86/* lib/mips/*
    aminc AndroidManifest.xml 1
    zip -f %apkfile%.zip
    ren %apkfile%.zip %apkfile%_armeabi-v7a.apk
    jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore d:\users\greg\.android\Hyperionics.keystore -storepass MyPass %apkfile%_armeabi-v7a.apk MyKeyName
    zipalign 4 %apkfile%_armeabi-v7a.apk %apkfile%_armeabi-v7a-aligned.apk
    del %apkfile%_armeabi-v7a.apk
    ren %apkfile%_armeabi-v7a-aligned.apk %apkfile%_armeabi-v7a.apk
    
    REM ------------------- x86 ---------------------
    copy/y %apkfile%.apk %apkfile%.zip
    zip -d %apkfile%.zip lib/armeabi/* lib/armeabi-v7a/* lib/mips/*
    aminc AndroidManifest.xml 9
    zip -f %apkfile%.zip
    ren %apkfile%.zip %apkfile%_x86.apk
    jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore d:\users\greg\.android\Hyperionics.keystore -storepass MyPass %apkfile%_x86.apk MyKeyName
    zipalign 4 %apkfile%_x86.apk %apkfile%_x86-aligned.apk
    del %apkfile%_x86.apk
    ren %apkfile%_x86-aligned.apk %apkfile%_x86.apk
    
    REM ------------------- MIPS ---------------------
    copy/y %apkfile%.apk %apkfile%.zip
    zip -d %apkfile%.zip lib/armeabi/* lib/armeabi-v7a/* lib/x86/*
    aminc AndroidManifest.xml 10
    zip -f %apkfile%.zip
    ren %apkfile%.zip %apkfile%_mips.apk
    jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore d:\users\greg\.android\Hyperionics.keystore -storepass MyPass %apkfile%_mips.apk MyKeyName
    zipalign 4 %apkfile%_mips.apk %apkfile%_mips-aligned.apk
    del %apkfile%_mips.apk
    ren %apkfile%_mips-aligned.apk %apkfile%_mips.apk
    
    
    del AndroidManifest.xml
    del %apkfile%.apk
    :done
    

    Greg

    更新-自发布此帖子以来,gradle构建过程取得了很大进展,因此此答案可能不是推荐的最佳实践,新的更改甚至可能会阻止它。运用你自己的判断力

    为此,首先,必须将本机库分别放在以下文件夹层次结构中

    lib
     -armeabi
      -arm.so
      -*.so
    
    -

    然后压缩lib(不带“s”)文件夹(例如arm.zip和x86.zip),并将“zip”扩展名重命名为“jar”(例如arm.jar和x86.jar)。将这些JAR放在适当的文件夹中(例如armeabi/libs和x86/libs)。现在我们将包括每种口味的依赖项。但我们不能使用“编译文件”…'”。我们必须使用“编译文件…”

    e、 g

    ====

    这里有一个更复杂的环境。您不仅有处理器体系结构变体,而且还有处理器的调试库(.jar、.so)。这里的示例有用于Arm调试的Debug.jar和用于Arm发布的NonDebug.jar;对于Arm和X86都是如此。这种配置可以通过使用gradle来实现。请阅读我的SO答案,了解调试文件夹的结构

    android {
    compileSdkVersion 18
    buildToolsVersion "19.0.0"
    
    final DEBUG_ROOT = "build-types/debug"
    final RELEASE_ROOT = "build-types/release"
    project.ext.set("projRoot", "")
    buildTypes {
        debug {
            project.projRoot = DEBUG_ROOT
    
            dependencies {
                debugCompile files(DEBUG_ROOT+"/libs/Debug.jar")
            }
        }
    
        release {
            project.projRoot = RELEASE_ROOT
            dependencies {
                releaseCompile files(RELEASE_ROOT+"/libs/NonDebug.jar")
            }
            runProguard true
            proguardFile 'proguard.cfg'
        }
    }
    sourceSets {
    
        final PROJ_ROOT = project.ext.get("projRoot")
        final BUILD_TYPE_RES = PROJ_ROOT + "/res"
        main {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            java.srcDirs = ['src/main/java']
            //resources.srcDirs = ['src/main']
            //aidl.srcDirs = ['src/main']
            //renderscript.srcDirs = ['src/main']
            res.srcDirs = ['src/main/res',BUILD_TYPE_RES]
            assets.srcDirs = ['src/main/assets']
        }
    
        flavorGroups 'abi'
        productFlavors {
            arm {
                flavorGroup 'abi'
                final ARM_LIB_PATH = PROJ_ROOT + "/arm/libs/armeabi.jar"
                dependencies {
                    armCompile files(ARM_LIB_PATH)
                }
            }
            x86 {
                flavorGroup 'abi'
                final X86_LIB_PATH = PROJ_ROOT + "/x86/libs/x86.jar"
                dependencies {
                    x86Compile files(X86_LIB_PATH)
                }
            }
    
        }
    
        // Move the tests to tests/java, tests/res, etc...
        instrumentTest.setRoot('tests')
    
        // Move the build types to build-types/<type>
        // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
        // This moves them out of them default location under src/<type>/... which would
        // conflict with src/ being used by the main source set.
        // Adding new build types or product flavors should be accompanied
        // by a similar customization.
        debug.setRoot(DEBUG_ROOT)
        release.setRoot(RELEASE_ROOT)
    }
    
    android{
    编译DK18版
    buildToolsVersion“19.0.0”
    最终调试\u ROOT=“生成类型/调试”
    最终版本\u ROOT=“构建类型/版本”
    project.ext.set(“projRoot”,“projRoot”)
    建筑类型{
    调试{
    project.projRoot=DEBUG\u ROOT
    依赖关系{
    调试编译文件(DEBUG_ROOT+“/libs/DEBUG.jar”)
    }
    }
    释放{
    project.projRoot=RELEASE\u ROOT
    依赖关系{
    releaseCompile文件(RELEASE_ROOT+“/libs/NonDebug.jar”)
    }
    runProguard true
    proguard文件“proguard.cfg”
    }
    }
    源集{
    final PROJ_ROOT=project.ext.get(“projRoot”)
    最终构建类型\u RES=PROJ\u ROOT+“/RES”
    主要{
    manifest.src文件“src/main/AndroidManifest.xml”
    java.srcDirs=['src/main/java']
    //resources.srcDirs=['src/main']
    //aidl.srcDirs=['src/main']
    //renderscript.srcDirs=['src/main']
    res.srcDirs=['src/main/res',BUILD\u TYPE\u res]
    assets.srcDirs=['src/main/assets']
    }
    风味组“abi”
    产品风味{
    手臂{
    风味组“abi”
    final ARM_LIB_PATH=PROJ_ROOT+“/ARM/libs/armeabi.jar”
    依赖关系{
    armCompile文件(ARM\u LIB\u路径)
    }
    }
    x86{
    风味组“abi”
    最终X86_LIB_PATH=PROJ_ROOT+“/X86/libs/X86.jar”
    依赖关系{
    X86编译文件(X86_LIB_路径)
    }
    }
    }
    //将测试移动到tests/java、tests/res等。。。
    instrumentTest.setRoot('tests'))
    //移动
    
    lib
     -x86
      -x86.so
      -*.so
    
        flavorGroups 'abi'
            productFlavors {
                arm {
                    flavorGroup 'abi'
                    dependencies {
                        armCompile files('arm/libs/armeabi.jar')
                    }
                }
                x86 {
                    flavorGroup 'abi'
                    dependencies {
                        x86Compile files('x86/libs/x86.jar')
                    }
                }
    
        }
    
    android {
    compileSdkVersion 18
    buildToolsVersion "19.0.0"
    
    final DEBUG_ROOT = "build-types/debug"
    final RELEASE_ROOT = "build-types/release"
    project.ext.set("projRoot", "")
    buildTypes {
        debug {
            project.projRoot = DEBUG_ROOT
    
            dependencies {
                debugCompile files(DEBUG_ROOT+"/libs/Debug.jar")
            }
        }
    
        release {
            project.projRoot = RELEASE_ROOT
            dependencies {
                releaseCompile files(RELEASE_ROOT+"/libs/NonDebug.jar")
            }
            runProguard true
            proguardFile 'proguard.cfg'
        }
    }
    sourceSets {
    
        final PROJ_ROOT = project.ext.get("projRoot")
        final BUILD_TYPE_RES = PROJ_ROOT + "/res"
        main {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            java.srcDirs = ['src/main/java']
            //resources.srcDirs = ['src/main']
            //aidl.srcDirs = ['src/main']
            //renderscript.srcDirs = ['src/main']
            res.srcDirs = ['src/main/res',BUILD_TYPE_RES]
            assets.srcDirs = ['src/main/assets']
        }
    
        flavorGroups 'abi'
        productFlavors {
            arm {
                flavorGroup 'abi'
                final ARM_LIB_PATH = PROJ_ROOT + "/arm/libs/armeabi.jar"
                dependencies {
                    armCompile files(ARM_LIB_PATH)
                }
            }
            x86 {
                flavorGroup 'abi'
                final X86_LIB_PATH = PROJ_ROOT + "/x86/libs/x86.jar"
                dependencies {
                    x86Compile files(X86_LIB_PATH)
                }
            }
    
        }
    
        // Move the tests to tests/java, tests/res, etc...
        instrumentTest.setRoot('tests')
    
        // Move the build types to build-types/<type>
        // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
        // This moves them out of them default location under src/<type>/... which would
        // conflict with src/ being used by the main source set.
        // Adding new build types or product flavors should be accompanied
        // by a similar customization.
        debug.setRoot(DEBUG_ROOT)
        release.setRoot(RELEASE_ROOT)
    }
    
    src
    -main
      -jniLibs
        -armeabi
          -arm.so
        -armeabi-v7a
          -armv7.so
        -x86
          -x86.so
        -mips
          -mips.so
    
    android {
    ...
    splits {
    abi {
      enable true
      reset()
      include 'x86', 'armeabi-v7a', 'mips', 'armeabi'
      universalApk false
      }
     }
    }
    
    // map for the version code
    ext.versionCodes = ['armeabi-v7a':1, mips:2, x86:3]
    
    import com.android.build.OutputFile
    
    android.applicationVariants.all { variant ->
        // assign different version code for each output
        variant.outputs.each { output ->
            output.versionCodeOverride =
                project.ext.versionCodes.get(output.getFilter(OutputFile.ABI)) * 1000000 + android.defaultConfig.versionCode
        }
    }
    
    android {
        flavorDimensions "abi"
    
        productFlavors {
            fat {
                flavorDimension "abi"
                ndk {
                    abiFilters "x86", "armeabi-v7a", "armeabi"
                    versionCode = 0;
                }
            }
            arm {
                flavorDimension "abi"
                ndk {
                    abiFilter "armeabi"
                    versionCode = 1;
                }
            }
            armv7a {
                flavorDimension "abi"
                ndk {
                    abiFilter "armeabi-v7a"
                    versionCode = 3;
                }
            }
            x86 {
                flavorDimension "abi"
                ndk {
                    abiFilter "x86"
                    versionCode = 6;
                }
            }
        }
    }
    
    // Each APK needs a different version code when submitted to Google,
    // bump the versionCode we set in defaultConfig
    android.applicationVariants.all { variant ->
        // Ugly hard coded flavorDimensions position
        // If you have more than one flavorDimension, make sure to target the position for "abi"
        def abiVersion = variant.productFlavors.get(0).versionCode
    
        variant.mergedFlavor.versionCode = abiVersion * 1000 + android.defaultConfig.versionCode
    }
    
    android {
        // Rest of Gradle file
            splits {
                abi {
                enable true
                reset()
                include 'armeabi', 'armeabi-v7a', 'x86'
                universalApk true
            }
        }
    }
    
    //Ensures architecture specific APKs have a higher version code
    //(otherwise an x86 build would end up using the arm build, which x86 devices can run)
    ext.versionCodes = [armeabi:1, 'armeabi-v7a':3, x86:6]
    
    android.applicationVariants.all { variant ->
        // assign different version code for each output
        variant.outputs.each { output ->
            int abiVersionCode = project.ext.versionCodes.get(output.getFilter(OutputFile.ABI)) ?: 0
            output.versionCodeOverride = (abiVersionCode * 1000) + android.defaultConfig.versionCode
        }
    }