I have a function already implemented in cpp with prototype
MyFunction(int size, int (* callback)(UINT16* arg1, UINT16* arg2));
Second argument is a function pointer which must be implemented in java. How can i implement that function?
Also how can i call MyFunction in JNI? Please help
Try this
Java
import java.util.*;
public class JNIExample{
static{
try{
System.loadLibrary("jnicpplib");
}catch(Exception e){
System.out.println(e.toString());
}
}
public native void SetCallback(JNIExample jniexample);
public static void main(String args[]){
(new JNIExample()).go();
}
public void go(){
SetCallback(this);
}
//This will be called from inside the native method
public String Callback(String msg){
return "Java Callback echo:" +msg;
}
}
In C++ native:
#include "JNIExample.h"
JNIEXPORT void JNICALL Java_JNIExample_SetCallback (JNIEnv * jnienv, jobject jobj, jobject classref)
{
jclass jc = jnienv->GetObjectClass(classref);
jmethodID mid = jnienv->GetMethodID(jc, "Callback","(Ljava/lang/String;)Ljava/lang/String;");
jstring result = (jstring)jnienv->CallObjectMethod(classref, mid, jnienv->NewStringUTF("hello jni"));
const char * nativeresult = jnienv->GetStringUTFChars(result, 0);
printf("Echo from Setcallback: %s", nativeresult);
jnienv->ReleaseStringUTFChars(result, nativeresult);
}
The idea here is calling a method in Java through its class instance. In case you do not know the name of java function in advance then function name can be passed as parameter as well.
For more information: http://www.tidytutorials.com/2009/07/java-native-interface-jni-example-using.html
In your C++ native code you can simply define a function call Java callback implementation and then pass the function pointer to your native library -- like this:
native c++:
// link func
callback_link(int size, int (* callback)(UINT16* arg1, UINT16* arg2)){
//jni->call Java implementation
}
// call your lib
MyFunction(int size, int (* callback_link)(UINT16* arg1, UINT16* arg2));
Related
I'm new in JNI and C++. I have to call lib function with shared pointer. My code:
JNIEXPORT jint JNICALL Java_com_test_NativeClient_subscribe(JNIEnv* env, jobject thisObj, jobject handler) {
jclass handlerClass = env->GetObjectClass(handler);
jmethodID starts = env->GetMethodID(handlerClass, "starts", "(I)V");
jmethodID joins = env->GetMethodID(handlerClass, "joins", "(Ljava/lang/String;Ljava/lang/String;)V");
// int subscribe(std::shared_ptr< SomeHandler > handler) // I need implement this
std::shared_ptr<?> sharedPointer = new std::shared_ptr<?>;
return some::lib::subscribe(sharedPointer);
}
SomeHandler it is an interface from lib - some::lib::SomeHamdler, but also I pass java implementation in the method (jobject handler). How I can properly define sharedPointer to call java implementation after subscribe method performed? Thanks in advance.
UPD: Java code:
public native int subscribe(SomeHandler handler); // native method in NativeClient
SomeHandler interface:
public interface SomeHandler {
void starts(int uptime);
void joins(String mac, String name);
SomeHandlerImpl class:
public class SomeHandlerImpl implements SomeHandler {
#Override
public void starts(int uptime) {
System.out.println("uptime is " + uptime);
}
#Override
public void joins(String mac, String name) {
System.out.println("mac: " + mac + ", nName: " + name);
}
All you need to do is store a global reference to the jobject and write some wrapper code:
class JavaWrapperHandler : public some::lib::callback {
jobject java_handler;
public:
JavaWrapperHandler(jobject handler) {
JNIEnv *env = nullptr;
vm->GetEnv(&env, JNI_VERSION_1_6);
java_handler = env->NewGlobalRef(handler);
}
~JavaWrapperHandler() {
JNIEnv *env = nullptr;
vm->GetEnv(&env, JNI_VERSION_1_6);
env->DeleteGlobalRef(java_handler);
}
virtual joins(std::string mac, std::string name) {
JNIEnv *env = nullptr;
vm->GetEnv(&env, JNI_VERSION_1_6);
jclass handlerClass = env->GetObjectClass(java_handler);
jmethodID joins = env->GetMethodID(handlerClass, "joins", "(Ljava/lang/String;Ljava/lang/String;)V");
env->CallVoidMethod(java_handler, joins, ...);
};
};
And you can instantiate this as follows in your JNI method:
std::make_shared<JavaWrapperHandler>(handler);
Note that you still need to store the shared_ptr again somewhere, otherwise it will immediately be freed. You could for example store it in a std::map<long, shared_ptr<JavaWrapperHandler>> and return the long as a jlong.
Points of note:
This code keeps a global reference to prevent the Java handler object from being garbage collected.
The global reference is freed when the handler is destroyed. Make sure to unregister the callback at some point if you want to free the Java object.
We use the GetEnv method from the JNI Invocation API. It will only produce a useful value if the current (C++) thread has already been attached to the JVM. If it fails, you need to call vm->AttachCurrentThread or vm->AttachCurrentThreadAsDaemon.
Let's say I have a Java application that has the following class,
class Adder {
public int add(int a, int b) { return a + b; }
}
If I load into Java a shared library (.so file) using System.loadLibrary("libABC.so"), can a native method in libABC invoke adder.add(1,2) where adder is an instantiation of Adder? If yes, an example/pointer in the right direction would be very much appreciated.
You have Adder class with add() method
class Adder {
public int add(int a, int b) { return a + b; }
}
For example, we have an instance named mAdder in class Demo.
public class Demo {
native void nativeEntry();
Adder mAdder = new Adder();
public static void main(String[] args){
System.loadLibrary("JNIBridge");
Demo demo = new Demo();
demo.nativeEntry();
}
}
In JNI, demo object is stored in thiz pointer.
Use GetObjectField() to get mAdder from thiz.
Use CallIntMethod() to invoke add() method of mAdder.
#include <jni.h>
#include <stdio.h>
//use command
//javah -jni Demo
//to generate jni method declaration
JNIEXPORT void JNICALL Java_Demo_nativeEntry(JNIEnv *env, jobject thiz) {
//thiz is the calling object
//in java main(), we call: demo.nativeEntry()
//so, thiz is demo object
//get class Demo
jclass demoCls = (*env)->FindClass(env, "Demo");
//get id of mAdder in Demo
jfieldID adderField = (*env)->GetFieldID(env, demoCls, "mAdder", "LAdder;");
//get object mAdder from object demo
jobject adderObject = (*env)->GetObjectField(env, thiz, adderField);
//get class Adder
jclass adderCls = (*env)->FindClass(env, "Adder");
//get id of method add in Adder
jmethodID addMethod = (*env)->GetMethodID(env, adderCls, "add", "(II)I");
// (II)I: is signature of method Adder.add()
// use command:
//javap -s -p Adder
//to get method signature
//call method add of object mAdder
jint sum = (*env)->CallIntMethod(env, adderObject, addMethod, 10, 20);
//sum = add(10,20)
printf("sum = %d\n", sum);
}
You can get full code at here.
Yes, and often even if add is a private method.
I have:
public class MyEntity {
private String _myEntityType;
public String get_myEntityType() {
return _myEntityType;
}
public void set_myEntityType(String _myEntityType) {
this._myEntityType = _myEntityType;
}
}
Then :
public class MyObjectEntity extends MyEntity {
public MyObjectEntity() {
super();
}
private String _myObjectDescription;
public String get_myObjectDescription() {
return _myObjectDescription;
}
public void set_myObjectDescription(String _myObjectDescription) {
this._myObjectDescription = _myObjectDescription;
}
}
Now I start getting into JNI.
public class MyPers {
// Load the 'my-pers-lib' library on application startup.
static {
System.loadLibrary("my-pers-lib");
}
public native Long myPersInit(MyEntity myEntity);
}
Then :
#include <jni.h>
#include <android/log.h>
extern "C"
JNIEXPORT jobject JNICALL
Java_my_ca_my_1gen_1lib_1pers_c_1libs_1core_RevPers_myPersInit(JNIEnv *env, jobject instance,
jobject myEntity) {
jclass myEntityClazz = env->GetObjectClass(myEntity);
/** START GET STRING **/
const char *myEntityType;
// and the get_myObjectDescription() method
jmethodID get_myObjectDescription = env->GetMethodID
(myEntityClazz, "get_myObjectDescription", "()Ljava/lang/String;");
// call the get_myObjectDescription() method
jstring s = (jstring) env->CallObjectMethod
(myEntity, get_myObjectDescription);
if (s != NULL) {
// convert the Java String to use it in C
myEntityType = env->GetStringUTFChars(s, 0);
__android_log_print(ANDROID_LOG_INFO, "MyApp", "get_myObjectDescription : %s\n",
myEntityType);
env->ReleaseStringUTFChars(s, myEntityType);
}
/** END GET STRING **/
return myEntityGUID;
}
I start it all up :
MyObjectEntity myObjectEntity = new MyObjectEntity();
myObjectEntity.set_myObjectDescription("A Good Day To Find Answers To Life's Important Questions.");
MyPers revPers = new MyPers();
Log.v("MyApp", "GUID : " + myPers.myPersInit(myObjectEntity));
The error I get is :
JNI CallObjectMethodV called with pending exception java.lang.NoSuchMethodError: no non-static method "Lmy_app/MyEntity;.get_myObjectDescription()Ljava/lang/String;"
QUESTION
How can I go about calling a method of a subclass / child class of an Java Object passed to JNI jobject, myEntity in this case?
extern "C"
JNIEXPORT jobject JNICALL
Java_myPersInit(JNIEnv *env, jobject instance, jobject myEntity)
Thank you all in advance.
How can I go about calling a method of a subclass / child class of an
Java Object passed to JNI jobject, myEntity in this case?
The question does not make sense. Objects do not have subclasses, classes do. Whatever the class of a given object happens to be defines the methods that you may invoke on that object. You seem to be trying to ask how to invoke methods of a subtype of the declared type of the native method parameter, but the premise of such a question runs counter to the actual fact that you do so exactly the same way you invoke any other instance method from JNI.
Consider: in Java, you would downcast to the subtype and then, supposing the cast succeeded, you would invoke the wanted method normally. In JNI, on the other hand, object references are not differentiated by pointed-to object type (they are all jobjects), so no casting is involved; you just go straight to invoking the wanted method normally.
Of course, a JNI error will occur if the class of the target object does not provide such a method. Whether it's a good idea to do what you describe is an entirely separate question.
I have some Java methods that I need to call from C++ via JNI. My JNI implementation is based on
Is it possible to make a Java JNI which calls jdbc?
There are two java files in my Java project. One is to define a class, and the other one contains the actual methods that c++ will call.
public class MyObject {
private static int no;
private static LocalDateTime time;
private static String status;
// getters, setters and toString
}
public class ObjectHandler {
public static MyObject objectReturnToC;
public static Object methodA (type1 arg1, type2 arg2, type3 arg3) {
objectReturnToC = new MyObject();
// setting fields in returnObject according to passed-in parameters
return objectReturnToC;
}
public static void methodB(Object objectReturnedFromC) {
// access fields in objectReturnedFromC, do computation and store in
}
}
I created C++ DLL in Visual Studio 2010. There's JVM.cpp, JVM.h, JavaCalls.h, and JavaCalls.cpp
JavaCalls.h
#ifdef JAVACALLSDLL_EXPORTS
#define JAVACALLSDLL_API __declspec(dllexport)
#else
#define JAVACALLSDLL_API __declspec(dllimport)
#endif
namespace JavaCalls
{
class JavaCalls
{
public:
static JAVACALLSDLL_API void *javaMethodA(type1, type2, type3);
static JAVACALLSDLL_API string toString(void **javaObject);
static JAVACALLSDLL_API void javaMethodB(void **javaObject);
};
}
JavaCalls.cpp
namespace JavaCalls
{
void *JavaCalls::javaMethodA(type1 arg1, type2 arg2, type3 arg3)
{
// invoke JVM
// Find class, methodID
jobject javaObject = CallStaticObjectMethod(jMain, "methodA",...);
return javaObject;
}
void JavaCalls::javaMethodB(void** javaObject) {
// invoke JVM
// Find class, methodID
CallStaticVoidMethod(jMain, "methodB",...);
}
}
C++ calling Java methodA and methodB using DLL:
int main()
{
void* a = JavaCalls::JavaCalls::javaMethodA(arg1, arg2, arg3);
// doing other stuff and updating fields in a
JavaCalls::JavaCalls::javaMethodB(static_cast<void**>(a));
}
Obviously, passing pointer, wishing it would be available to C++ is not working. But what should I do to keep the Java Object in C++ and pass it back to Java later? Should I create a C++ struct and map Java Object field into it using GetObjectField?
I don't quite understand why you need void** in your code. If you want to make the interface opaque, just use void*. Also don't forget to call NewGlobalRef() and DeleteGlobalRef() on the returned jobject - this will prevent its destruction by garbage collector:
void *JavaCalls::javaMethodA(type1 arg1, type2 arg2, type3 arg3)
{
jobject javaObject = CallStaticObjectMethod(jMain, "methodA",...);
return NewGlobalRef(jMain, javaObject);
}
void JavaCalls::javaMethodB(void* javaObject) {
CallStaticVoidMethod(jMain, "methodB", static_cast<jobject>(javaObject));
}
// add this method - it should be called when you finish using the object
void JavaCalls::ReleaseObject(void* javaObject) {
DeleteGlobalRef(jMain, static_cast<jobject>(javaObject));
}
There is a great helper program called javah.exe.
Write your code like you like to code and execute javah to the .class. It will create a full compatible .h file for you.
You will become for this:
public class Test {
public class MyObject {
private int no;
private String status;
}
public class Callback {
public void callback(MyObject afterCpp) {
}
}
public native void register(Callback v, MyObject beforeCpp);
}
This header:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Test */
#ifndef _Included_Test
#define _Included_Test
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Test
* Method: register
* Signature: (LTest/Callback;LTest/MyObject;)V
*/
JNIEXPORT void JNICALL Java_Test_register
(JNIEnv *, jobject, jobject, jobject);
#ifdef __cplusplus
}
#endif
#endif
You simply have to call callback to the 3rd c++-parameter.
I am trying to generate java bindings through swig for these two c++ functions
C++ functions
void SetUserData(void* data);
void* GetUserData() const;
On the java side I want it to look like this
setUserData(Object object);
getUserData() //return java.lang.Object
I would like the void* pointer to point to any java object I pass to it. In my swig file I tried to add these lines
//swig file
%typemap(jstype) void* "java.lang.Object";
%apply Object {void*};
I get a compile error:
b2Fixture.swig(22) : Warning 453: Can't apply (Object). No typemaps are defined. I have no idea how to make typemaps for this. Any ideas?
You probably want to do:
%apply jobject { void* };
because %apply copies the typemaps defined on one C++ type to another C++ type. Object is a Java type, not a C++ one so doesn't have any typemaps to be copied. jobject on the other hand is the JNI equivalent.
Additionally assuming you want to have 'normal' GC semantics (i.e. not be required to retain whatever data you pass in) you'll need to do some more work, for example:
%module test
%{
#include "test.hh"
%}
%apply jobject { void * };
%typemap(in) void * {
void *old = getData();
if (old) JCALL1(DeleteGlobalRef, jenv, (jobject)old);
$1 = JCALL1(NewGlobalRef, jenv, $input);
}
%typemap(out) void * {
$result = (jobject)($1);
}
%include "test.hh"
This makes a new Global Reference for your data, which could be the only thing keeping it from getting freed by the GC.
Given a header file:
void setData(void *);
void *getData();
and an implementation:
#include "test.hh"
namespace {
void *d = nullptr;
}
void setData(void *d) {
::d = d;
}
void *getData() {
return d;
}
That is sufficient to allow:
public class run {
public static void main(String[] argv) {
System.loadLibrary("test");
Object example = "HELLO";
test.setData(example);
System.out.println(test.getData());
}
}
to work correctly.
As written these typemaps are pretty ugly - they'll impact all usage of void *. So if you really used this you would want to use %apply or %clear a second time to limit their impact. You could also name the arguments in the header file and use that to limit where the typemap gets applied.
For completeness, here's another (simpler?) way to do it with JavaCPP. Assuming we have a GlobalData.h file like this in C++:
class Data {
public:
JNIEnv* env;
jobject data;
Data(JNIEnv* env, jobject obj) : env(env), data(NULL) { }
~Data() { if (data != NULL) env->DeleteGlobalRef(data); }
void SetUserData(jobject obj) {
if (data != NULL) env->DeleteGlobalRef(data);
data = env->NewGlobalRef(obj);
}
jobject GetUserData() { return data; }
};
We can use it from Java this way:
import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;
#Platform(include="GlobalData.h")
public class GlobalData {
static { Loader.load(); }
public static class Data extends Pointer {
public Data() { allocate(); }
private native #Raw(withEnv=true) void allocate();
public native void SetUserData(#Raw Object obj);
public native #Raw Object GetUserData();
}
public static void main(String[] args) {
Object someObject = new Object();
Data myData = new Data();
myData.SetUserData(someObject);
Object sameObject = myData.GetUserData();
System.out.println(someObject);
System.out.println(sameObject);
}
}
Where the output shows that someObject and sameObject point to the same object, for example:
java.lang.Object#7aa06577
java.lang.Object#7aa06577