Gradle android build pour différentes architectures de processeurs

je veux construire 4 apk séparés pour 4 architectures de processeurs Android (armeabi armeabi-v7a x86 mips) en utilisant Gradle.

j'ai des bibliothèques natives OpenCV construites pour 4 architectures CPU dans le libs dossier.

libs
    -armeabi
    -armeabi-v7a
    -x86
    -mips

je veux que chaque apk ne contienne que la bibliothèque OpenCV correspondant à la bonne architecture CPU.

le script de compilation actuel est comme suit:

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

    }
}

quelqu'un Peut-il m'aider à résoudre cette s'il vous plaît?

santé,

28
demandé sur Blukee 2013-10-09 13:51:56

4 réponses

à partir de la version 13 du Plugin Gradle D'Android, vous pouvez maintenant générer des APK séparés en utilisant le nouveau mécanisme "split". Vous pouvez lire à ce sujet ici.

la structure de fichier par défaut pour placer votre .so files is:

src
-main
  -jniLibs
    -armeabi
      -arm.so
    -armeabi-v7a
      -armv7.so
    -x86
      -x86.so
    -mips
      -mips.so

notez que le nom du .donc le dossier est sans importance tant qu'il a le .donc extension.

puis dans votre fichier Gradle build:

android {
...
splits {
abi {
  enable true
  reset()
  include 'x86', 'armeabi-v7a', 'mips', 'armeabi'
  universalApk false
  }
 }
}

et

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

notez que les codes de version ci-dessus ext.les codes de version sont largement hors de propos, c'est ici pour ajouter un offset unique pour chaque type D'ABI afin que les codes de version ne s'opposent pas.

24
répondu withoutclass 2015-01-13 17:43:40
https://stackoverflow.com/a/26129447/254573 J'ai dû faire référence à la documentation Android car il s'agit d'une nouvelle fonctionnalité qui peut encore changer: http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits

cependant, j'ai fini par devoir abandonner cette simple implémentation car j'avais besoin de supporter à la fois une grosse Construction et constructions spécifiques à l'architecture. Vous pourriez rencontrer ce même problème si vous soutenez à la fois le Google Play store (qui soutient APKs spécifiques à l'architecture) et L'Amazon Appstore (qui soutient seulement fat APKs).

il pourrait être possible de faire cela avec split APKs si vous pouvez ajouter un composant de saveur, mais à partir de Maintenant split + saveur n'est pas encore pris en charge:https://code.google.com/p/android/issues/detail?id=76469

j'ai fini par utiliser l'abiFilter, voir Exemple de code ci-dessous:

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
}

mise à Jour À l'aide de universalApk true résout cette solution, ajoute simplement du temps pour construire chaque apk.

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
    }
}
13
répondu Sebastian Gallese 2017-05-23 11:46:28

mise à jour-depuis le moment de cet affichage, il y a eu beaucoup de progrès dans le processus de construction du gradle, donc cette réponse pourrait ne pas être la meilleure pratique recommandée et de nouveaux changements pourraient même la freiner. Utiliser votre propre discrétion.

pour ce faire, vous devez d'abord placer les bibliothèques natives dans la hiérarchie de dossiers suivante séparément

lib
 -armeabi
  -arm.so
  -*.so

-

lib
 -x86
  -x86.so
  -*.so

puis fermez les dossiers lib(sans 's') (par exemple arm.zip et x86.zip) et renommer l'extension "zip" à 'jar' (par exemple arm.jar et x86.pot.) Placez ces bocaux dans des dossiers appropriés (par exemple armeabi/libs et x86/libs). Maintenant, nous allons inclure les dépendances pour chaque saveur. Mais nous ne pouvons pas utiliser "compiler fichier"....'". Nous devons utiliser "flavorCompile fichier '...'"

e.g.

    flavorGroups 'abi'
        productFlavors {
            arm {
                flavorGroup 'abi'
                dependencies {
                    armCompile files('arm/libs/armeabi.jar')
                }
            }
            x86 {
                flavorGroup 'abi'
                dependencies {
                    x86Compile files('x86/libs/x86.jar')
                }
            }

    }

