JNI Method not found - android

I have been receiving this error for my JNI code while I tried find the method ,using GetMethodID, my Java method is in an Interface.
Here is my interface
public interface printReader
{
public printImg readerPrint(String selectedName) throws Exception;
}
Native code
WprintImgIMPL.h
class WprintImgIMPL: public IWprintReader {
public:
WprintImgIMPL(JNIEnv *env, jobject obj);
~WprintImgIMPL(void);
virtual WprintImg readerPrint(char* readerName) ;
.....
.....
private:
JNIEnv *m_Env;
jobject m_jObj;
}
WprintImgIMPL.cpp
WprintImg WprintImgIMPL::readerPrint(char* readerName) {
jclass cls = m_Env->GetObjectClass (m_jObj);
jmethodID mid = m_Env->GetMethodID (cls, "readerPrint", "(Ljava/lang/String;)Lcom/site/name/printImg;");
.......
.......
}
Java code
public class printReaderIMPL implements printReader {
static final String DEBUG_TAG = "";
android.net.wifi.WifiManager.MulticastLock lock;
Context _context;
public printReaderIMPL (Context context) {
_context = context;
}
#Override
public printImg readerPrint(String selectedName) throws Exception {
Log.e(DEBUG_TAG, "readerPrint");
}
}
Constructor/destructor
WprintImgIMPL(JNIEnv *env, jobject obj){
m_Env = env;
m_jobj = env->NewGlobalRef(obj);
}
~WprintImgIMPL(void) {
m_Env->DeleteGlobalRef(m_jobj);
}
Error: GetMethodID: method not found: Lcom/site/name/NativeCode;.printImg:(Ljava/lang/String;)Lcom/site/name/printImg;
Signature are checked twice , after failure I generated again using Javap tool .
Thank you if you can input /comment and help in fixing this bug.

It is invalid to save a JNIEnv* across JNI method calls. It's only valid for the duration of the JNI method you are currently in. Out of the blue, e.g. in arbitrary C++ code, you need to call AttachCurrentThread() to get a current valid JNIEnv*.
However you can cache the methodID. There no need to look it up every time. Look it up in your constructor.

Related

Implementing Android Event Handlers Using C++

