Skip to content
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

Classloading possible issues #169

Open
astonbitecode opened this issue Oct 29, 2024 · 5 comments
Open

Classloading possible issues #169

astonbitecode opened this issue Oct 29, 2024 · 5 comments

Comments

@astonbitecode
Copy link

Hi,

I am having classloading issues when using android-activity and try to load classes of code that is being shipped within the same apk.
The system classes that are available by default (eg. java.lang.String) can be found and loaded with no issues. However, the custom classes cannot be found...

I wonder if the reason is what is described here:

You can get into trouble if you create a thread yourself (perhaps by calling pthread_create and then attaching it with AttachCurrentThread). Now there are no stack frames from your application. If you call FindClass from this thread, the JavaVM will start in the "system" class loader instead of the one associated with your application, so attempts to find app-specific classes will fail.

I see in android-activity README that indeed the rust applications do not run on application's main thread:

run an android_main() function in a separate thread from the Java main thread and marshal events (such as lifecycle events and input events) between Java and your native thread.

Can it be the reason?

I also tried to use the JNI_OnLoad in order to cache the JavaVM and use it later, as it is proposed, but it does not gets called either.

So, my question is, does android-activity support using jni to call classes shipped within the same apk? Do you have any pointers on how to achieve that?

I have this gitjub repo, where you can find my scenario if this can help.

Thanks!

@MarijnS95
Copy link
Member

I've answered the same question on your repository a while ago: astonbitecode/j4rs#102 (comment)

Does that help?

@MarijnS95
Copy link
Member

In fact it looks like your example/reproduction repository was a fork from my android-support repository where the linked solution was built and demonstrated. Seems like you just need to rebase on it to receive the solution within your tree.

@astonbitecode
Copy link
Author

As far as I understand, this answer implies implementing the Activity and using android.content.Context.getClassLoader() to load classes.
My question is whether we can have rust only implementation and use jni FindClass to load classes.

@MarijnS95
Copy link
Member

@astonbitecode I don't understand what "this answer implies implementing the Activity" is supposed to mean. You asked this question in the android-activity repository which already "implements" both GameActivity and NativeActivity, the latter of which is completely native ("Rust only") and supports interacting with Java through JNI, as shown in the proposed solution. Your linked repository also uses android-activity and has access to AndroidApp where the ClassLoader is pulled out of the Activity/Context:

https://github.com/MarijnS95/android-support/blob/cc03005d2f5b8c4036685d17ee986de5406e5e4f/src/lib.rs#L12-L25

While FindClass doesn't seem to be usable per the FAQ that you linked, it suggests that this uses ClassLoader under the hood which matches the solution above.


Separate from this, since you mentioned threading, I do wonder if FindClass has the app in DexPathList before android-activity spawns a user thread (especially since, according to the FAQ, ANativeActivity_onCreate() should have Java stackframes earlier on - except that those Java files don't reside in the app for NativeActivity - only for GameActivity). I.e. here:

/// This is the native entrypoint for our cdylib library that `ANativeActivity` will look for via `dlsym`
#[no_mangle]
extern "C" fn ANativeActivity_onCreate(
activity: *mut ndk_sys::ANativeActivity,
saved_state: *const libc::c_void,
saved_state_size: libc::size_t,
) {
abort_on_panic(|| {
let _join_log_forwarder = forward_stdio_to_logcat();
log::trace!(
"Creating: {:p}, saved_state = {:p}, save_state_size = {}",
activity,
saved_state,
saved_state_size
);
// Conceptually we associate a glue reference with the JVM main thread, and another
// reference with the Rust main thread
let jvm_glue = NativeActivityGlue::new(activity, saved_state, saved_state_size);
let rust_glue = jvm_glue.clone();
// Let us Send the NativeActivity pointer to the Rust main() thread without a wrapper type
let activity_ptr: libc::intptr_t = activity as _;
// Note: we drop the thread handle which will detach the thread
std::thread::spawn(move || {

@astonbitecode
Copy link
Author

I don't understand what "this answer implies implementing the Activity" is supposed to mean

Ok apparently I did not express myself correctly. I wanted to stress that my question is whether I can achieve classloading as described, without writing any java code myself.

I was thinking that using the android-activity I would be able to call jni using the classpath that includes the classes residing inside the apk itself. If this was possible, FindClass would locate and load the classes of the apk.

Maybe I am wrong, but I believe this would be possible if the android-activity entrypoint fn android_main(app: AndroidApp) is executed by the main Activity thread, instead on its own new native thread.

If this cannot be done, then I would propose to have a function similar to JNI_OnLoad that is called on the main thread before the android_main and give the ability to the rust application to load and cache any needed classes, as proposed by the first bullet here:

There are a few ways to work around this:
Do your FindClass lookups once, in JNI_OnLoad, and cache the class references for later use. Any FindClass calls made as part of executing JNI_OnLoad will use the class loader associated with the function that called System.loadLibrary (this is a special rule, provided to make library initialization more convenient). If your app code is loading the library, FindClass will use the correct class loader.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants