The DLL I made throws this error every time I try and load it in JNI. I narrowed the problem down to the main file, something in there is causing this. I'm not sure if it's because the toast notifications, couldn't find anything about that.
#include "stdafx.h"
#include <iostream>
#include <string>
#include "wintoast\wintoastlib.h"
#include "jni\me_glorantq_aboe_chat_client_notification_WindowsToastNotifier.h"
#include "ToastHandler.h"
std::wstring toastLogoPath = nullptr;
std::wstring jniToWstring(JNIEnv* env, jstring& jString) {
jboolean isCopy = true;
const char* chars = env->GetStringUTFChars(jString, &isCopy);
std::string str = std::string(chars);
std::wstring wstr(str.length(), L' ');
std::copy(str.begin(), str.end(), wstr.begin());
return wstr;
}
JNIEXPORT jboolean JNICALL Java_me_glorantq_aboe_chat_client_notification_WindowsToastNotifier_initialise(JNIEnv* env, jobject object, jstring logoPath) {
if (!WinToastLib::WinToast::instance()->isCompatible()) {
std::cerr << "WinToast is not compatible with this system!" << std::endl;
return false;
}
WinToastLib::WinToast::instance()->setAppName(L"A Bit of Everything");
std::wstring aumi = WinToastLib::WinToast::instance()->configureAUMI(L"me.glorantq", L"aboe", L"chat", L"win10");
WinToastLib::WinToast::instance()->setAppUserModelId(aumi);
if (!WinToastLib::WinToast::instance()->initialize()) {
std::cerr << "Failed to initialise WinToast!" << std::endl;
return false;
}
else {
std::cout << "Initialised WinToast!" << std::endl;
}
toastLogoPath = jniToWstring(env, logoPath);
std::cout << "Set logo path to: " << toastLogoPath.c_str() << std::endl;
return true;
}
JNIEXPORT void JNICALL Java_me_glorantq_aboe_chat_client_notification_WindowsToastNotifier_showNotificationNative(JNIEnv* env, jobject object, jstring header, jstring message) {
ToastHandler* handler = new ToastHandler;
WinToastLib::WinToastTemplate toastTemplate = WinToastLib::WinToastTemplate(WinToastLib::WinToastTemplate::ImageAndText02);
toastTemplate.setImagePath(toastLogoPath);
toastTemplate.setTextField(jniToWstring(env, header), WinToastLib::WinToastTemplate::FirstLine);
toastTemplate.setTextField(jniToWstring(env, message), WinToastLib::WinToastTemplate::SecondLine);
if (!WinToastLib::WinToast::instance()->showToast(toastTemplate, handler)) {
std::cerr << "Failed to show toast!" << std::endl;
}
}
When I remove everything from this file, everything works just fine, apart from the missing implementations of course.
Related
In my previous question i cached the JNIEnv* between JNI Calls. And from the comment i came to know its invalid and this result me to learn the JNI Local and Global references. I did some test program to understand it. From the test program i am not able to understand the use of the Global references. Because the Local references itself working fine between multiple JNI calls. I have 3 questions from the test program
Eager to know the reason, that how the local reference getting cached and working properly.
Change in the ArrayList size is working and not the String object
Reason to know how the cached JNIEnv in working ,although its invalid (in my previous question).
The test code given below.
jni code :
jclass cls1, cls2, cls3;
jmethodID mth1, mth2, mth3;
jstring str1, str2;
jobject obj1, obj2, obj3;
JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnFindClass
(JNIEnv *env, jobject obj) {
if (cls1 == NULL || str1 == NULL || obj1 == NULL) {
cout << "Initializing new string object" << endl;
cls1 = env->FindClass("java/lang/String");
mth1 = env->GetMethodID(cls1, "<init>", "(Ljava/lang/String;)V");
str1 = env->NewStringUTF("Apple");
obj1 = env->NewObject(cls1, mth1, str1);
mth1 = env->GetMethodID(cls1, "length", "()I");
long i = (long) env->CallIntMethod(obj1, mth1);
cout << "Length = " << i << endl;
//env->DeleteLocalRef(cls1);
//env->DeleteLocalRef(str1);
//env->DeleteLocalRef(obj1);
} else {
cout << "String Object already initialized" << endl;
mth1 = env->GetMethodID(cls1, "length", "()I");
long i = (long) env->CallIntMethod(obj1, mth1);
cout << "Length = " << i << endl;
}
}
JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnGetExistingObject__Ljava_lang_String_2
(JNIEnv *env, jobject obj, jstring str) {
if (cls2 == NULL || obj2 == NULL) {
cout << "Initializing from existing string object" << endl;
cls2 = env->GetObjectClass(str);
obj2 = (jobject) env->NewLocalRef(str);
mth2 = env->GetMethodID(cls2, "length", "()I");
long i = (long) env->CallIntMethod(obj2, mth2);
cout << "Length = " << i << endl;
//env->DeleteLocalRef(cls2);
//env->DeleteLocalRef(obj3);
} else {
cout << "Object already initialized" << endl;
mth2 = env->GetMethodID(cls2, "length", "()I");
long i = (long) env->CallIntMethod(obj2, mth2);
cout << "Length = " << i << endl;
}
}
JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnGetExistingObject__Ljava_util_ArrayList_2
(JNIEnv *env, jobject obj, jobject lst) {
if (cls3 == NULL || obj3 == NULL) {
cout << "Initializing from existing ArrayList object" << endl;
cls3 = env->GetObjectClass(lst);
obj3 = (jobject) env->NewLocalRef(lst);
mth3 = env->GetMethodID(cls3, "size", "()I");
long i = (long) env->CallIntMethod(obj3, mth3);
cout << "Size = " << i << endl;
//env->DeleteLocalRef(cls3);
//env->DeleteLocalRef(obj3);
} else {
cout << "Object already initialized" << endl;
mth3 = env->GetMethodID(cls3, "size", "()I");
long i = (long) env->CallIntMethod(obj3, mth3);
cout << "Length = " << i << endl;
}
}
Calling Java code (Test Code) :
a.fnFindClass();
a.fnFindClass();
String str = new String("Android OS");
a.fnGetExistingObject(str);
//Increasing the size
str = new String("Will modified string length get effect");
a.fnGetExistingObject(str);
ArrayList<Integer> al = new ArrayList<>();
al.add(1);al.add(2);al.add(3);
a.fnGetExistingObject(al);
al.add(4);al.add(5); //Increasing the size
a.fnGetExistingObject(al);
Test Result :
Initializing new string object
Length = 5
String Object already initialized
Length = 5
Initializing from existing string object
Length = 10
Object already initialized
Length = 10
Initializing from existing ArrayList object
Size = 3
Object already initialized
Length = 5
Thanks In Advance.
Global references prevent the garbage collector from deleting the relevant object. The object might also not be collected just because another java object has a reference to it or because the garbage collector didn't run in a short period between calls. You shouldn't rely on this and instead keep global references to anything you might need later.
ArrayLists can be resized. Strings are immutable and one always needs to create a new string for functions like substring or append.
I believe the issue with keeping JNIEnv * has to do with thread safety. There is no way for the C++ code to know two different calls don't come from two different threads. Each env needs to be attached to a different thread.
I'm using QtCreator to deploy C++/Java applications on Android. But I think my problem may not be specific to the way QtCreator deploys the app.
I want to create a C++ library providing a specific functionnality. To do so, the library needs to instantiate a Java class, this last one will be used to do some SDK functions class (for stuff that are not available in the NDK/C++ API).
Creating and using java objects from a C++ program works fine. I package the .java file to the application environment during compilation/deployment and then I can use the Java class via two approachs:
Declare JNI_OnLoad, load class id, method id, and later call them using jni
Use Qt QAndroidJniObject objects (this is specific to QtCreator)
Now the problem comes when I want to create and use java objects from a C++ library. It only works if the .java file is packaged with the top-level application. I could not find a way to package the java with and only with the library itself. Meaning that anyone why needs to use my library will not only have to simply link with the library, but will also need to package the .java file(s) needed by my library. This breaks encapsulation and gives a hard time to the end developer writing programs and simply wanting to load a library and expecting it to embed all it needs to work on its own...
My question is: How can the library embed the java file, so that this java file does not need to be part of the top level program package to let the library use it?
Here is a quick sample: MainWindow constrctor calls 4 functions themselves trying to create and use Java objects. Only the first two calls work...
main.cpp:
#include <QApplication>
#include <QMainWindow>
#include "MyLib.h"
#include <QtAndroidExtras/QAndroidJniObject>
#include "jni.h"
#include <assert.h>
// load java classes from main program
JavaVM* s_javaVM = NULL;
jclass s_classID = 0;
jmethodID s_ctorMethodID = 0;
jmethodID s_callmethodID = 0;
bool loadJava( JNIEnv *env )
{
jclass clazz = env->FindClass("my/FooPrg");
if (!clazz)
{
qCritical("Can't find FooPrg class");
return false;
}
// keep a global reference to it
s_classID = (jclass)env->NewGlobalRef(clazz);
// search for its contructor
s_ctorMethodID = env->GetMethodID(s_classID, "<init>", "()V");
if (!s_ctorMethodID )
{
qCritical("Can't find class contructor");
return false;
}
// search for a method
s_callmethodID = env->GetMethodID(s_classID, "Mult", "(I)I");
if (!s_callmethodID )
{
qCritical("Can't find Mult method");
return false;
}
return true;
}
jint JNICALL JNI_OnLoad(JavaVM *vm, void *)
{
s_javaVM = vm;
JNIEnv* env = NULL;
if (s_javaVM->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4) != JNI_OK)
return -1;
loadJava( env );
return JNI_VERSION_1_4;
}
void callJavaFunctionFromPrgWithQt()
{
if ( QAndroidJniObject::isClassAvailable("my/FooPrg") )
{
QAndroidJniObject obj("my/FooPrg","()V");
if ( obj.isValid() )
{
jint res = obj.callMethod<jint>("Mult", "(I)I", 0x0002);
assert( res == 4 );
}
else
{
assert( false );
}
}
else
{
assert( false );
}
}
void callJavaFunctionFromPrgWithJniLoad()
{
if ( s_classID != 0 && s_ctorMethodID != 0 && s_callmethodID != 0 )
{
JNIEnv* env = NULL;
if (s_javaVM->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4) != JNI_OK)
assert(false);
jobject j_object = env->NewGlobalRef( env->NewObject(s_classID, s_ctorMethodID ) );
jint res = env->CallIntMethod(j_object, s_callmethodID, 0x0002 );
assert( res == 4 );
}
else
{
assert( false );
}
}
class MainWindow : public QMainWindow
{
public:
MainWindow()
{
callJavaFunctionFromPrgWithQt(); // works
callJavaFunctionFromPrgWithJniLoad(); // works
callJavaFunctionFromLibWithQt(); // fails, assert
callJavaFunctionFromLibWithJniLoad(); // fails, because libraries JNI_OnLoad can't find FooLib.java!
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
MyLib.h:
#pragma once
void callJavaFunctionFromLibWithQt();
void callJavaFunctionFromLibWithJniLoad();
MyLib.cpp:
#include "MyLib.h"
#include <QtAndroidExtras/QAndroidJniObject>
#include "jni.h"
#include <assert.h>
// load java classes from main program
JavaVM* s_javaVM = NULL;
jclass s_classID = 0;
jmethodID s_ctorMethodID = 0;
jmethodID s_callmethodID = 0;
bool loadJava( JNIEnv *env )
{
jclass clazz = env->FindClass("my/FooLib");
if (!clazz)
{
qDebug("Can't find FooLib class");
return false;
}
// keep a global reference to it
s_classID = (jclass)env->NewGlobalRef(clazz);
// search for its contructor
s_ctorMethodID = env->GetMethodID(s_classID, "<init>", "()V");
if (!s_ctorMethodID )
{
qDebug("Can't find class contructor");
return false;
}
// search for a method
s_callmethodID = env->GetMethodID(s_classID, "Mult", "(I)I");
if (!s_callmethodID )
{
qDebug("Can't find Mult method");
return false;
}
return true;
}
jint JNICALL JNI_OnLoad(JavaVM *vm, void *)
{
s_javaVM = vm;
JNIEnv* env = NULL;
if (s_javaVM->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4) != JNI_OK)
return -1;
// uncommenting this makes the application crash upon load....
//loadJava( env );
return JNI_VERSION_1_4;
}
void callJavaFunctionFromLibWithQt()
{
if ( QAndroidJniObject::isClassAvailable("my/FooLib") )
{
QAndroidJniObject obj("my/FooLib","()V");
if ( obj.isValid() )
{
jint res = obj.callMethod<jint>("Mult", "(I)I", 0x0002);
assert( res == 4 );
}
else
{
assert( false );
}
}
else
{
assert( false ); // this assertion is reached!
}
}
void callJavaFunctionFromLibWithJniLoad()
{
if ( s_classID != 0 && s_ctorMethodID != 0 && s_callmethodID != 0 )
{
JNIEnv* env = NULL;
if (s_javaVM->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4) != JNI_OK)
assert(false);
jobject j_object = env->NewGlobalRef( env->NewObject(s_classID, s_ctorMethodID ) );
jint res = env->CallIntMethod(j_object, s_callmethodID, 0x0002 );
assert( res == 4 );
}
else
{
assert( false ); // this assertion is reached!
}
}
FooPrg.java:
package my;
import java.lang.Integer;
public class FooPrg
{
public FooPrg()
{
}
public int Mult(int val)
{
return val * 2;
}
}
FooLib.java:
package my;
import java.lang.Integer;
public class FooLib
{
public FooLib()
{
}
public int Mult(int val)
{
return val * 2;
}
}
jniload.pro:
TARGET = jniload
CONFIG += qt resources
QT += core gui widgets
android: QT += androidextras
SOURCES += src/main.cpp
TEMPLATE = app
INCLUDEPATH += ifc
LIBS += \
-l$$OUT_PWD/../../lib/jniload_lib/libjniload_lib.so
ANDROID_EXTRA_LIBS += \
$$OUT_PWD/../../lib/jniload_lib/libjniload_lib.so
ANDROID_PACKAGE_SOURCE_DIR = data/android/root
OTHER_FILES += data/android/root/src/my/FooPrg.java
jniload_lib.pro:
TARGET = jniload_lib
CONFIG += qt resources
QT += core gui widgets
android: QT += androidextras
SOURCES += src/MyLib.cpp
HEADERS += ifc/MyLib.h
TEMPLATE = lib
INCLUDEPATH += ifc
# This does has apparently no effect on library
ANDROID_PACKAGE_SOURCE_DIR = data/android/root
OTHER_FILES += data/android/root/src/my/FooLib.java
Finaly got a way to work this out.
I removed ANDROID_PACKAGE_SOURCE_DIR line from jniload.pro file and hanlde manual copy of the .java files through custom build steps:
custom_jniload_lib_step.target = jniload_lib_mockup.h
custom_jniload_lib_step.commands = $(COPY_DIR) data\android\root ..\..\android-build
QMAKE_EXTRA_TARGETS += custom_jniload_lib_step
PRE_TARGETDEPS += jniload_lib_mockup.h
custom_jniload_step.target = jniload_mockup.h
custom_jniload_step.commands = $(COPY_DIR) data\android\root ..\..\android-build
QMAKE_EXTRA_TARGETS += custom_jniload_step
PRE_TARGETDEPS += jniload_mockup.h
Then, upon deployment, android-build/src contains both FooLib.java and FooPrg.java and then both library and program can access them!
I am using JNI and I need to pass DateTime as an argument to a method. What format should I use?
Here is an example of my code:
int number = 10;
initial = env->GetMethodID(Simulator,"initialize", "(Ljava/util/Date;Ljava/util/Date;I)V");
if (env->ExceptionCheck()){
cout << "Fail:";
}
env->CallVoidMethod(Simulation,initial,"2014/05/21T00:00:00","2014/05/21T23:59:59",number);
I need to pass these Dates as Ljava/util/Date arguments and not as strings because I can't change the java code that JNI calls.
You just have to create the two java.util.Date objects first. Like this:
int number = 10;
jmethodID initial = env->GetMethodID(Simulator,"initialize", "(Ljava/util/Date;Ljava/util/Date;I)V");
if (env->ExceptionCheck()){
cout << "Fail:";
}
jclass dateType = env->FindClass("Ljava/util/Date;");
if(dateType == nullptr){
cout << "Fail:";
}
jmethodID dateTypeConstructor= env->GetMethodID(dateType, "<init>", "(Ljava/lang/String;)V");
if(constructor == nullptr){
cout << "Fail:";
}
jstring constructorString1 = env->NewStringUTF("2014/05/21T00:00:00");
jobject dateObject1 = env->NewObject(dateType , dateTypeConstructor, constructorString1);
if(dateObject1 == nullptr)
cout << "Fail:";
}
jstring constructorString2 = env->NewStringUTF("2014/05/21T23:59:59");
jobject dateObject2 = env->NewObject(dateType , dateTypeConstructor, constructorString2);
if(dateObject2 == nullptr)
cout << "Fail:";
}
env->CallVoidMethod(Simulation, initial, dateObject1, dateObject2, number);
The constructor Date(String s) is deprecated since JDK 1.1, but the principle is the same if you have to use a SimpleDateFormat or you can just write a static method in Java which will return a java.util.Date Object for the String that you pass to it.
I have a C++ class that uses a JNI JVM to run a Java method. For whatever reason my program is working for 3 iterations and on the 3rd loop the JVM crashes with a SIGSEGV.
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00007f2af5de1c81, pid=7669, tid=139822523557696
#
# JRE version: Java(TM) SE Runtime Environment (7.0_45-b18) (build 1.7.0_45-b18)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (24.45-b08 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# C [libc.so.6+0x7ac81]
This code is not complete at all.
#include <string>
#include <thread>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include "imgsearch.h"
#define PORT "8675"
#define BACKLOG 10
JNIEnv* create_vm() {
JavaVM* j;
JNIEnv* e;
JavaVMInitArgs j_args;
JavaVMOption opts;
j_args.version = JNI_VERSION_1_6;
j_args.nOptions = 1;
j_args.ignoreUnrecognized = 0;
char* cp = (char *)"-Djava.class.path=.:./lucene-4.4.0/analysis/common/lucene-analyzers-common-4.4.0.jar:./lucene-4.4.0/core/lucene-core-4.4.0.jar:./lucene-4.4.0/queryparser/lucene-queryparser-4.4.0.jar";
opts.optionString = cp;
j_args.options = &opts;
JNI_CreateJavaVM(&j, (void**)&e, &j_args);
return e;
}
JNIEnv* env = create_vm();
JavaVM* jvm;
void sigchld_handler(int s)
{
while(waitpid(-1, NULL, WNOHANG) > 0);
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
void sockcom(int csock) {
std::cout << "Yeah!" << std::endl;
}
int main(int argc, char *argv[]) {
env->GetJavaVM(&jvm);
std::string fP = "features/esp.feature";
ImgSearch *iS;
iS = new ImgSearch();
iS->LoadFeatures(fP);
//iS->BuildClusters(150000, 15);
//iS->ClustersToFile("output.clusters");
iS->FileToClusts("output.clusters");
iS->BuildIndex();
//iS->IndexToFile("output.index");
//iS->FileToIndex("output.index");
//iS->FindNeighbors();
//iS->NeighborsToFile("output.neighbors");
iS->FileToNeighbors("output.neighbors");
iS->BagOfWords("features/imglist.txt", "features/esp.size", "coll/output.bagofwords");
iS->BuildLuceneIndex("coll/output.bagofwords");
//std::string queryfile = "queries/query1.pgm";
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
return 2;
}
freeaddrinfo(servinfo); // all done with this structure
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) { perror("sigaction"); exit(1); }
printf("server: waiting for connections...\n");
while(1) { // main accept() loop
int pipefd[2];
int pres = pipe(pipefd);
if (pres < 0) {
perror("pipe");
continue;
}
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), s, sizeof s);
printf("server: got connection from %s\n", s);
std::thread th(sockcom, new_fd);
th.detach();
std::string queryfile = "queries/query3.pgm";
iS->Search(queryfile);
}
jvm->DestroyJavaVM();
return 0;
}
So iS->Search(queryfile); basically is the following:
void ImgSearch::Search(std::string &filePath) {
ImgQuery newQuery(filePath, Ndx, WORDS);
newQuery.ExtractKeyPoints();
newQuery.FindNeighbors();
newQuery.BagOfWords();
newQuery.LuceneSearch();
return;
// Error Checking?
}
Where the LuceneSearch function is the only one that uses the JNI JVM.
void ImgQuery::LuceneSearch() {
jclass qcls = env->FindClass("BatchSearch");
if (qcls == NULL) std::cout << "BatchSearch: Not Found." << std::endl;
jmethodID qmid = env->GetStaticMethodID(qcls, "main", "()V");
env->CallStaticVoidMethod(qcls, qmid);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
}
std::cout << "Still working..." << std::endl;
return;
// Error Checking?
}
Basically, what I am wondering is A) do you see anything obviously wrong? and B) How do you properly reuse a JVM throughout a project?
EDIT: Is there a way to wait for a JVM to be ready?
env->FindClass("java.lang.Math"); fails. Why?
gcc -I/System/Library/Frameworks/JavaVM.framework/Headers test.cpp -framework JavaVM -o test && ./test
http://developer.apple.com/library/mac/#samplecode/simpleJavaLauncher/Listings/utils_h.html#//apple_ref/doc/uid/DTS10000688-utils_h-DontLinkElementID_7
http://developer.apple.com/library/mac/#technotes/tn2147/_index.html
#include <jni.h>
#include <stdlib.h>
int main() {
printf("START.\n");
JavaVM* jvm = NULL;
JNIEnv *env;
JavaVMInitArgs vm_args;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 0;
int ret = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if(ret < 0) {
printf("Unable to Launch JVM\n");
return 1;
}
jclass mathClass = env->FindClass("java.lang.Math");
if (mathClass == NULL) {
printf("Unable to find java.lang.Math\n");
return 1;
}
jmethodID cosMethod = env->GetStaticMethodID(mathClass, "cos", "(D)D");
if (cosMethod == NULL) {
printf("Unable to find java.lang.Math.cos()\n");
return 1;
}
printf("call\n");
jdouble jIn = 0.1;
jdouble jOut = env->CallStaticIntMethod(mathClass, cosMethod, jIn);
printf("jOut: %f", jOut);
printf("DestroyJavaVM.\n");
jvm->DestroyJavaVM();
printf("END.\n");
return 0;
}
You should be calling:
jclass mathClass = env->FindClass("java/lang/Math");
From the documentation:
name: fully-qualified class name (that is, a package name, delimited by “/”, followed by the class name). If the name begins with “[“ (the array signature character), it returns an array class.
Try:
env->FindClass("java/lang/Math")