android: Fix assorted compatibility issues with plyer/pyjnius

pyjnius needs to do env->FindClass in the app thread, which only works if the app thread was created by Java, so I changed android_native_app_glue to do that instead of via pthreads
This commit is contained in:
rdb 2025-12-06 21:09:57 +01:00
parent ba6b66fbb6
commit ea55924e59
8 changed files with 84 additions and 1 deletions

View File

@ -4948,6 +4948,7 @@ if GetTarget() == 'android':
TargetAdd('org/panda3d/android/PandaActivity$1.class', opts=OPTS+['DEPENDENCYONLY'], input='PandaActivity.java')
TargetAdd('org/panda3d/android/PandaActivity$2.class', opts=OPTS+['DEPENDENCYONLY'], input='PandaActivity.java')
TargetAdd('org/panda3d/android/PythonActivity.class', opts=OPTS, input='PythonActivity.java')
TargetAdd('org/panda3d/android/PythonActivity$ActivityResultListener.class', opts=OPTS+['DEPENDENCYONLY'], input='PythonActivity.java')
TargetAdd('classes.dex', input='org/panda3d/android/NativeIStream.class')
TargetAdd('classes.dex', input='org/panda3d/android/NativeOStream.class')
@ -4955,6 +4956,7 @@ if GetTarget() == 'android':
TargetAdd('classes.dex', input='org/panda3d/android/PandaActivity$1.class')
TargetAdd('classes.dex', input='org/panda3d/android/PandaActivity$2.class')
TargetAdd('classes.dex', input='org/panda3d/android/PythonActivity.class')
TargetAdd('classes.dex', input='org/panda3d/android/PythonActivity$ActivityResultListener.class')
TargetAdd('p3android_composite1.obj', opts=OPTS, input='p3android_composite1.cxx')
TargetAdd('libp3android.dll', input='p3android_composite1.obj')

View File

@ -81,6 +81,16 @@ public class PandaActivity extends NativeActivity {
return Thread.currentThread().getName();
}
/**
* Called by android_native_app_glue to spawn the application thread.
* Gets passed a function pointer and a data pointer to pass to it.
*/
protected void spawnAppThread(long ptr, long data) {
new Thread(() -> {
nativeThreadEntry(ptr, data);
}).start();
}
/**
* Maps the blob to memory and returns the pointer.
*/
@ -193,4 +203,5 @@ public class PandaActivity extends NativeActivity {
}
private static native long nativeMmap(int fd, long off, long len);
private static native void nativeThreadEntry(long ptr, long data);
}

View File

@ -15,6 +15,12 @@ package org.panda3d.android;
import org.panda3d.android.PandaActivity;
import android.content.Intent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* Extends PandaActivity with some things that are useful in a Python
* application.
@ -26,4 +32,39 @@ public class PythonActivity extends PandaActivity {
public PythonActivity() {
mActivity = this;
}
// Helper code to further support plyer.
public interface ActivityResultListener {
void onActivityResult(int requestCode, int resultCode, Intent data);
}
private List<ActivityResultListener> activityResultListeners = null;
public void registerActivityResultListener(ActivityResultListener listener) {
if (this.activityResultListeners == null) {
this.activityResultListeners = Collections.synchronizedList(new ArrayList<ActivityResultListener>());
}
this.activityResultListeners.add(listener);
}
public void unregisterActivityResultListener(ActivityResultListener listener) {
if (this.activityResultListeners == null) {
return;
}
this.activityResultListeners.remove(listener);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (this.activityResultListeners == null) {
return;
}
this.onResume();
synchronized (this.activityResultListeners) {
Iterator<ActivityResultListener> iterator = this.activityResultListeners.iterator();
while (iterator.hasNext()) {
iterator.next().onActivityResult(requestCode, resultCode, intent);
}
}
}
}

View File

@ -255,10 +255,19 @@ static struct android_app* android_app_create(ANativeActivity* activity,
android_app->msgread = msgpipe[0];
android_app->msgwrite = msgpipe[1];
// Spawn the app thread in Java so it gets a valid class loader.
JNIEnv *env = activity->env;
jclass activity_class = (*env)->GetObjectClass(env, activity->clazz);
jmethodID method = (*env)->GetMethodID(env, activity_class, "spawnAppThread", "(JJ)V");
LOGE("got function pointer: %ld, data: %ld, env: %ld", (jlong)(void *)android_app_entry, (jlong)(void *)android_app, (jlong)(void *)env);
(*env)->CallVoidMethod(env, activity->clazz, method, (void *)android_app_entry, (void *)android_app);
/*
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
*/
// Wait for thread to start.
pthread_mutex_lock(&android_app->mutex);

View File

@ -168,7 +168,7 @@ struct android_app {
int msgread;
int msgwrite;
pthread_t thread;
//pthread_t thread;
struct android_poll_source cmdPollSource;
struct android_poll_source inputPollSource;

View File

@ -193,3 +193,10 @@ void android_show_toast(ANativeActivity *activity, const std::string &message, i
env->CallVoidMethod(activity->clazz, jni_PandaActivity_showToast, jmsg, (jint)duration);
env->DeleteLocalRef(jmsg);
}
/**
* Returns the JNIEnv pointer corresponding to the current thread.
*/
void *SDL_AndroidGetJNIEnv() {
return Thread::get_current_thread()->get_jni_env();
}

View File

@ -44,4 +44,9 @@ EXPORT_CLASS Filename android_find_library(ANativeActivity *activity, const std:
EXPORT_CLASS void android_set_title(ANativeActivity *activity, const std::string &title);
EXPORT_CLASS void android_show_toast(ANativeActivity *activity, const std::string &message, int duration);
// Used to support pyjnius
extern "C" {
EXPORT_CLASS void *SDL_AndroidGetJNIEnv(void);
};
#endif

View File

@ -38,3 +38,11 @@ Java_org_panda3d_android_PandaActivity_nativeMmap(JNIEnv* env, jclass, jint fd,
return (jlong)0;
}
}
/**
* Calls the given function pointer, passing the given data pointer.
*/
EXPORT_JNI void
Java_org_panda3d_android_PandaActivity_nativeThreadEntry(JNIEnv* env, jobject self, jlong func, jlong data) {
((void (*)(void *))(void *)func)((void *)data);
}