====

voici un environnement plus complexe. Vous avez non seulement des variantes de l'architecture du processeur, mais vous avez aussi des bibliothèques de débogage (.jar,.so) pour les transformateurs. Le exemple ici a que de Débogage.jar pour Bras de débogage et NonDebug.Jarre pour libération du bras;*.donc pour Arm et X86. Une telle configuration peut être obtenue en utilisant gradle ExtraPropertiesExtension s'il vous Plaît lire mes réponses ici, https://stackoverflow.com/a/19941684/319058 , pour comprendre comment le débogage des dossiers peuvent être structurés.

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

}

3
répondu Win Myo Htet 2017-05-23 10:30:08

Je n'ai pas de réponse de gradle, mais je pense que j'ai maintenant une réponse générique pour tout outil de construction Android. Voici mon idée sur la façon de créer des fichiers APK séparés pour chaque architecture de processeur prise en charge:

  1. Construisez votre APK avec tous les outils que vous utilisez, contenant toutes les bibliothèques de code natives que vous supportez, par exemple armeabi, armeabi-v7a, x86 et mips. Je l'appellerai le fichier APK "original".

  2. décompressez votre APK original dans un dossier vide, avec n'importe quel utilitaire zip / unzip, utilisez au mieux les outils en ligne de commande, pour que vous puissiez l'automatiser avec un script shell ou un fichier batch plus tard.

  3. dans le dossier où APK original n'a pas été compressé, supprimer le sous-dossier META-INF (il contient les signatures, nous aurons besoin de re-signer L'APK après toutes les modifications, de sorte que le META-INF original doit être supprimé).

  4. modifier le sous-dossier lib, et supprimer les sous-dossiers pour les architectures de processeurs que vous ne voulez pas dans le nouveau fichier APK. Par exemple, ne laissez que le sous-dossier 'x86' pour créer un APK pour les processeurs Intel Atom.

  5. Important: chaque APK pour une architecture différente, doit avoir un numéro de 'code de version' différent dans AndroidManifest.xml, et le code de version pour par exemple armeabi-v7a doit être légèrement plus élevé que celui pour armeabi (lisez Google directions for creating multiple APKs ici:http://developer.android.com/google/play/publishing/multiple-apks.html). Malheureusement, le fichier manifeste est sous une forme binaire compilée à l'intérieur de L'APK. Nous avons besoin d'un outil spécial pour modifier le code de version. Voir ci-dessous.

  6. Une fois que le manifeste est modifié avec un nouveau code de version, et les répertoires et fichiers inutiles supprimés, re-zip, signez et alignez votre plus petit APK (utilisez les outils jarsigner et zipalign de Android SDK).

  7. Répétez le processus pour tous les autres architectures que vous devez prendre en charge, la création de petites APK fichiers avec des codes de version légèrement différents (mais le même nom de version).

La seule question en suspens est la façon de modifier ‘versionCode’ dans le fichier manifeste binaire. Je n'ai pas pu trouver une solution pour cela pendant longtemps, donc finalement dû s'asseoir et manier mon propre code pour le faire. Comme point de départ, J'ai pris APKExtractor de Prasanta Paul, http://code.google.com/p/apk-extractor/, écrit en Java. Je suis l'ancienne école et encore plus à l'aise avec C++, donc mon petit utilitaire 'aminc' écrit en C++ est maintenant sur GitHub à:

https://github.com/gregko/aminc

j'ai posté toute la solution Visual Studio 2012, mais l'ensemble du programme est un single .fichier cpp qui peut probablement être compilé sur n'importe quelle plate-forme. Et voici un exemple de Windows .le fichier bat que j'utilise pour diviser mon apk "fat" nommé atVoice.apk en 4 petits fichiers nommés atVoice_armeabi.apk, atVoice_armeabi-v7a.apk, atVoice_x86.apk et atVoice_mips.apk. En fait, je soumets ces fichiers à Google Play (voir mon application àhttps://play.google.com/store/apps/details?id=com.hyperionics.avar