Java 通过JNI从pthread回调失败 我试图通过JNI调用C++类中的线程> /COD>。使用以下代码。我可以从Android应用程序中触发一次按键,我的callbackStringJNI()工作正常。pthread的创建由通过JNI向下传递的按钮触发。如果由pthread创建的函数/线程尝试异步调用callbackStringJNI(),我确实会在Android应用程序messageMe()中获得传递的字符串(仅在中断调试器时查看),但如果我尝试使用该字符串(即,在UI上显示某些内容),应用程序在Android源“Handler.class”函数joinThreadPool上中断,然后如果我再次恢复,我会在下面的Android代码中点击“异常”:
Android API错误代码:Java 通过JNI从pthread回调失败 我试图通过JNI调用C++类中的线程> /COD>。使用以下代码。我可以从Android应用程序中触发一次按键,我的callbackStringJNI()工作正常。pthread的创建由通过JNI向下传递的按钮触发。如果由pthread创建的函数/线程尝试异步调用callbackStringJNI(),我确实会在Android应用程序messageMe()中获得传递的字符串(仅在中断调试器时查看),但如果我尝试使用该字符串(即,在UI上显示某些内容),应用程序在Android源“Handler.class”函数joinThreadPool上中断,然后如果我再次恢复,我会在下面的Android代码中点击“异常”:,java,c++,c,android-ndk,java-native-interface,Java,C++,C,Android Ndk,Java Native Interface,Android API错误代码: public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass(
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(//////////Hits here - this message is never actually displayed though.
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
在下面的代码中,当调用getJNIEnv()
并通过按下按钮执行时,JNI\u OK
就是结果。当调用来自pthread中的回调时,JNI\u edatached
是结果,AttachCurrentThread()
成功
JNI C ndkfoo.C
#include <jni.h>
#include "testSocketClassWrapper.hpp"
static JavaVM* cachedJVM;
static jobject g_javaObj;
static jclass cachedclassID;
void *m_GBLpmyCSocket = NULL;
jint JNI_OnLoad(JavaVM *jvm, void *reserved)
{
cachedJVM = jvm;
JNIEnv* env;
if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4) != JNI_OK) {
LOGD("GETENVFAILEDONLOAD");
return -1;
}
return JNI_VERSION_1_4;
}
void Java_com_example_somecontrol1_MainActivity_initJNICallback(JNIEnv* env, jobject jobj) {
//LOGD("Java_org_test_games_Wrapper_initJNIBridge()");
g_javaObj = (*env)->NewGlobalRef(env, jobj);
jclass storeclassID = (*env)->FindClass(env, "com/example/somecontrol1/MainActivity");
if ( (*env)->ExceptionCheck(env) == JNI_TRUE ){
(*env)->ExceptionDescribe(env);
LOGD("got into exception describe");
}
cachedclassID = (jclass)(*env)->NewGlobalRef(env, storeclassID);
if ( (*env)->ExceptionCheck(env) == JNI_TRUE ){
(*env)->ExceptionDescribe(env);
LOGD("got into exception describe");
}
}
jstring Java_com_example_somecontrol1_MainActivity_inintNativeClass(JNIEnv * env, jobject object){
m_GBLpmyCSocket = (void *)MyClass_create();
return (*env)->NewStringUTF(env, "launched class");
}
typedef struct JniMethodInfo_
{
JNIEnv* env;
jclass classID;
jmethodID methodID;
} JniMethodInfo;
static JNIEnv* getJNIEnv()//was JNIEnv
{
//JavaVM* jvm = cocos2d::JniHelper::getJavaVM();
if (NULL == cachedJVM) {
LOGD("Failed to get JNIEnv. JniHelper::getJavaVM() is NULL");
return NULL;
}
JNIEnv *env = NULL;
// get jni environment
jint ret = (*cachedJVM)->GetEnv(cachedJVM, (void**)&env, JNI_VERSION_1_4);
switch (ret) {
case JNI_OK :
// Success!
LOGD("getenv successA");
return env;
case JNI_EDETACHED :
// Thread not attached
LOGD("thread not attached");
// TODO : If calling AttachCurrentThread() on a native thread
// must call DetachCurrentThread() in future.
// see: http://developer.android.com/guide/practices/design/jni.html
if ((*cachedJVM)->AttachCurrentThread(cachedJVM, &env, NULL) < 0)
{
LOGD("Failed to get the environment using AttachCurrentThread()");
return NULL;
} else {
// Success : Attached and obtained JNIEnv!
LOGD("getenv successB");
return env;
}
case JNI_EVERSION :
// Cannot recover from this error
LOGD("JNI interface version 1.4 not supported");
default :
LOGD("Failed to get the environment using GetEnv()");
return NULL;
}
}
static bool getMethodInfo(JniMethodInfo *methodinfo, const char *methodName, const char *paramCode)
{
jmethodID methodID = 0;
JNIEnv *pEnv = 0;
bool bRet = false;
do
{
pEnv = getJNIEnv();
if (! pEnv)
{
LOGD("getJNIEnv Break Called");
break;
}
//jclass classID = getClassID(pEnv);
//jclass classID = cachedclassID;
//methodID = (*pEnv)->GetMethodID(pEnv, classID, methodName, paramCode);
methodID = (*pEnv)->GetMethodID(pEnv, cachedclassID, methodName, paramCode);
if (! methodID)
{
LOGD("Failed to find method id of %s", methodName);
break;
}
//methodinfo->classID = classID;
methodinfo->env = pEnv;
methodinfo->methodID = methodID;
bRet = true;
} while (0);
return bRet;
}
void callbackStringJNI(const char *newstr)
{
LOGD("callbackStringJNI");
JniMethodInfo methodInfo;
if (! getMethodInfo(&methodInfo, "messageMe", "(Ljava/lang/String;)V"))
{
LOGD("Cannot find method!");
return;
}
jstring jstr = (*methodInfo.env)->NewStringUTF(methodInfo.env, newstr);
(*methodInfo.env)->CallVoidMethod(methodInfo.env, g_javaObj, methodInfo.methodID, jstr);
}
C++头文件
//file testSocketClass.hpp
extern "C" {
void callbackStringJNI(const char *);
}
class mYNewClass{
public:
void *threadfunc(void)
{
LOGD("in thread");
while(1){
LOGD("thread looping");
callbackStringJNI("readata start");
LOGD("sleep");
sleep(30);
}
LOGD("after callbackStringJNI");////this does not work
return(0);
}
static void *thread_helper(void *context)
{
return ((mYNewClass *)context)->threadfunc();
}
找到解决方案后,如能深入了解根本原因,将不胜感激 显然,回调使用的是Android工作线程,无法访问UI线程 修复Android java类
messageMe
function
public void messageMe(final String text) {
runOnUiThread(new Runnable() {
public void run()
{
Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show();
}
});
}
我确实做了另一个更重要的改变,以减少开销。我将methodID缓存在initJNICallback
中,就像这样
static jmethodID cachedmethodID;
initJNICallback(){
cachedmethodID = (*env)->GetMethodID(env, storeclassID, "messageMe", "(Ljava/lang/String;)V");//no NewGlobalRef needed.
}
这不是对你问题的回答,但在我上次研究Bionic(Android中的libc实现)时,pthreads有点中性化。android游戏开发的Chris Pruett推荐创建java线程的模型,然后在单独的进程中生成本机代码,通过dalvik授权消息。
//file testSocketClass.hpp
extern "C" {
void callbackStringJNI(const char *);
}
class mYNewClass{
public:
void *threadfunc(void)
{
LOGD("in thread");
while(1){
LOGD("thread looping");
callbackStringJNI("readata start");
LOGD("sleep");
sleep(30);
}
LOGD("after callbackStringJNI");////this does not work
return(0);
}
static void *thread_helper(void *context)
{
return ((mYNewClass *)context)->threadfunc();
}
public void messageMe(final String text) {
runOnUiThread(new Runnable() {
public void run()
{
Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show();
}
});
}
static jmethodID cachedmethodID;
initJNICallback(){
cachedmethodID = (*env)->GetMethodID(env, storeclassID, "messageMe", "(Ljava/lang/String;)V");//no NewGlobalRef needed.
}