I want to call back Java code from native code.
The Java code:
public final class Underlying {
public static native int setOnEventListener(OnEventListener listener);
public interface OnEventListener {
int EVENT_TEST = 1;
int onEvent(int code, String msg);
}
}
The C++ code (I omit some checks to make it clear):
extern "C" JNIEXPORT jint JNICALL
Java_packageName_Underlying_setOnEventListener(JNIEnv* env, jclass type, jobject listener) {
jclass clz = env->GetObjectClass(listener);
// assign to static jobject
eventListener = env->NewGlobalRef(listener);
// assign to static jmethodID
onEventMethodID = env->GetMethodID(clz, "onEvent", "(ILjava/lang/String;)I");
}
SIGILL (signal SIGILL: illegal instruction operand) occurs in GetMethodID. But I evaluate the same sentence by Evaluate Expression in Android Studio and everything is OK.
There must be some differences between reality and Evaluate Expression. One difference I could think of is that the calling thread may be different. setOnEventListener was originally called in UI thread, so I created a new thread to do this, but nothing changed.
I found two things lead to this error.
The return type is jint, then a jint must be returned.
In Android Studio, Make Project sometimes doesn't rebuild the C++ code to APK, Rebuild Project is needed in this situation.
Related
I have Android app, where I need to use C++ code. But I realised, I have problem to call C++ function. I have read something about it and tried to write something, I thought it could work. Here is my code:
private native int test(int a);
private void process() {
int ret=test(5);
Toast.makeText(this, String.valueOf(ret), Toast.LENGTH_LONG).show();
}
And my C++ code is:
#include <jni.h>
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_woodem_woodem_1opencvgrains_Main_test(JNIEnv *env, jint a)
{
return a*a;
}
Of course, my real function is much more complicated and I need to pass about 6 arguments, but I hope, this can illustrate.
NOTE: Even this code doesn't work to me.
My application crash immediately after calling process(). Could you please advice me, what am I doing wrong? Compiler tells me nothing and I have no idea, where the problem is.
You are forgetting about the second argument. For example method void test() will have JNI signature JNIEXPORT void JNICALL Java..._test(JNIEnv *env, jobject thiz). In your case, edit signature to (JNIEnv *env. jobject thiz, jint a).
Edit
The previous version showed a signature for a static function. I've updated it to match instance function.
For static signature is (JNIEnv *env, jclass clazz).
You've declared your native function as private native int test(); passing an integer argument to it.
It should be private native int test(int num);
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.
When I call a native code for first time it returns proper result, but the second time it returns garbage value.
Here's the native code :
JNIEXPORT jstring JNICALL
Java_com_example_project_NativeCodes_method2(JNIEnv *env, jobject thisObj,
jstring st) {
const char* st1=env->GetStringUTFChars(st,0);
string str=st1;
const int len=str.length();
for(int i=0;i<len;i++){
if (str[i]>=97 && str[i]<=122)
str[i] = str[i]-32;
}
.....
env->ReleaseStringUTFChars(st, st1);
return env->NewStringUTF(str.c_str());
}
And in Java class I declare native method as follows
public class NativeCodes(){
static{
System.loadLibrary("abclib");
}
public synchronized native String method2(String s);
}
In MainActivity.java I call native method like:
String s1,s2,s3,s4;
s1=edttxt1.getText().toString();
s2=edttxt2.getText().toString();
NativeCodes nc=new NativeCodes();
s3=nc.method2(s1);
s4=nc.method2(s2);
While debugging I find s3 gets the proper result whereas s4 receives garbage value.
I think I'm releasing the pointer properly with env->ReleaseStringUTFChars(st, st1);
But if a Log() statement is inserted between two calls for the method, both calls return correct result. For example,
s3=nc.method2(s1);
Log.i("String","Value: "+s3);
s4=nc.method2(s2);
Log.i("String","Value: "+s4);
This gives expected result. But I don't want to insert unnecessary code as there are many such native method calls made.
where am I doing wrong? Any help is highly appreciated.
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.
I want to call the Java method from Jni code with the int and int[] arguments. for that i have Go-ogled and found the following sample .
Calling a java method from c++ in Android
And it worked fine with String parameter . But While trying with int i got issues .
Please help me .
JNI CODE:
jstring Java_com_calljavafromjni_MainActivity_getJniString( JNIEnv* env, jobject obj){
jstring jstr = (*env)->NewStringUTF(env, "RAJESH TEST from JNI ");
jint sss=1111;
jclass clazz = (*env)->FindClass(env, "com/calljavafromjni/MainActivity");
jmethodID messageMe = (*env)->GetMethodID(env, clazz, "messageMe", "(Ljava/lang/String;)Ljava/lang/Integer;");
jobject result = (*env)->CallObjectMethod(env, obj, messageMe, sss);
const char* str = (*env)->GetStringUTFChars(env,(jstring) result, NULL); // should be released but what a heck, it's a tutorial :)
printf("%s\n", str);
return (*env)->NewStringUTF(env, str);
}
Java code
public String messageMe(Integer text) {
System.out.println( "aaaaaaaaaaaaaaaa "+text);
return "test";
}
I don't see where int[] come into your problem, but with int it should be easy to solve.
You need to look at your GetMethodId() call, specifically the method signature argument (the last one). The JNI Specification provides a list of all its Type Signatures here. That should also help you when you eventually come to pass your int arrays too.
So we can see at the moment your signature is:
String messageMe(Integer text)
but you told JNI it was (Ljava/lang/String;)Ljava/lang/Integer; which translates to something like:
java.lang.Integer messageMe(String text)
The Type Signatures show us that the signature for an int is simply I so your argument for GetMethodId() should be something like this:
jmethodID messageMe = (*env)->GetMethodID(env, clazz, "messageMe", "(I)Ljava/lang/String;");
I hope that helps. As I said before, JNI isn't the easiest thing to get into but the answers really are all in the Specification, you just have to look quite hard.
EDIT: I corrected my signature.
Basically, you were almost there - you just got the arguments and return value the wrong way around in the signature. It should be (<arguments>)<return type>. You also made the easy mistake of specifying the class for Integer, instead of the primitive type.