cocos2d-x 中多线程的c/c++代码通过JNI调用java代码

来源:互联网 发布:mac科研软件 编辑:程序博客网 时间:2024/05/01 12:21


这个文章为android multithread in c/c++ to call JNI 的第二篇。

第一篇地址为:http://blog.csdn.net/wu4long/article/details/17756419

上文提到,android的第三种方法,通过ClassLoader object来处理了。

 

  • Cache a reference to the ClassLoader object somewhere handy, and issueloadClass calls directly. This requires some effort.

     

    哪怎样处理呢(This requires some effort) ?

    下面我就以cocos2d-x的环境来具体描述吧。当然不是这个环境的也可以自己来稍微修改下。

    此文方法是参照 http://stackoverflow.com/questions/13263340/findclass-from-any-thread-in-android-jni

    FindClass from any thread in Android JNI

    不过此文章不是针对cocos2d-x来写的。有些地方给出的代码不严谨。

     

     

    ///return true if success. else return false.

    bool your_thread_start_javaVMAttachCurrentThread()

     

    {

     

    JNIEnv* env = NULL;

    if( JniHelper::getJavaVM()->AttachCurrentThread(&env,NULL) < 0) return false;

    return true;

    }

    void your_thread_stop_javaVMDetachCurrentThread()

    {

    JniHelper::getJavaVM()->DetachCurrentThread();

    }

    在线程函数中开始调用 your_thread_start_javaVMAttachCurrentThread(), 线程结束的地方调用 your_thread_stop_javaVMDetachCurrentThread()。 错误处理自己去处理吧。

    上面的两个函数你可以放置在你的认为合适的地方。当然也可以直接放置在JniHelper的类中。自行处理吧。

     

     

    为了可以继续使用JniHelper的方法,透明的规避多线程的问题。我们就直接修改JniHelper.cpp文件。

     

     

    static jobject gClassLoader;

    static jmethodID gFindClassMethod;


    增加一个新的函数:

    static void initClassLoaderForMultiThread()

    {

     

    JNIEnv *env = 0;

    do

    {

    if (! getEnv(&env))

    {

    break;

    }

    jclass cocos2dClass = env->FindClass(org/cocos2dx/lib/Cocos2dxRenderer);

    if(env->ExceptionCheck())

    {

    env->ExceptionDescribe();

    env->ExceptionClear();

    LOGD(ExceptioninitClassLoaderForMultiThread cocos2dClass is exception);

    break;

    }

     

    ///env->FindClass(java/lang/Class);

    jclass classClass = env->GetObjectClass(cocos2dClass);

    if(env->ExceptionCheck())

    {

    env->ExceptionDescribe();

    env->ExceptionClear();

    LOGD(Exception initClassLoaderForMultiThread classClass is exception);

    break;

    }

     

     

    jclass classLoaderClass = env->FindClass(java/lang/ClassLoader);

    if(env->ExceptionCheck())

    {

    env->ExceptionDescribe();

    env->ExceptionClear();

    LOGD(Exception initClassLoaderForMultiThread classLoaderClass);

    break;

    }

     

    jmethodID getClassLoaderMethod = env->GetMethodID(classClass,getClassLoader,

    ()Ljava/lang/ClassLoader;);

     

    jobject classLoader = env->CallObjectMethod(cocos2dClass, getClassLoaderMethod);

     

    if(env->ExceptionCheck())

    {

    env->ExceptionDescribe();

    env->ExceptionClear();

    LOGD(Exception initClassLoaderForMultiThread classLoader);

    break;

    }

     

    gClassLoader = env->NewGlobalRef(classLoader);

     

    jmethodID findClassMethod = env->GetMethodID(classLoaderClass,findClass, (Ljava/lang/String;)Ljava/lang/Class;);

     

     

    gFindClassMethod = findClassMethod;

     

    if(env->ExceptionCheck())

    {

    env->ExceptionDescribe();

    env->ExceptionClear();

    gFindClassMethod = NULL;

    gClassLoader = NULL;

    LOGD(Exception initClassLoaderForMultiThread findClassMethod);

    break;

    }

    }while(0);

    }

    然后在 JniHelper::setJavaVM 函数中调用上面的函数。如下所示:

     

    void JniHelper::setJavaVM(JavaVM *javaVM)

    {

    m_psJavaVM = javaVM;

    initClassLoaderForMultiThread(); }

     

    然后修改 getClassID_ 函数. 添加下面灰色阴影的部分。即在原有的FindClass失败的时候,

    用我们新的ClassLoader的方法来查找类。

    ret = static_cast(pEnv->CallObjectMethod(gClassLoader, gFindClassMethod, jstrName));

     

     

    static jclass getClassID_(constchar *className,JNIEnv *env)

    {

    JNIEnv *pEnv = env;

    jclass ret = 0;

     

    do

    {

    if (! pEnv)

    {

    if (! getEnv(&pEnv))

    {

    break;

    }

    }

     

    ret = pEnv->FindClass(className);

     

    if (! ret)

    {

    if(gClassLoader)

    {

    if(pEnv->ExceptionCheck())

    {

    pEnv->ExceptionDescribe();

    pEnv->ExceptionClear();

    }

    jstring jstrName = (pEnv)->NewStringUTF(className);

    ret = static_cast(pEnv->CallObjectMethod(gClassLoader, gFindClassMethod, jstrName));

    if(ret) break;

    }

    LOGD(Failed to find class of %s, className);

    break;

    }

    } while (0);

     

    return ret;

    }


    然后在你的线程代码中,可以使用JniHelper来调用相应的方法了。

     

    JniHelper::getStaticMethodInfo 等方法和本来单线程的就一样了。

     

    大功告成了。已经可以透明处理多线程了。

    如果你的环境不是cocos2dx,只需要修改将上面添加的部分移到你的环境中就可以了。

    唯一的区别,就是将这句

    jclass cocos2dClass = env->FindClass(org/cocos2dx/lib/Cocos2dxRenderer);

    中的字符串org/cocos2dx/lib/Cocos2dxRenderer 换成你的环境中肯定存在的java类就可以了。

     

    补充线程的资源同步:

    如果线程间需要用到java端的资源同步。可以使用 jint MonitorEnter(jobject obj); 和 jint MonitorExit(jobject obj);

    类似一个简单的同步锁,在Java中我们这样写

     

    synchronized (obj) {
    //dosomething
    }

    在JNI中,我们使用这组函数这样写。 当然首先通过Jni的方法来获取这个对象。

    jobject obj = ….;

    env->MonitorEnter(obj);

    //dosomething
    env->MonitorExit(obj);

0 0