I call a function in C++ from java. In java I have an array of Strings that I want to use in my C++-function.
I have in C++:
std::string names[6]; // Global variable
extern "C"
JNIEXPORT void JNICALL
Java_com_erikbylow_mycamera3_JNIUtils_updateStandingBoard(JNIEnv *env, jobject type, std::string *names, jint nbrElements){
memcpy(standingText, names, 6* sizeof(std::string));
nbrStandText = nbrElements;
}
In `Java`:
public static void updateStanding( String resultArray[]){
updateStandingBoard(resultArray, resultArray.length);
}
What is the simplest way of achieving what I want? When I try this and different variants it either crashes or yields nonsense data.
JNI is a primarily a C API, it doesn't know anything about std::string as you can validate by calling javah on the Java source file contained the native methods declaration.
Also Java isn't C, there is no need to pass the array size as additional parameter.
So your native void updateStandingBoard(String[] result, int size) should actually be native void updateStandingBoard(String[] result)
With this in mind, the JNI code should be
std::vector<std::string> names; // much safer or use std::array as alternative
extern "C"
JNIEXPORT void JNICALL
Java_com_erikbylow_mycamera3_JNIUtils_updateStandingBoard(JNIEnv *env, jobject type, jobjectArray names) {
jint nbrElements = env->GetArrayLength(names);
// now copy the strings into C++ world
for (int i = 0; i < nbrElements ; i++) {
// access the current string element
jobject elem = env->GetObjectArrayElement(names, i);
jsize length = env->GetStringLength(elem);
// pin it to avoid GC moving it around during the copy
const jchar *str = env->GetStringChars(elem, nullptr);
names.push_back(std::string(str, length));
// make it available again to the VM
env->ReleaseStringChars(elem, str);
}
}
This was just for the basic strings, if you are interested in UTF strings, then you should make use of std::wstring and the UTF variants of the above JNI functions.
Related
Does anyone know how to solve the error?
JNIEXPORT jstring JNICALL JAVA_com_pfc_camera_ndkmain_MainActivity_compresion(JNIEnv* env, jobject obj, jobjectArray jargv){
//jargv is a Java array of Java strings
int argc = env->GetArrayLength(jargv);
typedef char *pchar;
pchar *argv = new pchar[argc];
int i;
for(i=0; i<argc; i++)
{
jstring js = env->GetObjectArrayElement(jargv, i); //A Java string
const char *pjc = env->GetStringUTFChars(js); //A pointer to a Java-managed char buffer
size_t jslen = strlen(pjc);
argv[i] = new char[jslen+1]; //Extra char for the terminating null
strcpy(argv[i], pjc); //Copy to *our* buffer. We could omit that, but IMHO this is cleaner. Also, const correctness.
env->ReleaseStringUTFChars(js, pjc);
}
//Call main
Principal *pa=Principal::CreateInstance(argc,argv);
pa->Run();
pa->FreeInstance();
//Now free the array
for(i=0;i<argc;i++)
delete [] argv[i];
delete [] argv;
}
I understand that the error can come from not doing a casting but I do not have it very clear
[ https://i.stack.imgur.com/bOWKZ.png][1]
It seems that the problem has been solved, now I get another error but I do not understand if I'm passing two arguments js and pjc
[ https://i.stack.imgur.com/UHCAR.png][1]
In C++ you have to use explicit conversion to your desired type.
jstring js = (jstring)env->GetObjectArrayElement(jargv, i);
You can learn about jni programming here
Regarding your other question do the following:
const jbyte *pjc = env->GetStringUTFChars(js, NULL);
I need to integrate C++ files into my Android application project.
I can build the files and .so file is generated.
This is the header file which has the function process().
I need to invoke this method from my .java file.
class PayrollGenerator {
public:
typedef void (* PAYROLL_READY_CALLBACK) (std::vector<int> list, int id);
typedef void (* PROGRESS_CALLBACK) (int progress);
private:
PAYROLL_READY_CALLBACK _dataReadyCallback;
PROGRESS_CALLBACK _progressCallback;
public:
DataProcessor(PAYROLL_READY_CALLBACK dataReadyCallback, PROGRESS_CALLBACK progressCallback);
void process(int data);
};
Two callbacks are there which will give me result and the progress data of the data being processed.
I am not able to design the JNI methods for it.
BTW, I am run simple C++ programs from .java files.
But this one is quite complex to me.
Please help !!
Progress -
I created a C++ file and wrote the wrapper
JNIEnv *global;
jobject impl;
struct DataProcessorStruct {
jobject callback_ptr;
DataProcessor *dataProcessor;
};
void dataReadyCallback(std::vector<jint> processedSamples, jint heartRate){
jintArray arr = global->NewIntArray( processedSamples.size() );
global->SetIntArrayRegion( arr, 0, processedSamples.size(), ( jint * ) &processedSamples[0] );
jclass clazz = global->FindClass("com/app/AudioActivity");
jmethodID method = global->GetMethodID(clazz, "dataReady","[Ljava/util/List;I)V");
global->CallVoidMethod(impl,method,arr,heartRate);
__android_log_print(ANDROID_LOG_INFO, "test", "C-dataReadyCallback");
}
void progressCallback(jint progress){
jclass clazz = global->FindClass("com/app/AudioActivity");
jmethodID method = global->GetMethodID(clazz, "dataProcessProgress","(I)V");
global->CallVoidMethod(impl, method,progress);
__android_log_print(ANDROID_LOG_INFO, "test", "C-progressCallback");
}
JNIEXPORT jlong JNICALL Java_com_app_AudioActivity_createDataProcessorObject (JNIEnv * env, jobject obj){
global = env;
impl = obj;
DataProcessorStruct *cpp_obj = new DataProcessorStruct();
if (cpp_obj == NULL) {
return 0;
}
DataProcessor *csObj = new DataProcessor(dataReadyCallback,progressCallback);
if (csObj == NULL) {
return 0;
}
cpp_obj->dataProcessor = csObj;
cpp_obj->callback_ptr = env->NewGlobalRef(obj);
return (jlong)cpp_obj;
}
JNIEXPORT void JNICALL Java_com_app_processData (JNIEnv * env, jobject obj,jlong objId,jint sample,jint dataLeft){
impl = obj;
DataProcessorStruct *cpp_obj = (DataProcessorStruct *)objId;
DataProcessor *dataProcessor=cpp_obj->dataProcessor;
if (dataProcessor != NULL){
dataProcessor->process(sample,dataLeft);
}
}
The native methods in Java are -
public native long createDataProcessorObject();
public native void processData(long dataProcessor,int sample, int dataLeft);
Is this the right way of doing so ?
And is there any way I don't have to call class Java methods directly from dataReadyCallback() and progressCallback() C++ methods but somehow I can call interface methods which are in Java to get invoked from these C++ methods so that any class listening to these callbacks should get notified and not a particular class?
You can easily define native functions in Java wich are callbacks in your C++ programme. Usually you start declaring a class and some functions in java:
class MyJNative {
static { // static bloc, executed when class is loaded => load your cpp library
System.out.print ("Load library in ");
System.out.println(System.getProperty("java.library.path"));
System.loadLibrary("MyNativeCPP");
}
private static int next_id=1; // every object gets a unique id
private int id; // unique object id
public MyJNative () { // constructor (sets unique id)
id = next_id++;
// ...
}
public native static void doSetup(); // initialisation for your factory function
public native void doSomething(); // example method
...
}
Then you'll let javah generate the header for the native funtions in C++. You shouldn't hand-write the header yourself ! Here a step by step tutorial, and the example for the code above:
...
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: MyJNative
* Method: doSetup
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_MyJNative_doSetup
(JNIEnv *, jclass);
/*
* Class: MyJNative
* Method: doSomething
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_MyJNative_doSomething
(JNIEnv *, jobject);
...
#ifdef __cplusplus
}
#endif
#endif
As you see, JNI is designed to call C++/C functions and not member functions of C++ class. So you need to define some global functions (or static member functions of your class), that will get as a (first) parameter some ID which can identify the C++ object, and will then forward the call to this object.
The example above is simplified, but the idea would be that, the Java class maintains a unique ID. You could imagine that at creation, after setting the ID, it calls a factory function in C++, that would maintain a map<jint, unique_ptr<MyCPPclass>> objects so that all other functions like doSomething() can be invoked with ID as parameter and then call objects[ID].get()->doSomething().
The problem with this approach is the life of your C++ object: you manage it's destruction: C++ objects created on the C++ side won't be garbage collected, so it's difficult to guess when they are no longer needed.
A variant of this approach would be to host ther you "host it" somewhere in a byte array of a java object. But in this case the problem is the other way round: when the Java object is no longer needed, the C++ side won't be informed that the object is destructed and so the destructor won't be called.
As you can see, the JNI design makes it rather difficult to manage objects on both sides. So the safest approach is to manage objects on one side only and on the other side expose only functions that will use/manipulate the object.
I am trying to use openCV in my android project and trying to run this native code but I don't know how to use this parameter
JNIEXPORT jint JNICALL Java_com_example_helloopencvactivity_nativecalls_filepath
(JNIEnv * env, jobject jo, jstring str1, jstring str2) {
cv::Mat img1 = cv::imread("");
}
I tried using this
const char *nativeString = (*env)->GetStringUTFChars(env, str1, 0);
cv::Mat img1 = cv::imread(nativeString);
but i am getting this error error: no matching function for call to '_JNIEnv::GetStringUTFChars
I need to pass the file path from android file system to openCV's native code for processing, the passing element is string and should be read by imread
first in the java side, my codes looks likeļ¼
private String path="/mnt/sdcard/";
InitFeature(width,height,path);
public native void InitFeature(int width, int height,String path);
then in the jni side, it's:
//passed from the java part and release after InitFreature
const char* nPath;
//to save the globle path
char* g_path;
JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial6_Tutorial2Activity_InitFeature(JNIEnv* env, jobject,jint width,jint height,jstring path);
JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial6_Tutorial2Activity_InitFeature(JNIEnv* env, jobject,jint width,jint height,jstring path)
{
//jsize path_size;
nPath=env->GetStringUTFChars(path,NULL);
//path_size=env->GetArrayLength(path);
LOGD("path: %s \n",nPath);
int length=strlen(nPath);
LOGD("length: %d \n",length);
g_path=new char[length+1];
memcpy(g_path,nPath,length+1);
LOGD("path_2: %s \n",g_path);
LOGD("length: %d \n",strlen(g_path));
char l_path[128];
strcpy(l_path,g_path);
strcat(l_path,"color.jpg");
LOGD("path_3: %s \n",l_path);
LOGD("length: %d \n",sizeof(l_path));
m_width=width;
m_height=height;
center.x=float (m_width/2.0+0.5);//float (Img_tmp->width/2.0+0.5);
center.y=float (m_width/2.0+0.5);//float (Img_tmp->height/2.0+0.5);
env->ReleaseStringUTFChars(path,nPath);
}
since I have different native calls, one to initiate features(shown here) and others to process every frame, and after you env->ReleaseStringUTFChars(path,nPath); the string would be invisible to jni part. I have the copy the string to a global char* g_path;
and a little sample is here as well, the file path is "/mnt/sdcard/color.jpg" and check those logs.
then you can use imread() to get this jpg.
I use other libs like libjpg, so I am not showing the codes here.
I want to know how to get a custom object from Java to c++?
I need to implement a method in c++ for get performance. I already have the method working in java but I want to port to c++.
On Java a I call the method like this:
private native boolean P(Mat Previous, String Name);
On CPP file I need to get the mat object. Getting string is easy! But how can I get the custom mat object similar to c++(cv::Mat)? I need to get java Mat into a cv::Mat.
Here the cpp file:
JNIEXPORT bool JNICALL Java_br_raphael_detector_SimpsonDetector_P
(JNIEnv* env,jobject thiz, jobject Previous, jstring Name){
jboolean sim = false;
const char* N = env->GetStringUTFChars(Name,0);
std::string Nome = N;
//Release
env->ReleaseStringUTFChars(Name,N);
//Then Return
return sim;
}
A java Mat object is a totally different thing from a native cv::Mat, you can't get one directly from the other.
That said, if you know what fields are inside Mat, and you know the corresponding fields in cv::Mat, you can write a conversion function that copies the contents of the fields one-by-one.
// First get the Mat class
jclass Mat = (*env)->GetObjectClass(env, Previous);
// To get a field
jfieldId field = (*env)->GetFieldID(env, Mat, "fieldName", field type);
// To get a method
jmethodId method = (*env)->GetMethodID(env, Mat, "methodName", method signature);
from there you can read the values of fields, or call methods
// getting a field
(*env)->GetObjectField(env, Previous, field);
// calling a method
(*env)->CallObjectMethod(env, Previous, method, parameters);
refer to http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html for details
I'm one year late to the party, but the way you pass a Mat to C is with a jlong with the address of the Java's Mat. You can do something like this:
private static native boolean P(long matAddress, String name);
In C:
JNIEXPORT jboolean JNICALL Java_br_raphael_detector_SimpsonDetector_P
(JNIEnv* env,jobject thiz, jlong matAddress, jstring Name)
{
cv::Mat* image = (cv::Mat*)matAddress;
// Do stuff with image.
}
Then you would call the method in Java like this:
P(myMat.getNativeObjAddr(), name);
OpenCV exposes this method just for cases like this. (I would link to the documentation's page but the website is not loading here, sorry.)
I am having a hard time finding an answer to this. But, what is "jboject thiz" used for in JNI function calls? For example:
jobjectArray Java_com_gnychis_awmon_Test( JNIEnv* env, jobject thiz ) {
I use env to allocate objects often, but I've never used thiz and I'm not sure what it is for. Just for knowledge purposes.
The following is a JNI wrapper function which has two parameters, and returns a primitive array of objects:
jobjectArray Java_com_gnychis_awmon_Test( JNIEnv* env, jobject thiz );
From the function name you have given I don't think it is complete, that is, you haven't respected the obligatory function name convention which is:
Start the function with Java_
Append the package name separated by _ (undescores) i.e. com_company_awesomeapp. So far the function name is composed of: Java_com_company_awesomeapp
Append the Java class name where the native method has been defined,
followed by the actual function name. So at this point we should have the following function name: Java_com_company_awesomeapp_MainActivity_Test
The first parameter is a pointer to a structure storing all JNI function pointers, i.e. all the predefined functions you have available after you #include <jni.h>.
The second parameter is a reference to the Java object inside which this native method has been declared in. You can use it to call the other methods of the Java object from the current JNI function, i.e. Call Java instance methods from JNI code written in C or C++.
If for example you have the following Java class inside the MainActivity.java file:
public class MainActivity extends Activity
{
static
{
try
{
System.loadLibrary("mynativelib");
}
catch (UnsatisfiedLinkError ule)
{
Log.e(TAG, "WARNING: Could not load native library: " + ule.getMessage());
}
}
public static native Object[] Test();
}
Then, the jobject thiz parameter of the JNI function would be a reference to an object of type MainActivity.
I found this link that should help clarify the question.
https://library.vuforia.com/articles/Solution/How-To-Communicate-Between-Java-and-C-using-the-JNI
Here is an example in it that uses the "jobject".
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_initApplicationNative(
JNIEnv* env, jobject obj, jint width, jint height)
{
...
jclass activityClass = env->GetObjectClass(obj);
jmethodID getTextureCountMethodID = env->GetMethodID(activityClass,
"getTextureCount", "()I");
if (getTextureCountMethodID == 0)
{
LOG("Function getTextureCount() not found.");
return;
}
textureCount = env->CallIntMethod(obj, getTextureCountMethodID);
...
}
jobject thiz means the this in java class.
Sometimes if you create a static native method like this.
void Java_MyClass_method1 (JNIEnv *, jclass);
jclass means the class itself.