I have a layout design in Java that I am currently porting over to C++ via JNI. I am practically done at this point, but I am currently puzzled on how I am supposed to set up event handlers like setOnClickListener for example. I have gone through the JNI specification and have not gotten much luck.
If anyone can port the following snippet to C++ or lead me in the right direction (more reasonable due to how much code the result would be), that would be greatly appreciated.
public void setOnClickListener(boolean modification, int index, int commandIndex, final TextView textView){
final int key = index;
final int command = commandIndex;
if(modification) {
textView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
changingMenu(key, command, textView);
Runnable r = new Runnable() {
#Override
public void run() {
resetMenu(key, command, textView);
}
};
Handler h = new Handler();
h.postDelayed(r, 250);
}
});
return;
}
menuTitle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
toggleMenu();
}
});
}
EDIT: Passing bad argument to setOnClickListener
Java
Object getProxy (MyInvocationHandler mih) {
ClassLoader classLoader = new ClassLoader() {
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return super.loadClass(name);
}
};
return java.lang.reflect.Proxy.newProxyInstance(classLoader, new Class[] { }, mih);
}
C++
jobject createProxyInstance(JNIEnv *env, jclass cls, CFunc cfunc) {
jclass cls_IH = env->FindClass("com/app/core/MyInvocationHandler");
jmethodID cst_IH = env->GetMethodID(cls_IH, "<init>", "(J)V");
jobject myIH = env->NewObject(cls_IH, cst_IH, (jlong)cfunc);
jclass klass = env->FindClass("com/app/core/Activity");
jmethodID method = env->GetMethodID(klass, "getProxy", "(Lcom/app/core/MyInvocationHandler;)Ljava/lang/Object;");
return env->CallObjectMethod(context, method, myIH); //Returning wrong object?
}
jobject aa (JNIEnv *env, jobject obj, jobject proxy, jobject method, jobjectArray args) {
__android_log_print(ANDROID_LOG_ERROR, "TEST", "SUCCESS");
}
void setListeners() {
jclass klass = env->FindClass("android/view/View");
jmethodID method = env->GetMethodID(klass, "setOnClickListener", "(Landroid/view/View$OnClickListener;)V");
klass = env->FindClass("android/view/View$OnClickListener");
env->CallVoidMethod(imageView, method, createProxyInstance(env, klass, &aa));
}
The syntax you show boils down to creating anonymous inner classes at compile time and inserting calls to create objects of these classes (with the correct variables in scope) in place of the new View.OnClickListener() { ... } expression.
I see the following two options:
For each different interface, you create a small Java class that implements the interface, with a native implementation of the interface's method(s). This is the most direct approach, but it does require you to keep the tens or hundreds of interface implementations straight.
You use java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) to dynamically create objects that implement the necessary interfaces. This will re-route each method invocation to your InvocationHandler implementation, which should be a Java class that has a native Object invoke(Object proxy, Method method, Object[] args) implementation.
To make all this reusable, you can implement this InvocationHandler to wrap a std::function object, so the final call to eg menuTitle.setOnClickListener might look like the following:
env->CallVoidMethod(menuTitle, menuTitle_setOnClickListener,
createProxyInstance(env, cls_View_OnClickListener, [](JNIEnv *env, jobject proxy, jobject method, jobjectArray args) {
...
});
For the latter solution, define the following Java class:
class MyInvocationHandler implements InvocationHandler {
private long cfunc;
MyInvocationHandler(long cfunc) { this.cfunc = cfunc; }
public native static Object invoke(Object proxy, Method method, Object[] args);
}
Which you implement on the C++ side as:
typedef jobject (*CFunc)(JNIEnv *env, jobject obj, jobject proxy, jobject method, jobjectArray args)
extern "C" jobject Java_MyInvocationHandler_invoke(JNIEnv *env, jobject obj, jobject proxy, jobject method, jobjectArray args) {
jclass cls_myIH = env->GetObjectClass(obj);
jfieldID fld_myIH_cfunc = env->GetFieldID(cls_myIH, "cfunc", "J");
CFunc cfunc = (CFunc)env->GetLongField(obj, fld_myIH_cfunc);
cfunc(env, proxy, method, args);
return nullptr;
}
Finally, we can implement createProxyInstance as follows:
jobject createProxyInstance(JNIEnv *env, jclass cls, CFunc cfunc) {
jclass cls_IH = env->GetClass("MyInvocationHandler");
jmethodID cst_IH = env->GetMethodID(cls_ID, "<init>", "(J)V");
jobject myIH = env->NewObject(cls_ID, cst_IH, (jlong)cfunc);
// now call Proxy.createProxyInstance with this object as InvocationHandler
}

Android JNI call Java method from native thread

I am trying to call a method in my java class from within a thread in the native code but not having any success. These are the global vars:
JavaVM* javaVM = NULL;
jclass activityClass;
jobject activityObj;
Code called on initialisation of the native code:
extern "C" {
JNIEXPORT jint JNICALL
naInit(JNIEnv *pEnv, jobject pObj, jstring pFileName, jstring, defaultStorageDirectory) {
pEnv->GetJavaVM(&javaVM);
jclass cls = pEnv->GetObjectClass(pObj);
activityClass = reinterpret_cast<jclass>((jclass) pEnv->NewGlobalRef(cls));
activityObj = pEnv->NewGlobalRef(pObj);
}
}
Code used within the thread function:
void *decodeAndRender(void * args) {
JNIEnv *env;
javaVM->AttachCurrentThread(&env, NULL);
jmethodID retryStartVideoMethodID = env->GetMethodID(activityClass, "retryStartVideo", "()V");
env->CallVoidMethod(activityObj, retryStartVideoMethodID);
javaVM->DetachCurrentThread();
return 0;
}
Java code :
public void retryStartVideo() {
Log.d(TAG, "METHOD CALLED FROM CPP ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
}
Error in logcat:
JNI DETECTED ERROR IN APPLICATION: JNI CallVoidMethodV called with pending exception java.lang.NoSuchMethodError: no non-static method "Ljava/lang/Class;.retryStartVideo()V"
I have set the instances of the calling class to be a global ref and used that when calling from within the thread function but it is still failing to find the method. I can use similar code to call a static method no problem but I need to be able to call a non static one.

Issue with instance and class variable states when using JNI

I'm currently writing some Android code that uses JNI and I'm having difficulty how class and instance variables work. If I execute the following code I would expect the code to print a value of "18" but I always receive a value of "0". Can someone explain what I'm doing wrong?
// Java code
SampleClass sc = new SampleClass(18);
sc.printId() // returns 18, as expected
sc.nativePrintId() // returns 0, why?!
// Java Class
public class SampleClass
{
private int mId = -1;
public FFmpegMediaPlayer(int id) {
mId = id;
}
public void printId() {
System.out.println("id: " + mId);
}
public native void nativePrintId();
}
// JNI C++ code
static void nativePrintId(JNIEnv* env, jobject thiz)
{
jclass clazz = env->FindClass("wseemann/media/SampleClass");
jmethodID printId = env->GetMethodID(clazz, "printId", "()V");
env->CallVoidMethod(clazz, printId); // always prints zero?
}
You must pass the object, not the class, to CallVoidMethod.
Use:
env->CallVoidMethod(thiz, printId);
Also, you should get the class from the object, not from FindClass.
Use:
jclass clazz = env->GetObjectClass(thiz);

Fill java class members with c structure members

I have a code something similar to this
struct time
{
long milliscnds;
int secs;
}
In my java file , I had something like this
class jtime
{
long millscnds;
int secs;
}
new jtime time = new jtime();
public int native getTimeFromC(object time);
in native class
getTimeFromc(JNIEnv* env, jobject thiz,jobject jtime)
{
struct time *mytime = getTime();
now to fill the jtime with mytime
}
Suggestions please?
You can simplify your Java class and the required JNI code.
Currently, your native method has some issues:
public int native getTimeFromC(object time);
Parameter is Object but should be jtime.
Return value doesn't seem to have a purpose.
Since the method completely initializes a jtime object, why not create and return a jtime object?
This class definition has a factory method to create the object and a constructor that moves some the initialization work over from the JNI side.
public class jtime {
long millscnds;
int secs;
public jtime(long millscnds, int secs) {
this.millscnds = millscnds;
this.secs = secs;
}
public native static jtime FromC();
}
The factory method can be implemented like this:
JNIEXPORT jobject JNICALL Java_jtime_FromC
(JNIEnv * env, jclass clazz)
{
struct time *mytime = getTime();
jmethodID ctor = (*env)->GetMethodID(env, clazz, "<init>", "(JI)V");
jobject obj = (*env)->NewObject(env, clazz, ctor, mytime->milliscnds, mytime->secs);
return obj;
}
Tip: The javap tool is like javah but shows the signatures of non-native methods. Using javap -s jtime, you can see the signature of the constructor.
Something like the following:
void getTimeFromc(JNIEnv* env, jobject thiz, jobject jtime)
{
struct time *mytime = getTime();
// now to fill the jtime with mytime
jclass jtimeClazz = (*env)->GetObjectClass(jtime); // Get the class for the jtime object
// get the field IDs for the two instance fields
jfieldID millscndsFieldId = (*env)->GetFieldID(jtimeClazz, "milliscnds", "J"); // 'J' is the JNI type signature for long
jfieldID secsFieldId = (*env)->GetFieldID(jtimeClazz, "secs", "I"); // 'I' is the JNI type signature for int
// set the fields
(*env)->SetLongField(jtime, millscndsFieldId, (jlong)mytime.milliscnds);
(*env)->SetIntField(jtime, secsFieldId, (jint)mytime.secs);
}
Ideally you should cache the values of millscndsFieldId and secsFieldId as they won't change during execution (and you could also cache jtimeClazz if you NewGlobalRef it).
All JNI functions are documented here: http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html

Android - JNI NewObject() does not save values when passing it to Java

I am using native code to create an object which I then pass as an argument to a method I call.
Nothing appears to go wrong in native code, but when I get the call in Java, the object seems to have nulled values.
Here's the code of the java object I create in native:
package org.test;
import java.util.ArrayList;
public class JNITeam {
public int mTeamID;
public String mTeamName;
private ArrayList<String> mMembers;
public JNITeam(int id, String name) {
mTeamID = id;
mTeamName = name;
}
public void addMember(String name) {
mMembers.add(name);
}
}
Here's the native code used to create an instance of the class and pass it up to the Java method "onGetTeam", which takes an instance of the above class as a parameter. It is run from a thread created in Native code, hence I have to attach the thread.
JNIEnv* jenv = 0;
clientHandle->runningJVM->AttachCurrentThread(&jenv,0);
if (!jenv)
__android_log_print(ANDROID_LOG_INFO, ANDROID_DEBUG_TAG, "jenv is null");
jclass cls = jenv->GetObjectClass(clientHandle->job);
if (!cls)
__android_log_print(ANDROID_LOG_INFO, ANDROID_DEBUG_TAG, "cls is null");
jmethodID constructor = jenv->GetMethodID(clientHandle->JNITeamCls, "<init>", "(ILjava/lang/String;)V");
jint teamID = 2;
jstring js = jenv->NewStringUTF("test");
jobject dataObject = jenv->NewObject(clientHandle->JNITeamCls, constructor, teamID, js);
if (!dataObject)
__android_log_print(ANDROID_LOG_INFO, ANDROID_DEBUG_TAG, "dataobject is null");
if (jenv && cls && dataObject) {
jmethodID mid = jenv->GetMethodID(cls,"onGetTeam","(Lorg/test/JNITeam;)V");
if (mid) {
jenv->CallVoidMethod(clientHandle->job,mid);
}
else {
__android_log_print(ANDROID_LOG_INFO, ANDROID_DEBUG_TAG, "mid is null");
}
}
I do not want the object to be persistent; I want it to ony be valid during the call to Java, then it can be garbage-collected. However its data fields - that are set in the constructor - are just null when I call it, Why?
You're not passing the object you constructed to the method (or indeed doing anything with it).
Instead of
jenv->CallVoidMethod(clientHandle->job,mid);
don't you want
jenv->CallVoidMethod(clientHandle->job,mid,dataObject);
?
Also you're not checking whether that call succeeded or not.

Categories

Resources