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

  1923人阅读
转载请注明原地址: http://blog.csdn.net/wu4long/article/details/17757433
这个文章为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 issue loadClass 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<jclass>(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); 

你可能感兴趣的:(cocos2d-x专栏)