问题
I am working on a Xamarin.Android app with a C++ part. Now I need to call directly into Android Java interfaces from the C++ library.
I copied the code from Caleb Fenton's detailed and very helpful blog post which uses the JNI to call from C++ to Java. But I can't get the pointer to the JVM in the same way that he does it.
(By the way, I am mostly a C# programmer, so it's entirely possible that I've made an elementary mistake in C++).
In the header file:
#pragma once
class MyJniClass
{
//Create this once and cache it.
JavaVM *m_jvm; // Pointer to the JVM (Java Virtual Machine)
JNIEnv *m_env; // Pointer to native interface
bool init_jvm();
}
In the .cpp file:
#include <jni.h>
#include <dlfcn.h>
#include "MyJniClass.h"
typedef int(*JNI_CreateJavaVM_t)(void *, void *, void *);
/**Code is based on https://github.com/rednaga/native-shim/blob/master/vm.c
*/
bool MyJniClass::init_jvm()
{
// https://android.googlesource.com/platform/frameworks/native/+/ce3a0a5/services/surfaceflinger/DdmConnection.cpp
JavaVMOption opt[1];
opt[0].optionString = "-Djava.class.path=."; // I added a small java class to the dll to which this C++ class is linked,
//so that there would be a java class in the current directory.
//opt/*[1]*/.optionString = "-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y";
JavaVMInitArgs args;
args.version = JNI_VERSION_1_6;
args.options = opt;
args.nOptions = 1;
args.ignoreUnrecognized = JNI_FALSE;
void *libart_dso = dlopen("libart.so", RTLD_NOW); //libdvm.so is outdated, libnativehelper.so doesn't work
if (!libart_dso )
{
//Execution doesn't pass through here
return false;
}
//Try to get the JNI_CreateJavaVM function pointer
JNI_CreateJavaVM_t JNI_CreateJavaVM;
JNI_CreateJavaVM = (JNI_CreateJavaVM_t)dlsym(libart_dso, "JNI_CreateJavaVM");
if (!JNI_CreateJavaVM)
{
//Execution doesn't pass through here
return false;
}
signed int result = JNI_CreateJavaVM(&(m_jvm), &(m_env), &args);
if ( result != 0)
{
ostringstream os;
os << "Call to JNI_CreateJavaVM returned ";
os << result;
m_logger->writeEntry(Loglevel::debug, os.str()); // ===> Here, I can see that result is always -1
return false;
}
return true;
}
I tried to find the function JNI_CreateJavaVM in the ART source code here, but I couldn't find it. But surely it should be there, so that dlsym can find the function? I think I have to look further to find the source code for libart.so.
What am I doing wrong, that I can't get a valid call to JNI_CreateJavaVM?
回答1:
the first modification here would be to add the diagnosis option Xcheck:jni
.
This will provide details in case of errors.
Add another option by modifying JavaVMOption opt[1];
to JavaVMOption opt[2];
and then, add the following option:
opt[1].optionString = "-Xcheck:jni"
.
Also, the dll must be loaded from it's original location (as other DLLs are involved) and not from your project directory. Mor details are provided in the following post: JNI_CreateJavaVM() terminates with exit code 1
Finally, you should cast the pointer to native interface JNIEnv by modifying:
signed int result = JNI_CreateJavaVM(&(m_jvm), &(m_env), &args);
to
signed int result = JNI_CreateJavaVM(&(m_jvm), (void**)&(m_env), &args);
This should solve the problem.
回答2:
It looks like issue is related to library that provides JNI_CreateJavaVM
. I am referring to this line
void *libart_dso = dlopen("libart.so", RTLD_NOW);
If I strip your code from all the stuff that is not related to JVM, and if I use macOS based JDK it works just fine.
--- 8< CUT HERE 8< ----
#include <jni.h>
#include <dlfcn.h>
#include <iostream>
#include "MyJniClass.h"
using namespace std;
bool MyJniClass::init_jvm()
{
JavaVM *jvm;
JNIEnv *env = NULL;
JavaVMOption opt[1];
opt[0].optionString = "-Djava.class.path=.";
JavaVMInitArgs args;
args.version = JNI_VERSION_1_6;
args.options = opt;
args.nOptions = 1;
args.ignoreUnrecognized = JNI_FALSE;
int status = JNI_CreateJavaVM (&jvm, (void **) &env, &args);
cout << status << endl;
return true;
}
int main(int argc, char **argv) {
MyJniClass jni;
jni.init_jvm();
}
--- 8< CUT HERE 8< ----
--- 8< CUT HERE 8< ----
#pragma once
class MyJniClass
{
//Create this once and cache it.
JavaVM *m_jvm; // Pointer to the JVM (Java Virtual Machine)
JNIEnv *m_env; // Pointer to native interface
public: bool init_jvm();
};
--- 8< CUT HERE 8< ----
and then, I compile the code:
g++ -o lib/recipeNo027_main c/recipeNo027_main.c \
-I/Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home/include \
-I/Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home/include/darwin/ \
-rpath -L/Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home/lib/server -ljvm
(it is based on recipeNo027 from here: http://jnicookbook.owsiak.org/recipe-no-027/)
I can run it without any issues
> lib/recipeNo027_main
0
It looks like something fishy is going on inside your JNI_CreateJavaVM
implementation.
来源:https://stackoverflow.com/questions/56290184/attempt-to-call-jni-createjavavm-from-libart-so-fails