title: JNI callback to Java from c++ class crashing
body: <p>I'm trying to call a Java function from a C++ class using JNI on Android. I have searched and searched but haven't found my exact case. I can call methods in my c++ library from Java, but am having issues doing the reverse. I've messed with it for two days now and am wasting time, so could someone more knowledgable than I help me out?</p>
  
  <p>Full goal: Preserve the JNIEnv OR just the JavaVM (to get and attach a valid JNIEnv later) passed to a native c++ JNI EXPORT call from Java for later use by a c++ class method (not a JNI EXPORT).</p>
  
  <p>So, Java class method calls native c++ method, passing its JNIEnv* and jobject. Store those as static class members in a c++ class. Later, a method of that c++ class uses those static members to callback a Java method of the same class that originally passed its context or whatever.</p>
  
  <p>I've tried using env->NewGlobalRef(someObj); but it's strange because that will make some future uses of the reference objects succeed, but some still fail.</p>
  
  <p>Here's some code:</p>
  
  <p>Java code:</p>
  
  <pre><code>//this is what I want to call from native code
  public void something(String msg)
  {
  //do something with msg
  }
  
  public void somethingElse()
  {
      callNative();
  }
  
  private native void callNatve();
  
  //access native
  static
  {
      System.loadLibrary("someLib");
  }
  </code></pre>
  
  <p>All of the above works fine, the c++ trying to do the same however, does not. (Note: I need the class in my native library as a class and not standalone static calls)</p>
  
  <p>C++ code:
  (Note: for simplicity here everything is public)</p>
  
  <p>MyClass.h:</p>
  
  <pre><code>#include &lt;string&gt;
  #include &lt;jni.h&gt;
  
  class MyClass
  {
      //ctor
      //dtor
  
      void someCall(std::string)
  
      static JNIEnv* envRef;
      static JavaVM* jvmRef;
      static jobject objRef;
  };
  </code></pre>
  
  <p>//////////////////////////////////////////////////////////////////////////////////////
  MyClass.cpp</p>
  
  <pre><code>#include &lt;MyClass.h&gt;
  
  //static members
  MyClass:;:JNIEnv* envRef;
  MyClass::JavaVM* jvmRef;
  MyClass::jobject objRef;
  
  //this is the method whose instructions are crashing
  void MyClass::someCall(std::string msg)
  {
       //works assuming i call env-&gt;NewGlobalRef(MyClass::objRef) or setup/reattach from jvm in exported call or here
       jstring passMsg = envRef-&gt;NewStringUTF(msg.c_str());
  
      clsRef = envRef-&gt;GetObjectClass(objRef);
      if(clsRef == NULL)
      {
          return;
      }
  
      //This doesn't cause crash, but if I call FindClass("where/is/MyClass"); it does... strange
      jmethodID id = envRef-&gt;GetMethodID(clsRef, "something", "(Ljava/lang/String;)V");
      if(id == NULL)
      {
          return;
      }
  
      //Crashes
      //envRef-&gt;CallVoidMethod(clsRef, id, passMsg);
  
      if(envRef-&gt;ExceptionCheck())
      {
          envRef-&gt;ExceptionDescribe();
      }
  
      //Also crashes
      //jvmRef-&gt;DetachCurrentThread();
  }
  
  //this works
  extern "C"
  {
      JNIEXPORT void JNICALL Java_com_my_project_class_callNative(JNIEnv* env, jobject obj)
      {
          MyClass::objRef = env-&gt;NewGlobalRef(obj);
          MyClass::envRef = env;
  
          //tried both
          //MyClass::envRef-&gt;GetJavaVM(&amp;MyClass::jvmRef);
          env-&gt;GetJavaVM(&amp;MyClass::jvmRef);
  
  
          //Tried this
          /*
          int envStat = MyClass::jvmRef-&gt;GetEnv((void**)&amp;MyClass::envRef, JNI_VERSION_1_6);
          if(envStat == JNI_EDETACHED)
          {
              //TODO: LOG
              //std::cout &lt;&lt; "GetEnv: not attached" &lt;&lt; std::endl;
              if(MyClass::jvmRef-&gt;AttachCurrentThread(&amp;MyClass::envRef, NULL) != 0)
              {
                  //TODO: LOG
                  //std::cout &lt;&lt; "Failed to attach" &lt;&lt; std::endl;
              }
          }else if(envStat == JNI_OK)
          {
              //
          }else if(envStat == JNI_EVERSION)
          {
              //TODO: LOG
              //std::cout &lt;&lt; "GetEnv: version not supported" &lt;&lt; std::endl;
          }
          */
  
          //calling detachcurrentthread here crashes if set above
  
          MyClassObj.someCall(an std::string);
      }
  }
  </code></pre>
  
  <p>I've tried a few different approaches but they all cause crashes. I do DeleteGlobalRef() too when I use it, but it crashes way before then.  Any insight is appreciated</p>
  
  <p><strong>EDIT #1:</strong>
  As per Michael's suggestion, I've implemented the JNI_OnLoad function and have cached just the JavaVM* from there. Inside the MyClass::someCall(std::string) method I then use the JavaVM to get the JNIEnv, initialize a jclass object using env->FindClass and get the methodID for the something(String) java method, but attempting to callback to Java with CallVoidMethod results in a crash still.</p>
  
  <p>OnLoad defined as extern "C" in MyClass.cpp:    </p>
  
  <pre><code>JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void* reserved)
  {
      MyClass::jvmRef = jvm;
  
      return JNI_VERSION_1_6;
  }
  </code></pre>
  
  <p>Updated MyClass::someCall definition:</p>
  
  <pre><code>void MyClass::someCall(std::string msg)
  {
      //Get environment from cached jvm
      JNIEnv* env;
      jclass cls;
  
      int envStat = MyClass::jvmRef-&gt;GetEnv((void**)&amp;env, JNI_VERSION_1_6);
  
      bool attached = false;
      if(envStat == JNI_EDETACHED)
      {
          //TODO: LOG
          if(JavaInterface::jvmRef-&gt;AttachCurrentThread(&amp;env, NULL) != 0)
          {
              //TODO: LOG
              // "Failed to attach"
              return;
          }else if(envStat == JNI_OK)
          {
              attached = true;
          }else if(envStat == JNI_EVERSION)
          {
              //TODO: LOG
              // "GetEnv: version not supported"
          }
      }
  
       cls = env-&gt;FindClass("package/location/project/JavaClass");
      if(cls == NULL)
      {
          //TODO: LOG
          return;
      }
  
      jmethodID id = env-&gt;GetMethodID(cls, "something", "(Ljava/lang/String;)V");
      if(id == NULL)
      {
          return;
      }
  
      jstring passMsg = env-&gt;NewStringUTF(msg.c_str());
  
      //Crashes
      env-&gt;CallVoidMethod(cls, id, passMsg);
  
      if(attached)
          jvmRef-&gt;DetachCurrentThread();
   }
  </code></pre>