Open ajoklar opened 11 months ago
From what I understand JNI_OnLoad
is simply a function JNI calls by default after loading. And that function happens to do some setup work.
JNA doesn't have that and UniFFI doesn't default-call anything after load (well, it calls some of its own code, but nothing generated from the user definitions).
Have you tried manually initializing the Android context before calling any CPAL code? You can expose a function that initializes the Android context for you and make your app/library/kotlin wrapper call that as the first thing.
I tried a lot since I opened this issue, but did not succeed, yet. Here is what I found out:
ndk_context::initialize_android_context
needs pointers to the JVM and to the context (as documented here)
How do you get a pointer to the VM?
JNI_GetCreatedJavaVMs
(StackOverflow: How can I invoke a Java method from Rust via JNI?), but that leads to an UnsatisfiedLinkError
on Android:
dlopen failed: cannot locate symbol "JNI_GetCreatedJavaVMs"
Pass JNIEnv from Kotlin side to Rust: JNA exposes the class JNIEnv. Description:
Marker type for the JNIEnv pointer. Use this to wrap native methods that take a JNIEnv* parameter. Pass CURRENT as the argument.
On the Rust side, you could then get the VM via env.get_java_vm(), but I already have problems passing the object:
In the generated Kotlin file I manually added the function signature fun initialize_android_context(env: JNIEnv)
to the interface _UniFFILib
. I will automate this step in a script, if everything works. If the Rust function needs a pointer, it can't be defined in the UDL and also it should only exist for Android, not for all platforms.
It is called, when the instance gets initialized:
companion object {
internal val INSTANCE: _UniFFILib by lazy {
val instance = loadIndirect<_UniFFILib>(componentName = "mylibname")
instance.initialize_android_context(JNIEnv.CURRENT)
return@lazy instance
}
}
In the Rust library I added a function with this signature:
#[no_mangle]
pub extern "C" fn initialize_android_context(mut env: jni::JNIEnv)
An exception is thrown:
java.lang.IllegalArgumentException: Unsupported argument type com.sun.jna.JNIEnv at parameter 0 of function initialize_android_context
I tried to change the argument's type to:
env: *mut jni::sys::JNIEnv
env: *mut c_void
env: jobject
env: jclass
Always getting the same exception. The only example usage of JNIEnv.CURRENT
I found is a test in the JNA repo. It consists of this Java definition and this C implementation.
At this point, I'm out of ideas. I might be missing something obvious after messing with it for so long. It would be highly appreciated if anyone can point me in the right direction.
Based on this StackOverflow answer I found a solution that probably can be improved, but it works for now:
I didn't add the function to the UDL as it only makes sense on Android.
I'm trying to add audio functionality via CPAL to my library that is used in iOS and Android applications. Everything works on iOS, but on Android an error is thrown:
uniffi.fundsptest.InternalException: android context was not initialized
.There is an issue describing this behavior and it seems like it can be fixed implementing
JNI_OnLoad
. That function is part of JNI and as UniFFI depends on JNA, it is not called (right?). There doesn't seem to be an equivalent toJNI_OnLoad
- I even found this JNA issue opened by a mozilla developer 5 years ago.Does it mean that CPAL (or any library that needs an initialized Android context) is incompatible with UniFFI or is there any way to make it work?