Why "java.lang.NoSuchMethodError: no static method" from (JNIenv*)->GetStaticMethodID? - c++

My Android NDK project's Java Code calls C++ function ,
extern "C" JNIEXPORT jstring JNICALL Java_com_crimson_tub_MainActivity_stringFromJNI(JNIEnv *env, jobject jo)
I want to call
public static void requestPermissions(#NonNull final Activity activity, #NonNull final String[] permissions, #IntRange(from = 0L) final int requestCode)
found in Android Document:
https://developer.android.com/reference/android/support/v4/app/ActivityCompat.html#requestPermissions(android.app.Activity, java.lang.String[], int)
I have tried few variations of the code where i think the problem is,
jmethodID jmid = env->GetStaticMethodID(clzz,"requestPermissions", "(Landroid/app/Activity;[Ljava/lang/String;I)V;"),
//jmethodID jmid = env->GetStaticMethodID(clzz,"requestPermissions", "(Lcom/company/project/MainActivity;[Ljava/lang/String;I)V;"),
//jmethodID jmid = env->GetStaticMethodID(clzz,"requestPermissions", "(Ljava/lang/Object;[Ljava/lang/String;I)V;"),
all of the above alternatives lead to same exception,
No pending exception expected: java.lang.NoSuchMethodError: no static method "Landroid/support/v4/app/ActivityCompat;.requestPermissions(Landroid/app/Activity;[Ljava/lang/String;I)V;"
c++ code,
extern "C" JNIEXPORT jstring JNICALL
Java_com_crimson_tub_MainActivity_stringFromJNI(JNIEnv *env,jobject jo) {
std::string hello;
jobjectArray ret;
int i;
char *data[2]= { "android.permission.CAMERA" ,
"android.permission-group.CALENDAR"};
ret= (jobjectArray)env->NewObjectArray(2,env->FindClass("java/lang/String"),env->NewStringUTF(""));
for(i=0;i<2;i++) env->SetObjectArrayElement(ret,i,env->NewStringUTF(data[i]));
jint result = 0xffffffff;
jclass clzz = env->FindClass("android/support/v4/app/ActivityCompat");
if (!clzz){
hello += "-class";
}else{
hello += "+class";
}
jmethodID jmid = env->GetStaticMethodID(clzz,"requestPermissions", "(Landroid/app/Activity;[Ljava/lang/String;I)V;"),
//jmethodID jmid = env->GetStaticMethodID(clzz,"requestPermissions", "(Lcom/company/project/MainActivity;[Ljava/lang/String;I)V;"),
//jmethodID jmid = env->GetStaticMethodID(clzz,"requestPermissions", "(Ljava/lang/Object;[Ljava/lang/String;I)V;"),
env->CallStaticVoidMethod(clzz,jmid,jo,ret,result);
env->DeleteGlobalRef(jo);
return env->NewStringUTF(hello.c_str());
}
i would like to be able to call Android Java functions via JNI like,
ActivityCompat.requestPermissions
directly from c++ so later i can remove Java code from my project.
I have been working on this issue for quite a while now and still unsuccessful.
Gratitude towards All the help and it is greatly appreciated.
Thank you all.

Problem was the ; after the V in the signature. now it works. Lol.

Related

jni lags minecraft on garbage collect

i’m making a dll in c++ that reads fields via jni. after a while whenever the game garbage collects it stutters.
the memory view in visual studio doesn’t show any change in memory usage so i doubt it’s a memory leak.
this is where i assume it’s happening but i cannot see why / how it happens.
how can i figure out what’s causing the garbage collector to take so long?
jobject get_class_loader() {
jclass thread_clazz = env->FindClass("java/lang/Thread");
jmethodID curthread_mid = env->GetStaticMethodID(thread_clazz, "currentThread", "()Ljava/lang/Thread;");
jobject thread = env->CallStaticObjectMethod(thread_clazz, curthread_mid);
jmethodID threadgroup_mid = env->GetMethodID(thread_clazz, "getThreadGroup", "()Ljava/lang/ThreadGroup;");
jclass threadgroup_clazz = env->FindClass("java/lang/ThreadGroup");
jobject threadgroup_obj = env->CallObjectMethod(thread, threadgroup_mid);
jmethodID groupactivecount_mid = env->GetMethodID(threadgroup_clazz, "activeCount", "()I");
jfieldID count_fid = env->GetFieldID(threadgroup_clazz, "nthreads", "I");
jint activeCount = env->GetIntField(threadgroup_obj, count_fid);
jobjectArray arrayD = env->NewObjectArray(activeCount, thread_clazz, NULL);
jmethodID enumerate_mid = env->GetMethodID(threadgroup_clazz, "enumerate", "([Ljava/lang/Thread;)I");
env->CallIntMethod(threadgroup_obj, enumerate_mid, arrayD);
jobject array_elements = env->GetObjectArrayElement(arrayD, 0);
jmethodID threadclassloader = env->GetMethodID(thread_clazz, "getContextClassLoader", "()Ljava/lang/ClassLoader;");
if (threadclassloader != NULL) {
jobject classloader_obj = env->CallObjectMethod(array_elements, threadclassloader);
env->DeleteLocalRef(array_elements);
env->DeleteLocalRef(thread_clazz);
env->DeleteLocalRef(thread);
env->DeleteLocalRef(threadgroup_clazz);
env->DeleteLocalRef(threadgroup_obj);
env->DeleteLocalRef(arrayD);
return classloader_obj;
}
else {
env->DeleteLocalRef(array_elements);
env->DeleteLocalRef(thread_clazz);
env->DeleteLocalRef(thread);
env->DeleteLocalRef(threadgroup_clazz);
env->DeleteLocalRef(threadgroup_obj);
env->DeleteLocalRef(arrayD);
return 0;
}
}
jclass get_object(const char* class_name) {
jclass clazz = env->FindClass("java/lang/Class");
jmethodID class_for_name_method = env->GetStaticMethodID(clazz, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
if (class_for_name_method == NULL) {
env->FatalError("Class.forName(String,boolean,java.lang.ClassLoader)java.lang.Class method not found\n");
}
jstring class_to_load = env->NewStringUTF(class_name);
auto class_loader = get_class_loader();
jclass jar_class = (jclass)env->CallStaticObjectMethod(clazz, class_for_name_method, class_to_load, true, class_loader);
env->ReleaseStringUTFChars(class_to_load, NULL);
env->DeleteLocalRef(class_to_load);
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(class_loader);
return jar_class;
}
instance = std::make_unique<c_minecraft>();
auto minecraft_inst = instance->get_minecraft();
if (!minecraft_inst) {
env->DeleteLocalRef(minecraft_inst);
return;
} // do field suff after here. once done that delete local reference of minecraft_inst

How to Pass and return Array of properties in jni (java to c++) as well as (c++ to java)

How to Pass and return Array of properties in jni (java to c++) as well as (c++ to java)
import java.util.*;
public class Test {
public native static Properties[] getStudentDetails();
public static void main(String[] args) {
System.loadLibrary("Sample");
int a= 10;
Properties[] records = getStudentDetails();
for(Properties record:records){
System.out.print("\ntype:"+record.getProperty("type"));
System.out.print("\ttime:"+record.getProperty("time"));
System.out.print("\tsource:"+record.getProperty("source"));
System.out.print("\teid:"+record.getProperty("eid"));
System.out.println("");
}
}
}
it give me error
Assuming you have access to a std::vector<SearchRecord*> searchRecordResult somewhere:
extern "C"
JNIEXPORT jobjectArray JNICALL Java_Test_getStudentDetails(JNIEnv *env, jclass cls) {
// Get Properties class, its constructor and the put method
jclass cls_Properties = env->FindClass("java/util/Properties");
jmethodID mid_Properties_ctor = env->GetMethodID(cls_Properties, "<init>", "()V");
jmethodID mid_Properties_put = env->GetMethodID(cls_Properties, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
// Construct the key Strings up front
jstring key_type = env->NewStringUTF("type");
jstring key_time = env->NewStringUTF("time");
jstring key_source = env->NewStringUTF("source");
jstring key_eid = env->NewStringUTF("eid");
jobjectArray ret = env->NewObjectArray(searchRecordResult.size(), cls_Properties, 0);
for (int i = 0; i < searchRecordResult.size(); i++) {
auto result = searchRecordResult[i];
// Allocate and fill a Properties object, making sure to clean up the value Strings.
env->PushLocalFrame(5);
jobject prop = env->NewObject(cls_Properties, mid_Properties_ctor);
env->CallObjectMethod(prop, mid_Properties_put, key_type, env->NewStringUTF(result->type));
env->CallObjectMethod(prop, mid_Properties_put, key_time, env->NewStringUTF(result->time));
env->CallObjectMethod(prop, mid_Properties_put, key_source, env->NewStringUTF(result->source));
env->CallObjectMethod(prop, mid_Properties_put, key_eid, env->NewStringUTF(result->eid));
prop = env->PopLocalFrame(prop);
env->SetObjectArrayElement(ret, i, prop);
}
return ret;
}
The core loop just constructs Properties objects, fills their properties with put and assigns them to the right slot on the ret array.

JNI date value conversion issue, getting a different long value in C++

I'm badly stuck in one basic date conversion in jni as the (long) values passed from Java to JNI/C++ are quite different.
My Java date objects are initialized in class as,
Date date1=new Date(1220227200L * 1000);
// If I convert into milliseconds such as
long timeInMillisecond=date1.getTime();
System.out.println (timeInMillisecond )
// it obviously displays 1220227200000
In my native function, i'm retrieving as
jfieldID fid = env->GetFieldID(cls, "date1", "Ljava/util/Date");
// case 1 - value 1
jlong dobject = (jlong) env->GetObjectField(object ,fid);
cout <<dobject; //displays 139757766370904
// case 2 - value 2
long dobject2 = (long) env->GetObjectField(object, fid);
cout <<dobject2; // displays 140031771862616
// case 3 - value 3
long long dobject3 = (long long) env->GetObjectField(object ,fid);
cout <<dobject3; //displays 140456034100824
I wonder what would be the best way to get correct value here from Java in C++. I'm using C++ 11. Some one please help me here.
Update
Sorry guys, i think I misspelled a few variables, so it wasn't dobject2,3 in GetObjectfield (correcting that)and infact rewriting the code here to make it more clear.
//Data.java
class Data {
public long sDate;
public Date schedDate;
....
}
// Test.java
// Native function
public native static void sendEvent(Data EventRec);
Data Rec= new DataRec();
Rec.sDate= 1400754723399L;
Rec.schedDate = new Date(1220227200L * 1000);
sendEvent(Rec);
//C++
JNIEXPORT void JNICALL Java_Test_sendEvent (JNIEnv *env, jclass cls, jobject jobj) {
cls = env->FindClass("Data");
if (cls !=NULL) {
jmethodID ctorID = env->GetMethodID(cls, "<init>","()V");
if (ctorID != NULL) {
jfieldID fidLong = env->GetFieldID(cls, "sDate", "J");
long dObj = (long) env->GetLongField(jobj,fidLong);
cout << "C++ .. Event Date (LONG): " << dObj <<endl; // This is correct
jfieldID fidDate = env->GetFieldID(cls, "schedDate", "Ljava/util/Date");
// Here is the problem area, that I tried to mimic above...
jobject dobject= (env->GetObjectField(jobj,fidDate));
long dobj = env->GetLongField(dobject,fidDate);
// OR
// long dObj2 = (long) env->GetLongField(jobj,fidDate);
// .....
cout << "C++ .. Date (DATE)..." << dobj;
}
}
}
Your JNI code is not accessing the Java objects correctly.
In your Java_Test_sendEvent() implementation, the jclass parameter points to the class type that your public native static void sendEvent(Data EventRec); is declared in (which you did not show), and the jobject parameter points to the Data Rec object that your Java code is passing in to sendEvent().
Also, in your call to env->GetFieldID() for the Date field, you are missing a required semicolon at the end of the type signature string.
Try this instead:
JNIEXPORT void JNICALL Java_Test_sendEvent (JNIEnv *env, jclass cls, jobject EventRec)
{
jclass cls_EventRec = env->GetObjectClass(EventRec);
jfieldID fid_sDate = env->GetFieldID(cls_EventRec, "sDate", "J");
if (!fid_sDate) {
// error handling...
return;
}
jfieldID fid_schedDate = env->GetFieldID(cls_EventRec, "schedDate", "Ljava/util/Date;");
if (!fid_schedDate) {
// error handling...
return;
}
jlong sDate = env->GetLongField(EventRec, fid_sDate);
cout << "C++ .. Event Date (LONG): " << sDate << endl;
jobject schedDate = env->GetObjectField(EventRec, fid_schedDate);
if (schedDate) {
jclass cls_schedDate = env->GetObjectClass(schedDate);
jmethodID mid_getTime = env->GetMethodID(cls_schedDate, "getTime", "()J");
jlong timeInMillisecond = env->CallLongMethod(schedDate, mid_getTime);
cout << "C++ .. Date (DATE)..." << timeInMillisecond << endl;
}
}
You're overlooking something very important:
jfieldID fid = env->GetFieldID(cls, "date1", "Ljava/util/Date;");
jobject dobject = env->GetObjectField(dobject, fid); // Note: "Get Object Field"
This returns a java.util.Date object. You need to call getTime on it as well:
jmethodID mid = env->GetMethodID(cls, "getTime", "()J");
jlong timeInMillisecond = env->CallLongMethod(dobject, mid);

How to covert UTF8 string to UTF16 in JNI

Can anyone please tell me that what is going on wrong with me in this code.Actually in following line of codes I am taking the path of sdcard in a string in jni (C code) and in concatenate function concatenating these manually using loop.The string returned by concatenate works fine but when I am converting it to jstring it prints garbage value in my logcat.
Kindly tell me what is the problem.
jstring str=(jstring)env->CallObjectMethod(sdcard,storagestring);
const char jclass cfile=env->FindClass("java/io/File");
jmethodID fileid=env->GetMethodID(cfile,"<init>","(Ljava/lang/String;)V");
jclass envir=env->FindClass("android/os/Environment");
jmethodID storageid=env->GetStaticMethodID(envir,"getExternalStorageDirectory","()Ljava/io/File;");
jobject sdcard=env->CallStaticObjectMethod(envir,storageid);
jclass sdc=env->GetObjectClass(sdcard);
jmethodID storagestring=env->GetMethodID(sdc,"toString","()Ljava/lang/String;");
*nativeString = env->GetStringUTFChars(str, 0);
char *s =concatenate(nativeString,"/f1.3gp");
//fpath=s;
fpath=env->NewStringUTF(s);
jobject fobject=env->NewObject(cfile,fileid,fpath);
LOGI("size of char=%d size of string=%d",sizeof("/f1.3gp"),sizeof(fpath));
jmethodID existid=env->GetMethodID(cfile,"exists","()Z");
if(env->CallBooleanMethod(fobject,existid))
{
jmethodID delid=env->GetMethodID(cfile,"delete","()Z");
if(env->CallBooleanMethod(fobject,delid))
LOGE("File is deleting...%s",env->NewStringUTF("/f1.3gp"));
}
jmethodID newfileid=env->GetMethodID(cfile,"createNewFile","()Z");
if(env->CallBooleanMethod(fobject,newfileid))
LOGE("dig dig %s",fpath);
jthrowable exc=env->ExceptionOccurred();
if(exc)
{
env->ExceptionDescribe();
env->ExceptionClear();
}
LOGE("creating file %s",fpath);
}

FindClass crashes in Android NDK

I have a purely native Android app, where the entry point is:
void android_main(struct android_app* state)
{
}
I need to access device information such as, android.os.Build.MODEL and in my android_main, have the following:
JNIEnv *env = state->activity->env;
jclass build_class = env->FindClass("android/os/Build");
jfieldID brand_id = env->GetStaticFieldID(build_class, "MODEL", "Ljava/lang/String;");
jstring brand_obj = (jstring)env->GetStaticObjectField(build_class, brand_id);
const char *nativeString = env->GetStringUTFChars(brand_obj, 0);
Unfortunately it crashes at the second line - "FindClass". I know that env is not null because I checked for this, but otherwise I am unable to debug this and find out why it is crashing :(
Turns out I had to:
state->activity->vm->AttachCurrentThread(&env, NULL);
instead of
JNIEnv *env = state->activity->env;
This post was helpful:
http://blog.tewdew.com/post/6852907694/using-jni-from-a-native-activity
Alternatively , you can do this in native code :-
#include <sys/system_properties.h>
#define ANDROID_OS_BUILD_MODEL "ro.product.model"
char model_id[PROP_VALUE_MAX]; // PROP_VALUE_MAX from <sys/system_properties.h>.
int len;
len = __system_property_get(ANDROID_OS_BUILD_MODEL, model_id);
// On return, len will equal (int)strlen(model_id).
model_id will contain the req info
Refer this for all constants