I am working on an example of JNI in Android Studio, the goal it to generate a Random Value and pass it to a native function which will calculate its Square and return the result in this format ( Number/Square ).
I am passing the jint number as a parameter of the function, but the result is totally wrong (the result displayed is totaly wrong).
Here is my code :
Button Generating the number and calling the native function :
Button ButtonP = (Button)findViewById(R.id.button3);
ButtonP .setOnClickListener(
new View.OnClickListener()
{
public void onClick(View view)
{
Random r = new Random();
Integer valeur = 1 + r.nextInt(10 - 1);
Log.i("Tag", "Random Value BARRAK " + valeur);
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNIStop(valeur));
}
});
The Native function:
public native String stringFromJNIStop(Integer nombre);
The implementation of the function in the cpp file:
extern "C"
JNIEXPORT jstring JNICALL
Java_fr_utbm_testjniapplication1_MainActivity_stringFromJNIStop(
JNIEnv *env,
jobject, /* this */
jint nombre) {
jint CarreNombre = nombre*nombre;
//Convertir le carré en un jstring
char bufCarreNombre[64];
sprintf(bufCarreNombre, "%d", CarreNombre); // error checking omitted
jstring jStringCarre = (*env).NewStringUTF(bufCarreNombre);
//Le convertir en un char *
const char *strCarre= (*env).GetStringUTFChars(jStringCarre,0);
//Convertir le nombre en un jstring
char bufNombre[64];
sprintf(bufNombre, "%d", nombre); // error checking omitted
jstring jStringNombre = (*env).NewStringUTF(bufNombre);
//Le convertir en char *
const char *strNombre= (*env).GetStringUTFChars(jStringNombre,0);
//Concaténer les deux
char *concatenated;
concatenated = (char *) malloc(strlen(strNombre) + strlen("/") + strlen(strCarre) + 1);
strcpy(concatenated, strNombre);
strcat(concatenated, "/");
strcat(concatenated, strCarre);
/* Create java string from our concatenated C string */
jstring retval = (*env).NewStringUTF(concatenated);
//need to release this string when done with it in order to
//avoid memory leak
(*env).ReleaseStringUTFChars(jStringNombre,strNombre);
(*env).ReleaseStringUTFChars(jStringCarre,strCarre);
/* Free the memory in concatenated */
free(concatenated);
return retval;
}
The Native function:
public native String stringFromJNIStop(Integer nombre);
The implementation of the function in the cpp file:
extern "C"
JNIEXPORT jstring JNICALL
Java_fr_utbm_testjniapplication1_MainActivity_stringFromJNIStop(
JNIEnv *env,
jobject, /* this */
jint nombre) {
This does not agree with your Java. You've changed it from int to Integer in the Java without regenerating your .h file, or you've changed or invented your .h file without reference to your .java file; or your .cpp file doesn't agree with your .h/.hpp file. Don't do this. Use javah to generate your .h/.hpp file, and redo it every time you change the native declaration(s) in your .java files, and make sure your .cpp file agrees with the .h/.hpp file. It should be:
extern "C"
JNIEXPORT jstring JNICALL
Java_fr_utbm_testjniapplication1_MainActivity_stringFromJNIStop(
JNIEnv *env,
jobject, /* this */
jobject nombre) {
where nombre refers to an Integer. However it would be better, and would always have been better, to define your Java native method as:
public native String stringFromJNIStop(int nombre);
which will now agree with your existing .cpp.
Also your .cpp should #include your .h/.hpp. Then you wouldn't have needed extern "C" or JNI_EXPORT or JNI_CALL, and the compiler may have detected the signature disagreement between .cpp and .h/.hpp.
After some reflexion, problem was resolved, in fact, in order to implement the JNI function using jint, we have to adopt the exact conversion rules in the JAVA part, so instead of declaring the random value as an integer, we must declare it as a long ! so we must work as follows :
long valeur = 1 + r.nextInt(10 - 1);
Related
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.
I'm currently developping an android app with android studio, for this project i need to use a custom library written in c/c++ .
So i in order to do that i needed to use NDK.
The library contain methods that i need to implements in order to have access to specifics fonctions in android
My question is : how can I call my jni method inside the method existing in c
which will call a java method to store session key in the android system
So for a pratical exemple since it's maybe not clear:
In a file called lib_exemple.c i have three methods
the first one is the initialisation call to the library (jni to library c) -->working
JNIEXPORT void JNICALL
Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1init
(JNIEnv *env, jobject instance) {
lib_Example_init('0'); // c func of the library
}
then the second one is the jni who call a java method (jni to java) -> working
JNIEXPORT jint
JNICALL Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1store_1session_1key
(JNIEnv * env, jobject jobject1, jbyte jbyte1, jchar jchar1, jbyte jbyte2){
jclass clazz = (*env)->FindClass(env, "com/example/libExemple/Libs/ExempleLib");
jmethodID mCurrentActivityId = (*env)->GetMethodID(env, clazz, "KeyStoreSessionKey", "(BCB)I");
jint result = (*env)->CallIntMethod(env, jobject1, mCurrentActivityId, jbyte1, jchar1, jbyte2);
return result;
}
and the third method is the one the library c have in it (library C to jni)
int lib_Exemple_store_session_key(uint8_t Session, P_KEY_ST_T pKey, uint8_t keyType) {
//i want to call jni func here , so the librairy can access the native android function
return 0;
}
then to configure the ndk in a file called ExempleLib.java i have defined
static {
System.loadLibrary("LibExemple");
}
then the prototype of the initialisation of the library
public native void libExemple_init();
the prototype of the first method in lib_exemple.c
public native int lib_Exemple_store_session_key(byte pKey, char key_length, byte keyIndex);
and the java fonction called by it
protected int KeyStoreSessionKey(byte pKey, char key_length, byte keyIndex) {
...
}
my mainActivity contain
ExempleLib LibFunc = new ExempleLib();
Log.d(TAG, "init lib" );
LibFunc.lib_Exemple_init();
My goal is that after initialising my library and call an init function , it can call the first method in the second method of lib_exemple.c (the one used in the library )
thanks
EDIT :
The solution of my problem is when i initialize the lib , i need to save jvm
so i can access the JNIenv in the library method directly
jobject Savedinstance = NULL;
static JavaVM *cachedJVM;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
cachedJVM = jvm;
return JNI_VERSION_1_6;
}
JNIEnv *AttachJava() {
JavaVMAttachArgs args = {JNI_VERSION_1_6, 0, 0};
JNIEnv *java;
(*cachedJVM)->AttachCurrentThread(cachedJVM, &java, &args);
return java;
}
JNIEXPORT jint JNICALL
Java_com_example_vgosselin_libExemple_Libs_ExempleLib_lib_1init(JNIEnv *env,
jobject instance) {
Savedinstance = instance;
void *element = 0;
jint result;
result = lib_Exemple_init_();
return result;
}
so i can call the java method in
int lib_Exemple_store_session_key(uint8_t Session, P_KEY_ST_T pKey, uint8_t keyType) {
JNIEnv *NewEnv = AttachJava();
jclass clazz = (*NewEnv)->FindClass(NewEnv,"com/example/vgosselin/libExemple/Libs/ExempleLib");
jmethodID mCurrentActivityId = (*NewEnv)->GetMethodID(NewEnv,clazz,"KeystoreStoreKey","([BCB)I");
jint result;
jbyteArray Data = 0;
jchar Key = 0;
jbyte Type = 0;
result = (*NewEnv)->CallIntMethod(NewEnv, Savedinstance, mCurrentActivityId, Data, Key, Type);
return result;
}
and now i don't need the method in jni syntax anymore
JNIEXPORT jint JNICALL Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1store_1session_1key
I have implemented Jansson in Android with C and made a function which calculates values from json and that works in C, I tried to use that code in NDK with JNI it builds with no errors, but as i tried to arrange the code to work with JNI it gives me pointer error warning: return from incompatible pointer type. I have read that i need to use jlong for pointers but i cant figure out how that works, it is my first time working in it.
This is my code from C (gives no errors and compiles)
char *doCalc (char *invoice_str) {
json_error_t error;
json_t *invoice = json_loads (invoice_str, JSON_DISABLE_EOF_CHECK, &error);
...
char *result = json_dumps (json_data, JSON_PRESERVE_ORDER);
return result;
}
C code Arranged to work with JNI (gives me error warning: return from incompatible pointer type, which if im correct is because of jchar)
JNIEXPORT jchar JNICALL *Java_com_example_test_doCalc (JNIEnv* env, jobject obj,char const *invoice_str) {
json_error_t error;
json_t *invoice = json_loads (invoice_str, JSON_DISABLE_EOF_CHECK, &error);
...
char *result = json_dumps (json_data, JSON_PRESERVE_ORDER);
return result;
}
Then in my Activity I like to would run doCalc(charJ);, charJ has Json in it. Which would then give me dump of calculated values.
Also I might be looking at this completely wrong, any help is appreciated.
Try to use jstring instead of char*
JNIEXPORT jchar JNICALL * Java_com_example_test_doCalc(JNIEnv * env, jobject obj, jstring invoice_jstring) {
//convert invoice_jstring to char* link bellow
json_error_t error;
json_t * invoice = json_loads(invoice_str, JSON_DISABLE_EOF_CHECK, & error);
...
char * result = json_dumps(json_data, JSON_PRESERVE_ORDER);
return result;
}
for conversion jstring to char* you can use this answer:
JNI converting jstring to char *
I am trying to follow this tutorial
http://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html
but I'm in eclipse making an android app
So I create blank project and add a new class
package com.example.jpgtest;
import android.util.Log;
public class TestJNIPrimitive {
//static {
//System.loadLibrary("myjni"); // myjni.dll (Windows) or libmyjni.so (Unixes)
// System.load("d:/libjpeg.so");
// Log.d("TestJNIPrimitive", "load ok");
// }
// Declare a native method average() that receives two ints and return a double containing the average
private native double average(int n1, int n2);
// Test Driver
public static void main() {
System.load("d:/libjpeg.so");
Log.d("TestJNIPrimitive", "load ok");
double d = new TestJNIPrimitive().average(3, 2);
//double d = new TestJNIPrimitive().average(3, 2);
Log.d("TestJNIPrimitive", "d="+d);
}
}
and call it from onCreate()
TestJNIPrimitive t = new TestJNIPrimitive();
t.main();
I made the c and h file like this
TestJNIPrimitive.h
/
* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestJNIPrimitive */
#ifndef _Included_TestJNIPrimitive
#define _Included_TestJNIPrimitive
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TestJNIPrimitive
* Method: average
* Signature: (II)D
*/
JNIEXPORT jdouble JNICALL Java_TestJNIPrimitive_average
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
TestJNIPrimitive.c
#include <jni.h>
#include "TestJNIPrimitive.h"
JNIEXPORT jdouble JNICALL Java_TestJNIPrimitive_average(JNIEnv *env, jobject thisObj, jint n1, jint n2)
{
jdouble result;
//printf("In C, the numbers are %d and %d\n", n1, n2);
result = ((jdouble)n1 + n2) / 2.0;
// jint is mapped to int, jdouble is mapped to double
return result;
}
i build it and make a libjpg.so on my d drive root
i check the symbols
$ readelf -Ws /cygdrive/d/libjpeg.so | grep average
272: 00003f5c 72 FUNC GLOBAL DEFAULT 7 Java_TestJNIPrimitive_average
1896: 00003f5c 72 FUNC GLOBAL DEFAULT 7 Java_TestJNIPrimitive_average
When I run it, the load works ok but the call to average fails with
No implementation found for native Lcom/example/jpegtest/TestJNIPrimative;.average (II)D
Any ideas please, I'm really stuck on this
Thanks
You are missing the packace into JNI. That's the reason the function is not found.
Your Java package is: com.example.jpgtest
So your Native function must be:
JNIEXPORT jdouble JNICALL Java_com_example_jpgtest_TestJNIPrimitive_average(JNIEnv *env, jobject thisObj, jint n1, jint n2)
{
...
}
After compile with ndk-build, and use your new libjpeg.so must work.
I have to add that your java looks weird (at less to my eyes). I will implement that as:
package com.example.jpgtest;
import android.util.Log;
public class TestJNIPrimitive
{
// Test Driver
public static void main()
{
Log.d("TestJNIPrimitive", "load ok");
double d = new TestJNIPrimitive().average(3, 2);
Log.d("TestJNIPrimitive", "d="+d);
}
// Native functions (Callbacks from Java to C++)
// =========================================================================
// Declare a native method average() that receives two ints and return a double containing the average
private native double average(int n1, int n2);
// Load Native Libraries
// =========================================================================
static
{
System.load("libjpeg.so");
}
}
And the so must be inside folder libs/armeabi-v7a
I you want a basic theory check this or the specifications
i was unable to figure out the problem, but instead i read this
https://developer.android.com/tools/sdk/ndk/index.html
i build and ran the hello-jni example and then i build a new project and then added in the jni folder from the test and change the package name and the class name to match the new project
then i started modifying the functions to my needs and its ok now
not easy stuff this
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.