web-dev-qa-db-fra.com

Gradle Android build pour différentes architectures de processeur

Je veux créer 4 apks distincts pour 4 Android (armeabi armeabi-v7a x86 mips)) en utilisant Gradle.

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

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 construction actuel est le suivant:

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 ce problème s'il vous plaît?

À votre santé,

32
Blukee

À partir de Android Gradle Plugin version 13, vous pouvez maintenant générer des APK séparés en utilisant le nouveau mécanisme "split". Vous pouvez en lire plus ici .

La structure de fichiers par défaut pour placer vos fichiers .so est:

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

Notez que le nom du fichier .so est sans importance tant qu'il porte l'extension .so.

Ensuite, dans votre fichier de construction Gradle:

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 dans ext.versionCodes sont largement hors de propos, ceci est ici pour ajouter un décalage unique pour chaque type ABI afin que les codes de version ne se heurtent pas.

25
withoutclass

La solution split ABI APK pour gradle est la plus simple que j'ai trouvée jusqu'à présent. @withoutclass a une bonne écriture ici: https://stackoverflow.com/a/26129447/25457 J'ai dû faire référence à la documentation Android car il s'agit d'une nouvelle fonctionnalité cela peut encore changer: http://tools.Android.com/tech-docs/new-build-system/user-guide/apk-splits

Cependant, j'ai fini par devoir abandonner cette implémentation simple, car j'avais besoin de prendre en charge à la fois une version complète et des versions spécifiques à l'architecture. Vous pouvez rencontrer ce même problème si vous prenez en charge à la fois le Google Play Store (qui prend en charge les APK spécifiques à l'architecture) et l'Amazon Appstore (qui ne prend en charge que les gros APK).

Il peut être possible de le faire avec des fichiers APK fractionnés si vous pouvez ajouter un composant de saveur, mais pour l'instant, la saveur split + n'est pas encore prise en charge: https://code.google.com/p/Android/issues/ détail? id = 76469

J'ai fini par utiliser abiFilter, voir l'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'utilisation de universalApk définie sur 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
    }
}
14
Sebastian Gallese

Je n'ai pas de réponse graduelle, mais je pense avoir 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. Créez votre APK avec tous les outils que vous utilisez, contenant toutes les bibliothèques de code natives que vous prenez en charge, par exemple armeabi, armeabi-v7a, x86 et mips. Je l'appellerai le fichier APK "original".

  2. Décompressez votre fichier APK d'origine dans un dossier vide, avec n'importe quel utilitaire Zip/unzip, utilisez au mieux les outils de ligne de commande, afin de pouvoir l'automatiser avec un script Shell ou un fichier batch plus tard.

  3. Dans le dossier où l'APK d'origine a été décompressé, supprimez le sous-dossier META-INF (il contient les signatures, nous devrons signer à nouveau l'APK après toutes les modifications, donc le META-INF d'origine doit être supprimé).

  4. Passez au sous-dossier lib et supprimez les sous-dossiers de toutes les architectures de processeur que vous ne souhaitez pas dans le nouveau fichier APK. Par exemple, ne laissez que le sous-dossier 'x86' pour créer un fichier APK pour les processeurs Intel Atom.

  5. Important: chaque APK pour une architecture différente, doit avoir un numéro de `` versionCode '' différent dans AndroidManifest.xml, et le code de version pour par exemple armeabi-v7a doit être légèrement supérieur à celui d'armeabi (lire les instructions de Google pour créer plusieurs APK 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 versionCode ici. Voir ci-dessous.

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

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

Le seul problème en suspens est la manière de modifier ‘versionCode’ dans le fichier manifeste binaire. Je n'ai pas pu trouver de solution pour cela pendant longtemps, alors j'ai finalement dû m'asseoir et lancer mon propre code pour le faire. Comme point de départ, j'ai pris APKExtractor par Prasanta Paul, http://code.google.com/p/apk-extractor/ , écrit en Java. Je suis de la vieille école et toujours plus à l'aise avec C++, donc mon petit programme utilitaire "aminc" écrit en C++ est maintenant sur GitHub à:

https://github.com/gregko/aminc

J'ai posté l'intégralité de la solution Visual Studio 2012, mais l'ensemble du programme est un seul fichier .cpp qui peut probablement être compilé sur n'importe quelle plate-forme. Et voici un exemple de fichier Windows .bat que j'utilise pour diviser mon apk "fat" nommé atVoice.apk en 4 fichiers plus petits 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 sur https://play.google.com/store/apps/details?id=com.hyperionics.avar ) et tout fonctionne parfaitement. Voir aussi ce projet Github de Jorge Suárez de Lis , qui publie un script similaire pour 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

3
gregko