Background
I've recently started to develop some code using the NDK, and I've thought of a possible portability problem that could occur while developing using NDK.
The problem
Since NDK uses native code, it needs to be compiled per CPU architecture. This is a problem since the user needs to run the app no matter what CPU the device has.
Possible solutions I've found so far
I've noticed I can modify the file "jni/Application.mk" and use:
APP_ABI := armeabi armeabi-v7a x86
however, I don't know what I should do from this step on. Will the app contain all of the compiled code for each of the CPU architectures and automatically choose the correct one when running itself?
Also, what if there will be another CPU architecture that is unknown?
What will happen if I try to run the app on Google TV, which according to what I remember doesn't support the NDK at all?
Another solution I've found is the multi-apk support. However, I'm not sure I understand it. Does it all mean that you create the same APK, each time with a different configuration? No special automation tool from ADT to help with that?
If you don't set APP_ABI at all, or use
APP_ABI := all
in your Application.mk, then ndk-build will build for all architectures supported by your version of NDK. The latest one, to date r8d, will in addition to armeabi armeabi-v7a x86 build also for mips. When another architecture will be released, you will hopefully automatically get the APK built to support it.
When you application is installed on an Android device, the system will automatically choose the matching ABI version and install the correct shared libraries from the APK.
One drawback of this approach is that if the native libraries are big, your "monolithic" APK file may become huge. You can use the multi-APK approach to make user downloads smaller. The official site recommends: You should generally use multiple APKs to support different device configurations only when your APK is too large (greater than 50MB). You should carefully follow the version code guildlines if you choose this route.
Unfortunately, there are no trustworthy prophecies regarding NDK support on Google TV, but there seem to be no technical justification for its current unavailability. If and when it arrives, your ndk-build will take care of it automatically.
UPDATE Here is a simple process to maintain split APK. And by the way, the new Android TV does support NDK.
For the latest version (now r9) you have to specify in "jni/Application.mk"
APP_ABI := all
or
APP_ABI := armeabi armeabi-v7a x86 mips
without ./ndk_build will only build 'armeabi'
Related
I am working with Xamarin Forms and I published the Android app ad-hoc to disc.
What I would like to do is to give the apk to some people to test by uploading it to a web site I have so they can download it. Here's what was created:
com.xx.xx-arm64-v8a.apk
com.xx.xx-armeabi-v7.apk
com.xx.xx-x86_64.apk
com.xx.xx-x86.apk
com.xx.xx.apk
My question is twofold. Why does it create four apps and which of those should I give to the person? I assume it's okay to give the last in the list but if that's the case then why are the others created?
I also heard something about a new feature with Android that would allow smaller builds and just let a person download the build that is needed for their device. Can someone tell me if I could use that with these apks and if so how I would do it.
Thanks
Here are the list of options that I have selected:
Please open your Android Options, If you enable the Generate one package (.apk) per selected ABI selection, you will get serveral .apk file. If you want to generate one .apk file, just unselect it like following screenshot.
If you want to enable the Generate one package (.apk) per selected ABIselection. you can give the .apk file by Users' device(CPU Architectures).
Xamarin.Android supports the following architectures:
armeabi – ARM-based CPUs that support at least the ARMv5TE instruction set. Note that armeabi is not thread-safe and should not be used on multi-CPU devices.
Note
As of Xamarin.Android 9.2, armeabi is no longer supported.
armeabi-v7a – ARM-based CPUs with hardware floating-point operations and multiple CPU (SMP) devices. Note that armeabi-v7a machine code will not run on ARMv5 devices.
arm64-v8a – CPUs based on the 64-bit ARMv8 architecture.
x86 – CPUs that support the x86 (or IA-32) instruction set. This instruction set is equivalent to that of the Pentium Pro, including MMX, SSE, SSE2, and SSE3 instructions.
x86_64 CPUs that support the 64-bit x86 (also referred as x64 and AMD64) instruction set.
If you want to know more details about CPU Architectures, you can refer to this thread.
https://learn.microsoft.com/en-us/xamarin/android/app-fundamentals/cpu-architectures?tabs=windows
while you are creating an apk Please make sure your Architecture should be armeabi-7a and armeabi-v8a in Project Properties - > Android Options -> Advanced.
after that Clean Solution.
than Please go for .apk Archive it will create simple Signed Apk which you can distribute.
Hope it helps.
I have a few native libraries that are fairly big and bloating the application binary size. I looked into APK splitting but maintaining and QAing multiple APKs is not something I want to do.
So I'd like to exclude unused architectures in my release build flavour using the ndk.abiFilters Gradle property. (The debug builds I want to leave alone so that I can run the HAXM-accelerated emulator on x86).
I was thinking of just having armeabi-v7a and arm64-v8a in my release flavour, but I'm not sure whether it is worth worrying about the market share of the other architectures I'm dropping. After a lot of looking around I cannot find any reference to market share of ARMv6 (armeabi), MIPS, x86 or x86_64. My intuition tells me that the latter three are almost non-existent, but I'm not sure about ARMv6.
I found this link on a forum that has a list of phones that use various architectures. Is there something more reliable somewhere? Maybe with % of users similar to the Android version dashboard?
Anything else I may miss if I just build my native libs for armeabi-v7a and arm64-v8a (or armeabi, armeabi-v7a and arm64-v8a)?
First off, if you're worring about binary size, you don't really need arm64-v8a, all those devices can run the armeabi-v7a binaries just fine. Only if you really need to cram the last extra performance out of it, it might be worthwhile.
As for armeabi and ARMv6; Android itself doesn't officially support it any longer, since Android 4.4 (October 2013) - and since Android 4.0 it should be much less common (from that version, AOSP source requires modifications to still build for ARMv6). So in practice, if you aren't supporting versions below 4.4, you can drop that one without any significant loss.
Also, for x86; many of those devices ship with surprisingly decent emulation of arm binaries, so those can manage with the armeabi-v7a version just fine as well.
EDIT: The above was written in 2015; these days Play Store requires that apps include support for arm64-v8a. But these days the next question is more about whether you need to include armeabi-v7a at all, or if the market share of 32 bit devices is small enough to drop support for.
Including additional architectures will no longer have any impact on the binary size when using app bundles, as in that case Google Play will serve each device only the binaries that apply to that particular device. Not only that, but also application updates will be way smaller and faster.
Leaving the previous information for projects still not using app bundles:
Unfortunately, the Android Dashboard, as useful as it is, does not provide architecture information, nor does Google Analytics.
The Unity statistics used to provide statistics per architecture and CPU features. Note, however, that these are not general statistics, but only cover users of Unity applications/games. The information doesn't seem to be available in a public link anymore, so I have replaced the direct links with the latest snapshots in archive.org.
I was stuck with this problem when using Mapbox, then later found this article which was very useful.
Based on the below picture you just need armeabi-v7a and x86. Then based on Jose Gómez answer, I only added armeabi-v7a and didn't have any problem at all.
So add this line to your app.gradle
android {
defaultConfig {
//other configs
ndk {
abiFilters "armeabi-v7a"
}
}
}
If you're still worried about 2% - 3% of those who use x86 architecture, like ASUS ZenFone and lenovo phones then use this config instead in app.gradle
ndk {
abiFilters "armeabi-v7a", "x86"
}
Also for genymotion emulators you should use x86 architecture
UPDATE
If you get this error while publishing the apk in play store
Then use this
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"
}
And finally, I suggest you use app bundle for releasing the APK
When I read #mstorsjo answer I was a little bit confused how to really use only one (or two) native libs, even though it's quite simple and straightforward. Therefore, I will give here an example and some more explanation (based on my further research).
For each supported architecture we have to create a specific folder in the jniLibs folder and drop the .so file there. As an example, for supporting armeabi-v7a (32bit) and arm64-v8a (64bit):
|--app
|--|--src
|--|--|--main
|--|--|--|--jniLibs
|--|--|--|--|--armeabi-v7a
|--|--|--|--|--|--.so Files
|--|--|--|--|--arm64-v8a
|--|--|--|--|--|--.so Files
With armeabi-v7a you support more than 90% of the available devices, but since it is 32bit architecture, running it on 64bit devices will result in an loss of performance (20-30%) {1}. For each specific case it can be quite handy to check the actual amount of supported devices, which can be done within the Google Play Console in the Release management > Device catalog section, by specifying ABI as filter.
Attention
When you don't add binaries for all architectures you have to be aware of the following:
If your app contains any other native libraries, you'll have to make sure you only have the same versions of them as well. That is due to Android demanding that all loaded native libraries be built for the same architecture. For example, if the first native library loaded is armeabi-v7a, Android will look ONLY for armeabi-v7a libraries on all System.loadLibrary() calls after that. If it doesn't find that exact architecture, it'll throw the java.lang.UnsatisfiedLinkError exception. {1}
I ran into this problem because some of my dependencies used native libraries, whereby armeabi-v7a couldn't be loaded anymore.
I want to publish 2 apk on google store (made with unity), using the multi-apk feature.
I looked on internet and i tried this :
Created a Application.mk at ProjectFolder/jni/
with only APP_ABI := x86 or APP_ABI := armeabi armeabi-v7a depending on the lib I wanted to use.
Created 2 apk with 2 version code with Unity 'device filtrer' settings to x86 or Ar Mv 7, depending on the apk I wanted to build.
Published both apk on google develloper console.
The Apk have not the same size, so different libs are used. (With both lib, the apk weight 16 mo more, with only arm, like ~8mo more and with only x86 like ~8.5mo).
The problem is, 'native platform' detected by google console is always 'armeabi-v7a, x86' (both architectures), no matter the apk.
So I get a error message on google console saying that both apk have exaclty same device compatibility.
(PS: if I want 2 differents apk, it's because each lib take 8mo on the final apk, so 16mo for an empty project. And with google size limitation for apk...)
Update :
Ok the probleme was from a plugin (everyplay).It had libs for x86 and arm architechture. The solution was to remove the x86 plugin lib before building the arm apk, and to remove arm plugin lib to build the x86 apk.
I also noticed that changing APP_ABI in Application.mk was not necessary, the unity parameter device filtrer override it.
If Google is pointing this out, it means that somewhere Unity is overriding / including files for x86 even when you're trying to build for ARM. Here's a couple of things you should do
Open Unity's Player Settings, go to the Android tab, select Other Settingsand under Configuration -> Device Filter select whatever architecture you want to build for (Default is both)
This one is a little more un-intuitive. Just because you do step 1 above, doesn't ALWAYS mean that only 1 architecture gets built. If a plugin, it it's /lib folder includes ANY files that are for both architectures, then both architectures are built. So you need to manually comb through each plugi folder and ensure that there's no such files.
I am using Crosswalk browser for my current application, and after the building process it creates 2 libraries (Which are around 17 mb in size each).
And since that adds a big amount of overhead to the application, i was trying to separate the build into 2 different apk's one for arm and one for x86 architecture.
According to android documentation it is possible, but I was not able to found enough information.
Is it possible to build two times with each time only one version of crosswalk lib getting added ?
looking at crosswalk's make_apk.py, it seems that by default, different APKs are already generated for each platform.
You can specify which architectures you want to generate an APK for using the --arch option that can be set to either x86, x86_64 or arm.
The solution for me was to manually delete libxwalkcore.so under crosswalk project -> libs -> armeabi-v7a or x86.
Depending on for which architecture you wish to built delete the other .so file.
I am currently in the process of updating our project OpenSSL to 1.0.1i using http://wiki.openssl.org/index.php/Android.
Looking in the config file I found that OpenSSL has 2 Android build options: android-armv7 and android-x86.
I have been able to build the armv7 configuration and it appears to be working correctly on a Nexus 5 and a Kindle Fire 1st Gen.
What I am wondering is would my current library work if it were in the armeabi directory instead of the armeabi-v7a directory? I was not able to find sufficient information as to whether it matters if the OpenSSL is built with armv7 but my project libraries are built with older arm in mind.
Note: My minimum API level is 8.
What I am wondering is would my current library work if it were in the armeabi directory instead of the armeabi-v7a directory?
I think there are a couple questions here. First, can you put libssl and libcrypto in armeabi/. That's an Android question (not an OpenSSL question). I seem to recall Brian talking about this on the NDK mailing list (but I can't find it at the moment). I believe the idea is armeabi/ is a fallback if a more specific library is not found in, for example, armeabi-v7a/.
Second is, can you run ARMv7a version of libssl and libcrypto on other platforms. I believe ARMv7a added a few hypervisor extensions over ARMv7, so you should be OK since OpenSSL does not use them. However, you might find yourself in trouble if running on an older device with ARMv6 or ARMv5.
In this case, you might want to download an older version of the Android NDK that builds for ARMv5, and then place ARMv5 version of libssl and libcrypto in armeabi/. You can find older versions of the NDK at Android NDK about a third of the way down the page.
To be more specific, Android 2.2 is API 8, and it was released around May 2010. So you might want to fetch and build with Android NDK Revision 3 from March 2010. NDK R3 only supported armeabi and targeted ARMv5TE (from the CPU-ARCH-ABIS.TXT file). The download is http://dl.google.com/android/ndk/android-ndk-r3-linux-x86.zip.
OpenSSL does not follow the instructions at Standalone Toolchain for ARMv7a. Its missing the -mfloat-abi=softfp flag. You might have trouble calling a function that passes a float to the library from Java. There are not many of them, but one is RAND_add. The entropy estimate is passed as a float and after the incompatibility, your estimate will likely be 0.0f. See Hard-float and JNI on the NDK mailing list and [Bug #3080]: Android NEON and CFLAGS options.
Here's a note from the README's that you should also be aware of:
III.3. Automatic extraction of native code at install time:
-----------------------------------------------------------
When installing an application, the package manager service will scan
the .apk and look for any shared library of the form:
lib/<abi>/lib<name>.so
If one is found, then it is copied under $APPDIR/lib/lib<name>.so,
where $APPDIR corresponds to the application's specific data directory.
If you update the APK and nothing changes, then be sure to delete anything under lib\ or delte the APK first (they have a tendency to become "sticky").
Another issue you will likely encounter is building and compiling against 1.0.1. Be sure you provide a wrapper shared object with a different name. Otherwise, you will likely link against 0.9.8 at runtime, and not the 1.0.1 gear in your APK. That's because Zygote loads Android's version of OpenSSL, and that version is 0.9.8. Later, when Zygote forks to create your process, the link-loader will not map-in your version of OpenSSL because its already present from Zygote.
OpenSSL has 2 Android build options: android-armv7 and android-x86
I added android-x86 to the script in June 2014. I was able to get through the build with one patch: [Bug #3398] PATCH: fix broken compile on android-x86 with no-comp configure option. I don't have an x86 Android device, so I was not able to run the self tests on a device. Feedback is welcomed.