My project includes native libraries compiled only for ARMv7. When I try to install it on Samsung Galaxy Y (which has ARMv6), I get INSTALL_FAILED_CPU_ABI_INCOMPATIBLE error.
However, I want to make my application deployable for every devices, no matter they have ARMv7 or not. If target device is an ARMv7, using native library will be a plus. That's it. For the rest of devices, application will do its job without calling native methods. (Because of libary has a large .so file, I don't want to include compilations for other architectures).
Any ideas to overcome this error ? How can I tell Android to ignore native libraries compiled for different architectures and make my APK deployable ?
PS. I'm seeking soultions rather than "creating mutiple APKs"
Thanks
So, by looking trough the NDK documentation I found this interesting hint (look inside the NDK folder in docs/Programmers_Guide/html/md_3__key__topics__c_p_u__support__chapter_1-section_8__a_b_is.html):
put your library inside assets folder
at runtime detect the cpu architecture using this java call: String arch = System.getProperty("os.arch");
If the result is something like "armv71" copy the lib from your assets to your data directory in the lib subdir: /data/data/<package_name>/lib/
Now you can safely call System.loadLibrary(...)
Related
I would like to use a shared library, that is compiled for arm64, on Android. I have my .so file inside a aarch64-linux-gnu folder, but for other libraries I have instead a aarch64-linux-android folder.
Please can these libraries compiled for aarch64-linux-gnu run on an arm64 Android device? What do these names stand for precisely? I know that aarch64 refers to the arm64 processor architecture but I don't know how the operating system is related here.
Thank you!
Android and ARM my have some libraries that are the same. Basically the SO file has to be able to find all the libraries it was linked against to run, and the versions need to match up so nothing breaks. This is risky, and it is generally safest to compile the entire program on your target machine. You can see if everything can be located/what is missing using:
ldd /path/to/file.so
this will give you a list of libraries and where the file thinks they are - or ??? if it can't find it. You need to double check and see if the results of this look OK.
Even if all dependencies are found, mismatch in versions or architecture will cause the program to break at run-time. You need to extensively test the use of the externally linked library and even then you may miss some cases that break your program. For this reason I would try and get the source code if possible, and re-compile everything on the target machine.
Assume we have a jni folder structure below.
armeabi
a.so
b.so
armeabi-v7a
a.so
On a ARMv7-based device, I want to load b.so, but the there is no b.so under folder "armeabi-v7a", so will the system report a not found library error or use the b.so under folder "armeabi"?
And even more, what order does system look for a so file among armeabi,armeabi-v7a,arm64-v8a,x86,x86_64? For example, on a x86_64-based device, system look for the so file in folder x86_64 first, but if not found, will system continue to look for the file in x86,arm64-v8a,armeabi-v7a,armeabi in sequence?
Have you seen: http://developer.android.com/ndk/guides/abis.html#am
I believe it answers your questions:
Both the Play Store and Package Manager expect to find NDK-generated
libraries on filepaths inside the APK matching the following pattern:
/lib/<abi>/lib<name>.so
If the system does not find the native shared libraries where it expects
them, it cannot use them. In such a case, the app itself has to copy the
libraries over, and then perform dlopen().
Further down the page is this particular bit:
Automatic extraction of native code at install time
When installing an application, the package manager service scans the APK, and looks for any shared libraries of the form:
lib/<primary-abi>/lib<name>.so
If none is found, and you have defined a secondary ABI, the service scans for shared libraries of the form:
lib/<secondary-abi>/lib<name>.so
When it finds the libraries that it's looking for, the package manager copies them to /lib/lib.so, under the application's data directory (data/data//lib/).
If there is no shared-object file at all, the application builds and installs, but crashes at runtime.
So in your case if you are on a armeabi-v7a architecture, you'll have to copy the lib/armeabi/libb.so file over and use dlopen() as the PackageManager knows nothing of what needs to be loaded in your app but did find something in the lib/armeabi-v7a directory.
In addition to Morrison Chang's answer, one could always consider dropping the armeabi part altogether (so you don't need two copies of the lib, which I guess is large). In practice, there are very few modern devices (running Android 4.0 or newer) that support armeabi but not armeabi-v7a (and none since Android 4.4). See https://stackoverflow.com/a/30924571/3115956, https://stackoverflow.com/a/28926267/3115956 for a few cases of that. That will reduce the file size without losing support for any significant part of your target userbase, assuming that you require Android 4.0. If you still target older versions, it's more of a reason to keep armeabi though.
Too less memory in HW phones and tablets, but HAXM works fine?
My basic problem is that my app (most likely JNI dynamic library loaded by my Java app) is too large. The Java app crashes without calling the JNI instructions using HW phones and templates. However it works fine with the HAXM emulator that just enlarge the primary memory of itself.
I need to reduce the size of the JNI dynamic library, I assume.
I have a huge number of C-functions, most of them not used?
Covering an topic area of application programming, more than 64000 entries and 995 are used. Making a C-program in Win32 the linker filter the unused stuff away. Real smooth.
However in Android Studio I have as usual a Java app written using a dynamic JNI library, that is the Android JNI standard use. There is no linker determine what functions are used or not. However I have a front-end JNI file that calls the other bunch of C-functions. The Java system do not analyse what JNI functions are called and find the tree of calls being able to sort it out, I assume? (Nor would a DLL in WIn32 do because it don't know what will be called, I assume.)
The static lib approach might work, how do I do it?
One idea is to create a static lib of the huge number of C-functions and when the standard Android JNI uses it by calls from the front end JNI file, I assume that this will only use the functions of the static library needed and drop the rest, like when linking for the Win32 C-program. I might be wrong?
Well, I tried to find out how to do a static lib in the Android Studio, and also how to use a proprietary static lib to be linked into the JNI dynamic lib. However it is very hard to find out by googling or searching by stackoverflow. I assume this approach is rare.
Does anyone know how to make a static JNI lib in Android Studio
Does anyone know how to use a static JNI lib in Android Studio making a Android app with a
JNI front-end calling the static lib functions?
And is able to describe it?
It might not be possible in the JNI and then it is good to know it is
impossible. Is it possible to get this optimisation made this way?
You can use a static library in Android Studio, but you should build it with ndk-build using Android.mk as configuration.
In your build.gradle you can refer to this prebuilt library "c:\android\obj\libmystatic.a":
model {
android.ndk {
moduleName = "hello-jni"
abiFilters += armeabi-v7a
ldFlags += "-LC:/android/obj"
ldLibs += "mystatic"
}
}
The issue is how to disguard unused functions in a JNI file
Well I answer my own question with the help of #Michaels hint.
The basic problem is a too huge APK for the HW Samsung Tab3 so it crashes, while the HAXM works fine. Using a small JNI dummy I initially made works in the Tab3 but not the real JNI code. My assumption is it is too large, well is it?
The idea of making a static lib using it when making the dynamic automatic JNI lib is redundant if the optimisations are available in other ways. The horror is to manually sort out what is used among more than 64K entries. Thanks in anyway #Alex.
Yes there are optimisations possible by setting the following flags in the app gradle:
android.ndk {
moduleName = "XXXX"
cppFlags.add("-ffunction-sections")
cppFlags.add("-fdata-sections")
cppFlags.add("-fvisibility=hidden")
CFlags.add("-ffunction-sections")
CFlags.add("-fdata-sections")
CFlags.add("-fvisibility=hidden")
ldFlags.add("-Wl,--gc-sections")
/*
* Other ndk flags configurable here are
* cppFlags += "-fno-rtti"
* cppFlags += "-fno-exceptions"
* ldLibs = ["android", "log"]
* stl = "system"
*/
}
This reduces my APK from 11Mb to 7Mb (removing the unused functions in the JNI part). And some changes in the app, reducing the Win64 app from 0,9Mb to 0,6Mb, reducing the APK to 6Mb. Setting the Build variant to arm7 debug (Tab3) the APK is 2,7MB but still crashes (on the load jni library command in the Main java).
How do I know the release size? - Is it possible to test?
The issue is how large the APK will be in release mode (I have the assumption it is smaller than debug mode) but release versions must be signed etc and my intension is not releasing the app for market but testing the size of it, see if the planned app possibly can fit in a commercial Android HW unit. If not I have to make a general redesign of my planned app, however being much wiser. But redesigning is a huge work and I rather like to be certain the original plan was impossible? Seeing apps like MS Office copies I assumed it must be possible.
Being trying to fiddle with the Build/Edit flavours the Gradle just complains putting away the debugging capabilities, so that was a dead end.
So the present question is:
Is there any possibility to find out and try the app in release shape without commercially releasing it?
This question already has an answer here:
UnsatisfiedLinkError on Android 5.0 Lollipop
(1 answer)
Closed 7 years ago.
I am loading my native library by:
try {
System.loadLibrary("myNative");
} catch (UnsatisfiedLinkError e) {
//java.lang.UnsatisfiedLinkError here
System.load("/data/data/com.my.app/app_native/libmyNative.so");
}
The above code is finally packaged to a Jar file.
In another project, I load the above Jar with DexClassLoader:
DexClassLoader dexClassLoader = new DexClassLoader(jarPath,
optJarPath,
getDir("native", Context.MODE_PRIVATE),
getClassLoader());
Notice that when constructing this dexClassLoader instance, I have specified the path where the native code is located, that's getDir("native", Context.MODE_PRIVATE).
(I am using NDK release 10 to generate the native library. When the native code file libmyNative.so is generated, my java code (which packaged to final Jar) checks the CPU architecture type & copy the right one to getDir("native", Context.MODE_PRIVATE).)
The above code works fine on other devices except Android 5.0 Lollipop. When run on Android 5.0 Lollipop device, I constantly get the following error:
java.lang.UnsatisfiedLinkError: dlopen failed: "/data/data/com.my.app/app_native/libmyNative.so" is 32-bit instead of 64-bit
at java.lang.Runtime.load(Runtime.java:331)
at java.lang.System.load(System.java:982)
How to solve this problem?
You appear to be attempting to use a 32-bit library on a 64-bit target. If you can't provide a 64-bit one, you will have to convince Android to fallback to 32-bit mode to accommodate the library.
Apparently, compatibility mode (the source seems to call it an ABI override) is normally set at install time, by the installer discovering that only 32-bit (and not 64-bit) libraries are available. But in your case, the library is not visible at install time, so that won't work.
If you place a "dummy" 32-bit lib in the apk that is the closest match for the 64-bit device's needs, then the system will hopefully configure your app to run in 32-bit compatibility mode so that later loading your real 32-bit lib will actually work.
I don't know if the 32-bit lib would need to be a real one vs. an empty file with the correct location and plausible name, but the hello-jni project from the ndk samples folder's libhello-jni.so should should work. You won't need any corresponding java code, just to have a native library the installer can discover (however, calling into it as a test might not be a bad idea).
It is possible there may be some other way(s) to trigger this, such as something in the manifest (though nothing is mentioned in the documentation). I'd be less likely to suspect that any measures at runtime will work, since this mode may already be firmly set before any of your code runs (it looks like you may actually end up with two instances of zygote running on such a system, one 64 bit and the other 32, with the idea being that apps get launched by whichever one is believed to be appropriate).
I am writing a game, and I have huge native library, I am trying to load my native library in my main activity like
static {
try {
System.loadLibrary("mylib");
} catch (UnsatisfiedLinkError e) {
Log.d(TAG, "Unsatisfied Link error: " + e.toString());
}
}
I have tested this code on many devices in house, I don't get this error. but from my published I do get logs with "Caused by: java.lang.UnsatisfiedLinkError: Cannot load library". NOTE: This crash is not universal, only few people are getting this crash
Is it problem where the library is put in exported apk ? I have now library put into /libs/armabi/libmylib.so automatically by eclipse?
Is it some thing to do with version of android ? I am supporting android versions since 2.3 (API level 9)
or do I need to load library from different place?
or am I missing something very important
Is it problem with whole app being installed on SDCARD ?
More Info on crash is : load_segments: 68 failed to map segment from mylib.so
Considering that your app works correctly on most of the devices and only sometimes gives you the error, it's safe to assume that the library is packaged correctly in the APK and that its size/memory footprint is also acceptable.
As such, I'll go on the whim and suggest that the problem is with the build/compile architecture of the library. Most newer Android devices use ARM7 processor. It's quite likely that your library is compiled against ARM7 architecture as well. Some older devices, especially those running Android 2.3, use ARM6 processor (I have one such device that I use for testing - LG GT540 running Android 2.3.3), which is not forward-compatible with ARM7 architecture. I have seen a crash with error similar to the one you indicated (load_segments: 68 failed to map segment from mylib.so) when I attempted to run an app designed for ARM7 on my old ARM6 phone.
There are three ways of going around this issue:
Compile the library against both architectures and include two separate .so files into the apk. Then at runtime determine the type of processor and load the correct one. Frankly, I don't know if this is even possible.
Create two separate apk files - one for ARM6 and one for ARM7 - then use filters in the manifest to specify the corresponding architecture. You can upload both of them to google play for the same app - and filters in the manifest will control which one is downloaded to which device.
Only support ARM7 architecture by specifying device requirements in your manifest. You will loose some customer audience, but will have less work for yourself maintaining the two versions of the app.
EDIT: According to NDK documentation, it can produce multiple libraries for different architectures in one go. You can control specifically which CPUs you want to target by adding the following line into the Application.mk file:
APP_ABI := arch1 arch2 arch3 ...
for example,
APP_ABI := armeabi armeabi-v7a mips
Then the build process will create different versions of the native library. These versions will need to be placed in the final apk in directory
lib/<abi>/lib<name>.so
where is the name of the architecture. You will then load the library with
System.loadLibrary("<name>");
Alternatively you can build separate apk files for each architecture then use multi-apk functionality on google play.
You can find all the information on architecture targeting in file CPU-ARCH-ABIS.html file in the docs subdirectory of the NDK.