Our Android project includes some native libraries and we support only arbeabi-v7. So the generate apk only includes native libraries for this architecture.
Lately we added RenderScript that causes the generated apk to include librsjni.so and libRSSuport.so native for all the supported platform i.e. x86, arbeabi-v7 etc.
The build.gradle files changes to add RenderScript are:
defaultConfig {
// Other configs
renderscriptTargetApi 18
renderscriptSupportModeEnabled true
}
The generated apk libs folder looks like:
libs
- armeabi-v7
- librsjni.so
- libRSSuport.so
- Other native libs
- x86
- librsjni.so
- libRSSuport.so
- Other archs
- librsjni.so
- libRSSuport.so
This causes issues in phones where primary abi is non armeabi-v7 as the run time may think that non armeabi-v7 architecture are supported and tries to run it and app crashes.
As of now we have added the following to build.gradle :
ndk {
abiFilters "armeabi-v7a"
}
and in gradle.properties:
android.useDeprecatedNdk=true
Though this solves the issue, but it does seems like a hack or non standard solution.
Is there a standard or recommended way to solve this issue?
If you only use prebuilt libraries, your best option is to employ the splits feature:
android {
…
splits {
abi {
enable true
reset()
include "armeabi-v7a"
universalApk false
}
}
}
Related
I'm planning to migrate from ABI split to App Bundle feature. Currently I'm using this code:
def versionCodesAbi = ['x86': 1, 'x86_64': 2, 'armeabi-v7a': 3, 'arm64-v8a': 4]
splits {
abi {
enable true
reset()
include "x86", "x86_64", "armeabi-v7a", "arm64-v8a"
// "armeabi", "mips", "mips64" last three not needed and not supported currently
universalApk true
}
}
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def abi = versionCodesAbi.get(output.getFilter(OutputFile.ABI))
if (abi != null) {
output.versionCodeOverride =
abi * 1000 + variant.versionCode
}
}
}
which gives 4 APKs per ABI (+ universal one). The reason of using this code is to reduce app size, because of PanoWidget (uses NDK) and
renderscriptTargetApi 28
renderscriptSupportModeEnabled true
After removing splits configuration (+4001 to versionCode) and building Bundle I got .aab file, which converted to .apks (using bundletool) contains folder standalones/. Inside I have four "kinds" of APK, for x86, x86_64, armeabi-v7a and arm64-v8a ABIs. Everything looks fine for now.
Now I've noticed that apps code isn't using RenderScript at all, so I think it's redundant to use supportMode and targetApi. I've removed these two lines, tested on devices/emulator, everything works fine. So next I'm producing Bundle and now it doesn't have x86_64 APK version inside .apks archive... Should it be ommitted without RenderScript support? I'm still using VrPanoramaView and it probably have some specific NDK code for every ABI (don't see on GitHub)... Sadly I don't have x86 (32 or 64) device for testing and nom I'm afraid of releasing this Bundle... Am I missing smth, do I even need _64 version?
Edit:
Removing these two options in the build.gradle will remove the native libraries that were used by RenderScript: librsjni.so and libRSSupport.so. These two libraries will be removed for all ABIs.
Since after disabling RenderScript, you still have 3 ABIs, it looks like your app depends on other libraries which make use of native code, but don't provide the libraries for the x86_64 architecture, which is why the x86_64 directory disappears. This probably means that your app never worked properly on x86_64 before since the x86_64 directory would be loaded by the platform but some native libraries would be missing.
Eventually, you should identify which library brings these native libraries and see if they can also build the 64 bit version, but in the short term, nothing will break since the x86_64 devices also support x86 (32-bit) libraries.
Previous post:
If you have any *.bc files in your APK, the 64-bit libraries are removed from the APKs because those RenderScript files are 32-bit only and cannot be loaded in a 64-bit process.
If you migrate to a more recent version of RenderScript, the *.bc files won't be generated and the 64-bit native libraries will be present again in the APKs. Or if you don't need RenderScript at all, then remove those files completely.
Here is my problem. I am working on an already quite large Android application, and am adding a native library.
I would like to build only for armeabi-v7a while I am testing, for various reasons. My test phone has this abi, and I have issues building some third party libraries with some ABIs at the moment.
Now, I already googled / SEed for how to filter ABIs.
I have added in my app build.gradle the following :
splits {
abi {
enable true
reset()
include 'armeabi-v7a'
}
}
defaultConfig {
externalNativeBuild {
cmake {
abiFilters "armeabi-v7a"
}
}
}
To be safe, I duplicated this config in my modules build.gradle.
However, running a build results in CMake errors, saying it cannot find some tools for ABIs mips and x86_64. (I actually don't have them)
It would really help if someone could suggest why gradle would ignore the abi configuration entries, and how I could solve this.
In my app I'm using "de.mrmaffen:vlc-android-sdk:2.0.6" library and it's taking much storage but I can't find a smaller alternative.
So I decided as CommonsWare mentioned to use splits or product flavors but I can't get it to work.
I tried to follow the docs with no luck.
Any help appreciated.
Building the LibVLC Android SDK yourself
If you are using "de.mrmaffen:vlc-android-sdk:2.0.6", buid the LibVLC Android SDK yourself
As explained here, afterwards simply run this Gradle command: ./gradlew buildLibVlc
Building a specific version of the LibVLC Android SDK
cd vlc-android // if this folder doesn't exist yet, simply run ./gradlew cloneVlcAndroid
git tag // to list all release versions
git checkout {tag-name} // to checkout the git repo at the given tag
cd ..
./gradlew buildLibVlc // build it
Make sure you followed the Android compile instructions in terms of dependencies and check this:
git clone repo.
open up a command prompt in this repo. "cd" or change directory into
it.
git clone .
change directory into the vlc-android directory.
git tag
git checkout <tag_version>. In this case, 2.1.2.
cd ..
comment out both the compile/build of the build.gradle script that
you don't need.
./gradlew buildLibVlc
you should have a successful build with both the Java sources and
shared object (*.so) files in the jniLibs folder.
create a libs folder right next to the jniLibs folder if you
updated the gradle version.
Get it via Maven CentralJCenter
Just add this dependency to your project and you're good to go.
dependencies {
implementation 'de.mrmaffen:libvlc-android:2.1.12#aar'
}
I'm using this version. Remember to add JCenter to your project's build.gradle file:
allprojects {
repositories {
jcenter()
}
}
VLC Android SDK via JCenter supports the next ABIs: armeabi-v7a, arm64-v8a, x86 and x86_64.
You can filter specific ABIs in your app's build.gradle file (x86_64 and arm64-v8a are excluded):
android {
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
It's possible to check included files in mrmaffen's repository and via recent Android Studio versions:
Configure multiple APKs for ABIs
Add an abi block inside
your splits block. In your abi block, provide a list of desired ABIs. Source
android {
...
splits {
// Configures multiple APKs based on ABI.
abi {
// Enables building multiple APKs per ABI.
enable true
// By default all ABIs are included, so use reset() and include to specify that we only
// want APKs for x86 and armeabi-v7a.
// Resets the list of ABIs that Gradle should create APKs for to none.
reset()
// Specifies a list of ABIs that Gradle should create APKs for.
include "x86", "armeabi-v7a"
// Specifies that we do not want to also generate a universal APK that includes all ABIs.
universalApk false
}
}
}
Build multiple APKs
Click Build > Build APK to build all APKs for the currently selected
module in the Project pane.
Gradle creates the APKs for each ABI
into the project's build/outputs/apk/ directory.
Configure Build Variants
This page builds on the Configure Your Build Overview to show you how you can configure build variants to create different versions of your app.
Combine abi filters and build variants
Exclude abi from apk
android {
productFlavors {
x86 {
ndk {
abiFilter "x86"
}
}
arm {
ndk {
abiFilters "armeabi-v7a", "armeabi"
}
}
}
}
Multi-APK through ABI and density splits
How to reduce the number of APKs with ABI splits
Here’s
a code snippet that you can use to set version codes for ABI splits.
Give the x86_64 and x86 higher version numbers than ARM, as many x86
devices can run ARM code through an emulation layer, although with
lower performance.
If you don’t wish to manage too many APKs, target the most popular
ones (usually ARM and maybe x86) with a split APK and serve a
universal APK to everyone else.
It’s vital that you publish the universal APK on the Play Store with a lower
version number than all other ABI-specific packages.
If you need more flexibility for your Multi-APK setup, check out
Multi-APK through product
flavors.
Recommended Medium post to choose supported ABIs
In this
post
we will understand what is ABI, problems with the ABI split, and
alternative way to avoid those problems... For instance, Android supports the following ABIs :
mips, mips64, X86, X86–64, arm64-v8a, armeabi, armeabi-v7a
So you have to take a call on supporting 64-bit libraries based on
size vs performance criteria but mips, mips-64, and armeabi should be
removed without any hesitation.
Solve UnsatisfiedLinkError Problem on some devices
As explained here, 64-bit processors generate and check arm64 folder to load native libraries.
If your project does not have arm64 folder. Here is the solution:
build.gradle
defaultConfig {
...
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
You need to add this filters(abiFilters) to your app module’s
build.gradle files. So when your device try to run your app, it will
check gradle file and understands that it should not generate any
folder and use existing native library resources. Boom. Almost solved.
But still there is one more thing. Add this line to your gradle.properties to use deprecated Ndk.
gradle.properties
android.useDeprecatedNdk=true
You can use app bundle so you don't need to handle multiple apks. Google Play will handle it for you automatically. Plus this approach can provide additional benefits. For more details: https://developer.android.com/guide/app-bundle
I want to use some jni libraries. So I put binaries like this:
project
-app
--jnilibs
---armeabi
----libqwerty.so
and in my build.gradle (app module):
sourceSets.main {
jniLibs.srcDirs = ['jniLibs']
}
it works as expected, however when I add renderscript to my project:
renderscriptTargetApi 22
renderscriptSupportModeEnabled true
application doesn't see my jniLibs directory anymore. And the content of getApplicationInfo().nativeLibraryDir is: [libRSSupport.so, librsjni.so]
UPDATE: ok, I was able to resolve this issue with just renaming directory armeabi to armeabi-v7a. However what if I want to support some other cpu-architectures?
See the answer by a Google developer: renderscript libraries are not supported for Arm v6 and they have no plans to port them back. As for x86, MIPS, and 64-bit versions, Gradle will automagically add appropriate RS binaries to your APK.
I am using arcgis in my app and it bundles native dependencies that are large in size. I don't want to include the x86 dependency if it means reducing the size of the apk. How do I tell gradle to automatically exclude the x86 native library.
I tried removing it manually during the build. but it shows up again after rebuild.
how do I tell gradle to automatically exclude the x86 native library
Use splits:
android {
// other good stuff here
splits {
abi {
enable true
reset()
include 'armeabi-v7a'
universalApk false
}
}
}
This tells Android to build only an ARMv7 version of your APK. You would need to adjust the include line to list what APKs you want.
However, you may be better served using splits to just build a separate x86 APK file (have include 'x86', 'armeabi-v7a') and ship both, so you better support x86 but still have smaller files.