I want to build 4 separate apks for 4 different Android CPU processor architectures (armeabi armeabi-v7a x86 mips) using Gradle.
I have native OpenCV libraries built for 4 CPU architectures in the libs folder.
libs
-armeabi
-armeabi-v7a
-x86
-mips
I want to each apk only contains the OpenCV library corresponding to the correct CPU architecture.
The current build script is as below:
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"
}
}
}
}
Can someone help me to resolve this please?
Cheers,
The split ABI APK solution for gradle is the simplest I have found so far. @withoutclass has a good writeup here: https://stackoverflow.com/a/26129447/254573 I had to reference the Android documentation since this is a new feature that can still change: http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
However, I ended up having to abandon this simple implementation since I needed to support both a fat build and architecture specific builds. You might encounter this same issue if you support both the Google Play store (which supports architecture specific APKs) and the Amazon Appstore (which supports only fat APKs).
It might be possible to do this with split APKs if you can add a flavor component, but as of now split+flavor is not yet supported: https://code.google.com/p/android/issues/detail?id=76469
I ended up using the abiFilter, see sample code below:
Update Using universalApk set to true solves this solution, simply adds time to build each apk.
UPDATE - since the time of this posting, there have been much progress in the gradle build process, thus this answer might not be the recommended best practice and new changes might even brake it. Use your own discretion.
To do that, first, you must put the native libraries in the following folder hierarchy separately
-
then zip the lib(without 's') folders (e.g. arm.zip and x86.zip) and rename the 'zip' extension to 'jar' (e.g. arm.jar and x86.jar). Put these jars in a appropriate folders (e.g. armeabi/libs and x86/libs). Now we are going to include the dependencies for each flavor. But we cannot use "compile file '....'". We have to use "flavorCompile file '...'"
e.g.
====
Here is more complex environment. You not only have processor architecture variants but you also have debug libraries(.jar,.so) for the processors. The example here has as Debug.jar for Arm debug and NonDebug.jar for Arm release; and *.so for both Arm and X86. Such configuration can be achieved by using gradle ExtraPropertiesExtension Please read my SO answer here, https://stackoverflow.com/a/19941684/319058 , to understand how the debug folders can be structured.
}
I don't have a gradle answer, but I think I now have a generic answer for any Android build tool. Here is my idea on how to create separate APK files for each supported processor architecture:
Build your APK with any tools you use, containing all native code libraries you support, e.g. armeabi, armeabi-v7a, x86 and mips. I'll call it the 'original' APK file.
Unzip your original APK into an empty folder, with any zip/unzip utility, best use command line tools, so that you could automate it with a shell script or batch file later.
In the folder where original APK was uncompressed to, delete META-INF sub-folder (this contains the signatures, we'll need to re-sign the APK after all the modifications, so the original META-INF must be deleted).
Change to lib sub-folder, and delete the sub-folders for any processor architectures you don't want in the new APK file. For example, leave only 'x86' sub-folder to make an APK for Intel Atom processors.
Important: each APK for a different architecture, must have a different 'versionCode' number in AndroidManifest.xml, and the version code for e.g. armeabi-v7a must be slightly higher than the one for armeabi (read Google directions for creating multiple APKs here: http://developer.android.com/google/play/publishing/multiple-apks.html ). Unfortunately, the manifest file is in a compiled binary form inside the APK. We need a special tool for modifying the versionCode there. See below.
Once the manifest is modified with a new version code, and unnecessary directories and files deleted, re-zip, sign and align your smaller APK (use jarsigner and zipalign tools from Android SDK).
Repeat the process for all other architectures you need to support, creating smaller APK files with slightly different version codes (but the same version name).
The only outstanding issue is the way to modify ‘versionCode’ in binary manifest file. I could not find a solution for this for a long time, so finally had to sit down and crank my own code to do this. As the starting point, I took APKExtractor by Prasanta Paul, http://code.google.com/p/apk-extractor/, written in Java. I’m the old school and still more comfortable with C++, so my little utility program 'aminc' written in C++ is now on GitHub at:
https://github.com/gregko/aminc
I posted the entire Visual Studio 2012 solution, but the whole program is a single .cpp file which probably can be compiled on any platform. And here is a sample Windows .bat file I use to split my "fat" apk named atVoice.apk into 4 smaller files named atVoice_armeabi.apk, atVoice_armeabi-v7a.apk, atVoice_x86.apk and atVoice_mips.apk. I actually submit these files to Google Play (see my app at https://play.google.com/store/apps/details?id=com.hyperionics.avar) and all works perfectly. Please also see this Github project by Jorge Suárez de Lis, who posts a similar script for Linux.
Greg
As of Android Gradle Plugin version 13 you can now generate seperate APK's using the new "split" mechanism. You can read about it here.
The default file structure for placing your .so files is:
Note that the name of the .so file is unimportant as long as it has the .so extension.
Then in your Gradle build file:
and
Note that the version codes above in ext.versionCodes are largely irrelevant, this is here to add a unique offset for each ABI type so version codes do not clash.