I have an aar library that uses JNI and loads an .so using the below:
static {
try {
System.loadLibrary("Native_Thing");
} catch (UnsatisfiedLinkError var3) {
String nativeAbsolutePath = NativeNetworkMonitor.GetNativeLibraryAbsolutePath();
if (nativeAbsolutePath != null) {
String libAbsolutePath = nativeAbsolutePath + System.mapLibraryName("Native_Thing");
System.load(libAbsolutePath);
} else {
System.out.println("Native Library reference incorrect and no context set");
}
} catch (Exception var4) {
var4.printStackTrace();
}
}
This code works fine and dandy in Android 11. But for some reason in Android 12 it does not. The catch clause is entered and the error message states: "java.lang.UnsatisfiedLinkError: dlopen failed: "[...]/lib/x86_64/libNative_Thing.so" is for EM_X86_64 (62) instead of EM_AARCH64 (183)"
This error makes no sense to me. The emulator image is an Android 12 x86_64 image. But that error seems to be complaining about the .so being build for an x86_64 image and thinks it wants an .so built for arm64
I have confirmed that my applications APK contains all the ABI versions of the .so.
If I build the aar in a way that each ABI is a separate file and include the amd64 one in my application it works.
So in summary it seems like the Android 12 x86_64 emulator image is installed my applications APK with the x86_64 ABI .so as it should but then when it goes to actually load the .so for some reason the emulator thinks it wants an arm64 version
Upon further investigation it appears the issue is that the aar library that uses JNI is not 64 bit compatible. However, the above errors are still very confusing and irrelevant.
Related
I want to integrate h3 java binding library to my android app and I'm getting following exception:
java.lang.UnsatisfiedLinkError: No native resource found at /android-armv7l/libh3-java.so
at com.uber.h3core.H3CoreLoader.copyResource(H3CoreLoader.java:67)
Does anyone used this library before for Android OS?
Thank you.
Initially, following the intended usage as seen in their README should make it work. If it doesn't, see below.
Known Issue: Android, can't use library
UnsatisfiedLinkError: This can be encountered when the corresponding native library is not copied/detected in the project. Following NickRadu's workaround should make it work.
Below is a step-by-step guide.
Add a JNI folder in your project app folder and rename it jniLibs (app/src/main/jniLibs) (for some reason, having it named jni only doesn't work for me).
Get the H3 JAR (make sure you use the same version) and extract the JAR contents.
Copy the folders prefixed with android- and insert them in the jniLibs folder (from step 1).
Rename the copied folders, remove the android- prefix.
Add splits { abi { enable false } } to your app's build.gradle file (within android).
Done. In general, the library should now work as expected.
If during the app installation you encounter:
INSTALL_FAILED_NO_MATCHING_ABIS,
then depending on your test device, create a copy of the folder (along with its contents) and rename it as needed.
For example, a device running on arm64-v8a, I just made a copy of the arm64 folder and renamed it to arm64-v8a. Or if you're using an emulator, make sure that you're not using one with an x86 CPU.
D8 errors: Invoke-customs are only supported starting with Android O (--min-api 26), add these compile options in your app's build.gradle (within android -- note that it may change depending on your system's Java version)
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
Note: It is best to test the app on multiple CPU architecture types first to see it's behavior.
To quickly see the CPU Architecture of the device, you could install Droid Hardware Info, or run a quick test code yourself.
Here's a test block I used and its corresponding result logs:
private fun testH3() {
val h3 = H3Core.newSystemInstance()
val lat = 37.775938728915946
val lng = -122.41795063018799
val resolution = 8
val hexAddr = h3.geoToH3Address(lat, lng, resolution)
val hex = h3.stringToH3(hexAddr)
val kRingsResult = h3.kRings(hexAddr, 1)
Log.d("H3Test", "geoToH3Address: $hexAddr")
Log.d("H3Test", "stringToH3: $hex")
Log.d("H3Test", "kRings: $kRingsResult")
}
Result:
D/H3Test: geoToH3Address: 8828308281fffff
D/H3Test: stringToH3: 613196570331971583
D/H3Test: kRings: [[8828308281fffff], [8828308281fffff, 882830828dfffff, 8828308285fffff, 8828308287fffff, 8828308283fffff, 882830828bfffff, 8828308289fffff]]
I made a sample project where the library works as expected. See android-uber-h3-sample.
Also be advised that the library will not work on Android api < 26 without some modifications to the code. The function that H3Core relies on to parse the hex long to hex string Long.parseUnsignedInt was not added to Android Java until api 26.
I have a native library ('processor') that I'm building with the Android NDK that depends on libjpeg. This issue might be unrelated to the Android aspect however. I've successfully built the library, but when I attempt to run the library fails to load properly with this error:
failed: dlopen failed: cannot locate symbol "jpeg_mem_src"
However if I nm -D processor.so it does indeed contain the symbols:
...
U jpeg_CreateCompress
U jpeg_CreateDecompress
U jpeg_destroy_compress
U jpeg_destroy_decompress
U jpeg_finish_compress
U jpeg_finish_decompress
U jpeg_mem_dest
U jpeg_mem_src
U jpeg_read_header
U jpeg_read_scanlines
...
processor build.gradle:
sources {
main {
jni {
dependencies {
project ":jpeg"
}
}
}
}
ndk {
moduleName "processor"
cppFlags.add("-fexceptions")
ldLibs.add("log")
stl "gnustl_shared"
}
The java side loads it with:
static
{
try
{
Log.i("JNI", "Trying to load lib");
System.loadLibrary("gnustl_shared");
System.loadLibrary("processor");
}
catch (UnsatisfiedLinkError ule)
{
Log.e("JNI", ule.toString());
}
}
Any idea why the symbols exist, but can't be found? Thanks!
Depending on Android versions, you will have to load the jpeg library manually (and include it in your apk too !)
So just add a System.loadLibrary("jpeg"); to your static block, and it should do the trick.
Btw, the fact that you can see the symbols in your .so file only means that your file is using them, not that your .so provides an implementation. You can see it as nm reports the symbols with the U letter, for undefined.
I am using the latest AS from the canary channel, and the ndk that goes with it.
I modified my Android project to suit, and my app (including JNI) builds and runs properly.
I have not (yet) allowed the tools to automatically handle building the JNI part. Lots of special sauce in the existing makefiles, so that would be a lot of work. So I don't have an android.ndk section in my build file. I also have a single gradle.build file.
My debug build type looks like this:
android.buildTypes {
debug {
isMinifyEnabled = false
proguardFiles += file('proguard-rules.txt')
isDebuggable = true
isJniDebuggable = true
}
}
I added a native run configuration. When I try to debug it, I get an exception like LLDB server not found.
If I switch the to gdb, I get a different exception. Any thoughts?
A couple of JNI samples like Teapot and hello-jni are working fine.
Here is the LLDB exception:
LLDB server not found
java.lang.Throwable
at com.intellij.openapi.diagnostic.Logger.error(Logger.java:115)
at com.android.tools.ndk.run.lldb.AndroidLLDBDebuggerContext.launchLLDBServer(AndroidLLDBDebuggerContext.java:66)
at com.android.tools.ndk.run.lldb.AndroidLLDBDebuggerContext.startServer(AndroidLLDBDebuggerContext.java:55)
at com.android.tools.ndk.run.AndroidNativeDebugRunner$NativeDebugLauncher.launchDebug(AndroidNativeDebugRunner.java:109)
at org.jetbrains.android.run.AndroidRunningState.launchDebug(AndroidRunningState.java:835)
at org.jetbrains.android.run.AndroidRunningState.clientChanged(AndroidRunningState.java:814)
at com.android.ddmlib.AndroidDebugBridge.clientChanged(AndroidDebugBridge.java:879)
at com.android.ddmlib.Device.update(Device.java:721)
at com.android.ddmlib.Client.update(Client.java:938)
at com.android.ddmlib.HandleAppName.handleAPNM(HandleAppName.java:112)
at com.android.ddmlib.HandleAppName.handleChunk(HandleAppName.java:65)
at com.android.ddmlib.MonitorThread.callHandler(MonitorThread.java:414)
at com.android.ddmlib.MonitorThread.processClientActivity(MonitorThread.java:322)
at com.android.ddmlib.MonitorThread.run(MonitorThread.java:263)
I'm afraid you need compile your C++ codes with gradle but not makefile. LLDB server will be generated with gradle automatically. Android studio didn't support any other external build systems(e.g. Makefile) yet.
We have attach the debugger as java not as native. As for you have mentioned ndk it should use native. but any how try this method to avoid debugger detaching and app gets killed automatically.
The title says it all. I'm using NDK to build a native library for use with Unity (the game engine, not the Ubuntu shell). I already have much of the code in place and it works on my Xperia Z Ultra which runs Android 4.4.4. However, recently I sent the app to some other people to test on their phones, and it worked on none of their phones. They were using Android 4.0 and 4.1, so I tried running the app on my own Android 4.0.4 device (an old Xperia Mini Pro) and had the same problem. After much narrowing down, I've found out that the root of the problem is including OpenCV in the build, even if it's not referenced at all.
Here's the code I have now. First, the simplest CPP file you've seen:
//Test.cpp:
extern "C"
{
int Test(int a, int b)
{
return a + b;
}
}
Note how it doesn't even include anything from OpenCV. The makefile (or whatever it's called in the context of NDK, I'm mostly a Windows/Visual Studio person) is:
#Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#---------------------------
#note: if I comment these lines out, the library works just fine.
#if I don't, it won't load at all.
#---------------------------
OPENCV_PACKAGE_DIR := D:\Eclipse\OpenCVAndroid\OpenCV-2.4.9-android-sdk
include $(OPENCV_PACKAGE_DIR)/sdk/native/jni/OpenCV.mk
LOCAL_MODULE := Test
LOCAL_SRC_FILES := Test.cpp
include $(BUILD_SHARED_LIBRARY)
Building this project gives me a "libTest.so" file. I put this into my Unity project, at Assets/Plugins/Android/ (I've also tried putting it in Plugins/Android/libs/armeabi-v7a/, no luck). I also have a script inside unity which invokes the library:
//TestNative.cs:
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
public class TestNative : MonoBehaviour
{
[DllImport("Test")]
public static extern int Test(int a, int b);
void OnGUI()
{
try
{
GUI.Label(new Rect(0, 0, 100, 100), "2 + 3 = " + Test(2, 3));
}
catch (System.Exception e)
{
GUI.Label(new Rect(0, 0, 600, 400), e.GetType().ToString() + " " + e.Message + "\n" + e.StackTrace);
}
}
}
When I run this on my Z Ultra, it works just fine. When I run it on the Mini Pro, it fails with the exception "DllNotFoundException: Test". I've checked logcat for errors, and this is what it says:
01-06 06:46:27.660: D/dalvikvm(11135): Trying to load lib /mnt/asec/com.cet.sna2-2/lib/libTest.so 0x2bb86638
01-06 06:46:27.660: D/dalvikvm(11135): Trying to load lib /mnt/asec/com.cet.sna2-2/lib/libTest.so 0x2bb86638
01-06 06:46:27.660: E/Unity(11135): Unable to find Test
It doesn't say anything else, it just fails. The fact that it works on 4.4.4 makes me think it might have something to do with build configurations or something like that, but I can't figure out what it is. Any ideas? Thanks.
I found out, after much trial and error, that the reason for the error is that Android somehow manages to mess the library dependencies up. The work-around is to manually load the libraries in the specific order of their dependencies, using LoadLibrary. This can be done in Unity using AndroidJavaClass to avoid having to write an entire jar library just for loading the native ones.
You have to build a .so that correspond to the architecture of the device your are using.
You should look at this link :
https://developer.nvidia.com/AndroidWorks
It provides useful tools to develop on Android with Visual Studio
You can easily configure an Android Dynamic Library (.so) that correspond to the architecture you need :)
Android/NDK project, worked with NDK versions all the way up to r8c. Under 8d and 8e, I get a compilation error message on the armeabi-v7a build:
Compile++ thumb : myproject <= MyFile.cpp
C:\cygwin\tmp\ccFXOc2F.s: Assembler messages:
C:\cygwin\tmp\ccFXOc2F.s:1935: Error: can't resolve `.data.rel.ro.local' {.data.rel.ro.local section} - `.LPIC44' {*UND* section}
The armeabi, MIPS, and x86 builds for the same project are successful.
It's reliably popping up on the same file. The file is nothing special - vanilla C++, it compiles and works on numerous other platforms (iOS, Windows, NDK r8c to name some). No STL. It does define a healthy amount of string constants though (AKA initialized read/only data). What could be going on?
Already tried a full rebuild, even deleted the obj folder altogether.
The C++ flags are:
LOCAL_CPPFLAGS := -fshort-wchar -fsigned-char -Wno-psabi
I know NDK comes with several versions of GCC; might a toolchain change help? How exactly?
Surely looks like a compiler bug to me. It's related to indexed access to a large chunk of static const data. When I've slightly reformulated a perfectly innocent statement, the error message went away.
Used to be:
//In global scope
static const LPCWSTR Comments[] = {L"A lot of strings here", L"String 2", L"String 3" /* and many more */ }:
void SomeMethod()
{
DoSomething(Comments[i]); //That was the offending line - commenting it out would get rid the error
}
Replaced with:
void SomeMethod()
{
static LPCWSTR pComments = 0;
if(!pComments)
pComments = Comments;
DoSomething(pComments[i]); //Now it works.
}
Ooky, spooky stuff.