In my function, i am trying to get the size of a String array that has been passed to a C++ class through JNI, but i keep getting the error: "jobjectarray has not been declared". here is my C++:
int targetCount = 0;
JNIEXPORT void JNICALL
Java_com_example_processArray(JNIEnv *env, jobject obj, jobjectarray targetsArray){
targetCount = env->GetArrayLenght(env, targetsArray);
}
Not sure how to go about this because i thought it uses the Array already passed directly. Solutions greatly appreciated as always. Thanks
Use javah to create your jni signatures. jobjectarray is not correct, the correct syntax is jobjectArray
See Documentation
Related
I am looking at the MoreTeapots sample:
https://github.com/googlesamples/android-ndk/tree/master/MoreTeapots
and I see that is easy to call a Java method from the C++ code using android_native_app_glue.c by using this method:
jclass clazz = jni->GetObjectClass(app_->activity->clazz);
jmethodID methodID = jni->GetMethodID(clazz, "updateCamera", "(FFF)V");
jni->CallVoidMethod(app_->activity->clazz, methodID, x, y, z);
yet, can I do the reverse in that sample?
Declare a function into java code and that calls the native code. Notice that sample uses "glue"; therefore, the answer is not just these steps:
https://developer.android.com/ndk/samples/sample_hellojni.html
it is different. I see no hooks from java to glue, only from glue to java. I believe glue is running in its own thread.
any leads?
thx!
you could declare a callback in your native code
JNIEXPORT void JNICALL Java_package_Activity_nativeCallback(JNIEnv* jenv, jobject obj);
And then from Java code, just declared as native
native void nativeCallback();
Remember that this execute in main thread, and you should send a command to the background thread to process.
Best regards.
That does not work with "glue". If you look at the sample I mentioned it is different. It uses glue. So I added to the java code:
public native void nativeCallback();
then to native:
extern "C" {
JNIEXPORT void JNICALL Java_com_sample_moreteapots_MoreTeapotsNativeActivity_nativeCallback(JNIEnv *env, jobject obj);
};
JNIEXPORT void JNICALL Java_com_sample_moreteapots_MoreTeapotsNativeActivity_nativeCallback(JNIEnv *env, jobject obj) {
int i = 0;
return;
}
it compiled and linked. But when I tried to invoke the method from Java I get this:
FATAL EXCEPTION: main
Process: com.sample.moreteapots, PID: 31733
java.lang.UnsatisfiedLinkError: No implementation found for void com.sample.moreteapots.MoreTeapotsNativeActivity.nativeCallback()
(tried Java_com_sample_moreteapots_MoreTeapotsNativeActivity_nativeCallback
and Java_com_sample_moreteapots_MoreTeapotsNativeActivity_nativeCallback__)
I'm kinda' new to JNI, but been reading alot about JNI when I wanted to use a legacy dll in my project.
Quick enough, I found out that I can't parameters to native methods. Here's an example that I tried to code with no success:
Hello.java:
package HelloWorld;
Public class Hello {
Private static native int HelloWorld();
Private static native int Mirror(int a);
Static {
System.loadLibrary("Example"); //got path in
vm arguments - works.
}
Public static void main(String[] args) {
Int a = 8;
System.out.println(Mirror(a));
}
Used javah to create header which got me this signature:
JNIEXPORT jint JNICALL Java_HelloWorld_Hello_Mirror (JNIEnv *, jclass, jint);
Wrote a cpp:
Same signature as the h with impl of: "return 1";
That's it and... This is the error I get from eclipse:
Exception in thread "main" java.lang.UnsatisfiedLinkError: HelloWorld/Hello.Mirror(I)I
at HelloWorld.Hello.main (Hello.java:14)
These are my ideas:
A. Saw something about c and cpp, am I mixing things.
B. jdk or stuff?
C. The library loads perfectly (checked the other function that does not get parameters), maybe the types are incompatible?
Stuck on this for a while, Hope you guys can help me!
Solved!
Just needed to add '{' around both functions in the cpp file, after the "extern c" command...
I have created my first native call in Java with the Android SDK today.
I found a few examples but there aren't consistent with the function head.
I used always
JNIEXPORT void JNICALL Java_com_test_Calculator_calcFileSha1
(JNIEnv *, jclass, jstring);
but I have seen
JNIEXPORT void JNICALL Java_com_test_Calculator_calcFileSha1
(JNIEnv *, jobject, jstring);
Belonging to the heads are different was to get the class of the caller.
But what is the preferred way?
From the C++ code I want to call a java method. I found the JNI documentation (Calling Instance Methods).
But I don't know what the first parameter (object) should be.
I tried to give the class instance which I get from the call of the native method, which fails with an AbstractMethodError.
Fixed source code:
public class TestCalc extends Activity {
static {
System.loadLibrary("Test");
}
private void setFilesize(long size) {
}
}
Native Library:
// header
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_com_test_TestCalc_calcFilesize
(JNIEnv *, jobject, jstring);
void setFilesize(const INT_64 size);
#ifdef __cplusplus
}
#endif
#endif
// code
JNIEnv * callEnv;
jobject callObj;
JNIEXPORT void JNICALL Java_com_test_SHA1Calc_calcFileSha1
(JNIEnv * env, jobject jobj, jstring file)
{
callEnv = env;
callObj = jobj;
[...]
}
void setFilesize(const INT_64 size) {
jmethodID mid;
jclass cls;
cls=callEnv->FindClass("com/test/TestCalc");
mid=callEnv->GetMethodID(cls, "setFilesize", "(J)V");
if (mid == 0) {
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:LC: [%s]", "Cannot find method setFilesize");
return;
}
callEnv->ExceptionClear();
callEnv->CallVoidMethod(callObj, mid, size);
if(callEnv->ExceptionOccurred()) {
callEnv->ExceptionDescribe();
callEnv->ExceptionClear();
}
}
Thanks for any advices.
Call[type]Method is only for private methods and constructors. (Calling Instance Methods) When you call a public method, you will get an AbstractMethodError.
jobject could be any java object while jclass is supposed to be an object representing java.lang.Class. In C API, jobject and jclass is the same thing while C++ API is trying to enforce type safety and declares those as different types where jclass inherits jobject. The second argument is supposed to represent a class, so jclass is preferred. Even if in C it doesn't matter, it can hint the developer or even save you time if you ever decide to switch to C++. Read more about JNI types here.
I have a working implementation of NDK library and corresponding Java-class. But I am not able to add overloaded method to that class. Currently my class contains:
package com.package;
public class MyClass
{
public static native String getFileName();
static
{
System.loadLibrary("mylib");
}
}
My jniwrappers.cpp file has the following declaration:
JNIEXPORT jstring JNICALL
Java_com_package_MyClass_getFileName(_JNIEnv* env, jobject thiz);
Up to this point everything is working fine. But next I modify my class:
package com.package;
public class MyClass
{
public static native String getFileName();
public static native String getFileName(int index);
...
}
And add to jniwrappers.cpp another declaration:
JNIEXPORT jstring JNICALL
Java_com_package_MyClass_getFileName__I(_JNIEnv* env, jobject thiz, jint index);
It compiles fine, Android application starts, does not get UnsatisfiedLinkError but when it calls the second method with the argument the first C++ function is being called but not the second. I have other methods with arguments in that class but none of them are overloaded so their respective JNI signatures do not contain arguments.
So, what am I doing wrong?
You should use javah tool to generate those signatures.
To use it, build the class file where you have your native function. You'll get a class file.
Run javah -jni com.organisation.class_with_native_func, it'll generate a header file for you.
It's far cleaner than editing it yourself.
You have to add a __ onto the end of the original getFileName function now that it is overloaded. Your 2 C function prototypes should now look like this:
JNIEXPORT jstring JNICALL Java_com_package_MyClass_getFileName__
(JNIEnv *, jclass);
JNIEXPORT jstring JNICALL Java_com_package_MyClass_getFileName__I
(JNIEnv *, jclass, jint);
So i have been looking into JNI calls so i can interact with some pre written C++ programs, i dont know any C++ but am trying to learn some basics. I have just been trying to do a simple call to a method outside my JNI method but always get the following error:
error c3861 'myMethod': identifier not found
#include <stdio.h>
#include <string.h>
#include "StringFuncs.h"
JNIEXPORT jstring JNICALL Java_StringFuncs_changeWord(JNIEnv *env, jobject obj, jstring inStr, jint inLen)
{
const char *inString;
inString = env->GetStringUTFChars(inStr, NULL);
char otherString[40];
strcpy_s(otherString,inString);
if(myMethod())
{
memset(otherString, '-', inLen);
}
jstring newString = env->NewStringUTF((const char*)otherString);
return newString;
}
bool myMethod()
{
return true;
}
int main()
{
return 0;
}
Any words of wisdome?
You have to declare your methods before you call them. So in your header type
bool myMethod();
Or you can move the code above your _changeWord function, then the declaration/definition is in one.
Move myMethod() above Java_StringFuncs_changeWord() in the source file.
In C++ you generally have to declare a symbol before you use it. So, somewhere before Java_StringFuncs_changeWord you need to declare myMethod:
bool myMethod();
If this is going to be a shared function (other cpp modules will call it) then you most likely want to put it in a header file which can be #included by other files. If the function only makes sense to be called by this module, you want to put the declaration at the top of the file, after the other #includes.
You can also declare and define the function in one go by moving the whole function above the function that calls it, but this wont always work (if you have two functions which reference eachother you have to have a separate declaration).