-
Notifications
You must be signed in to change notification settings - Fork 231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initializing Android JNI Context #1778
Comments
From what I understand 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:
How do you get a pointer to the VM?
|
Based on this StackOverflow answer I found a solution that probably can be improved, but it works for now: Added this function to the Rust libraryuse jni::{
signature::ReturnType,
sys::{jint, jsize, JavaVM},
};
use std::{ffi::c_void, ptr::null_mut};
pub type JniGetCreatedJavaVms =
unsafe extern "system" fn(vmBuf: *mut *mut JavaVM, bufLen: jsize, nVMs: *mut jsize) -> jint;
pub const JNI_GET_JAVA_VMS_NAME: &[u8] = b"JNI_GetCreatedJavaVMs";
#[no_mangle]
pub unsafe extern "system" fn initialize_android_context() {
let lib = libloading::os::unix::Library::this();
let get_created_java_vms: JniGetCreatedJavaVms =
unsafe { *lib.get(JNI_GET_JAVA_VMS_NAME).unwrap() };
let mut created_java_vms: [*mut JavaVM; 1] = [null_mut() as *mut JavaVM];
let mut java_vms_count: i32 = 0;
unsafe {
get_created_java_vms(created_java_vms.as_mut_ptr(), 1, &mut java_vms_count);
}
let jvm_ptr = *created_java_vms.first().unwrap();
let jvm = unsafe { jni::JavaVM::from_raw(jvm_ptr) }.unwrap();
let mut env = jvm.get_env().unwrap();
let activity_thread = env.find_class("android/app/ActivityThread").unwrap();
let current_activity_thread = env
.get_static_method_id(
&activity_thread,
"currentActivityThread",
"()Landroid/app/ActivityThread;",
)
.unwrap();
let at = env
.call_static_method_unchecked(
&activity_thread,
current_activity_thread,
ReturnType::Object,
&[],
)
.unwrap();
let get_application = env
.get_method_id(
activity_thread,
"getApplication",
"()Landroid/app/Application;",
)
.unwrap();
let context = env
.call_method_unchecked(at.l().unwrap(), get_application, ReturnType::Object, &[])
.unwrap();
ndk_context::initialize_android_context(
jvm.get_java_vm_pointer() as *mut c_void,
context.l().unwrap().to_owned() as *mut c_void,
);
} Modifications to the autogenerated Kotlin fileinternal interface _UniFFILib : Library {
companion object {
internal val INSTANCE: _UniFFILib by lazy {
// initialize android context once and as soon as possible
val instance = loadIndirect<_UniFFILib>(componentName = "mylibname")
instance.initialize_android_context()
return@lazy instance
}
}
// add function to FFI definition
fun initialize_android_context()
// …
} I didn't add the function to the UDL as it only makes sense on Android. |
@ajoklar I am trying to set up a cpal test sound with uniffi. Can you see what's wrong with it? https://github.com/Tuurlijk/android-cpal-test Ok, just discovered that the example code works on native device, but not on the emulator. |
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 to
JNI_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?
The text was updated successfully, but these errors were encountered: