I need to access a boolean or int field in activity from jni which is not static, but i get this JNI Warning and throws exception
JNI WARNING: instance fieldID 0x46488338 not valid for class Ljava/lang/Class;
in Lcom/example/filehandler/FileHandlerActivity;.beginFileOperation:(Ljava/lang/String;)I (GetIntField)
I am able to access a static field, why it's not able to access an instance field?
Here is the JNI code i used to access int field in Activity:
jint Java_com_example_filehandler_FileHandlerActivity_beginFileOperation(JNIEnv *env,jobject obj,jstring path)
{
target=(*env)->GetStringUTFChars(env,path,0);
jclass cls=(*env)->GetObjectClass(env,obj);
//jclass cls=(*env)->FindClass(env,"com/example/filehandler/FileHandlerActivity");
if(cls==NULL)
{
LOG_INFO("jni : class not found");
return 0;
}
jfieldID fid=(*env)->GetFieldID(env,cls,"status","I");
if(fid==NULL)
{
LOG_INFO("jni : field not found");
return 0;
}
else
{
LOG_INFO("jni : field found");
}
sdcard_status=(*env)->GetIntField(env,obj,fid);
LOG_INFO("jni : sdcard status = %d",sdcard_status);
//writeToFile(target);
(*env)->ReleaseStringUTFChars(env,path,target);
return 1;
}
Related
I have a class named FJpegStreamReader, that has loaded a jni (.so):
System.loadLibrary("fjpeg");
and has a singleton constructor:
private static volatile FJpegStreamReader mInstances;
public static FJpegStreamReader getInstance() {
if (mInstances == null) {
synchronized (FJpegStreamReader.class) {
if (mInstances == null) {
mInstances = new FJpegStreamReader();
}
}
}
return mInstances;
}
when i invoke it with singleton, it work well,
FJpegStreamReader.getInstance().open("/sdcard/markers.jpg", FJpeg.MODEL_OPEN);
But now, i dont want to use singleton mode, I try to change FJpegStreamReader , and invoke like this :
FJpegStreamReader readerA = new FJpegStreamReader();
FJpegStreamReader readerB = new FJpegStreamReader();
readerA.open("/sdcard/markers1.jpg", FJpeg.MODEL_OPEN);
readerB.open("/sdcard/markers2.jpg", FJpeg.MODEL_OPEN);
Bitmap bmA = readerA.getBackgroundImage();
Bitmap bmB = readerB.getBackgroundImage();
and readerA work well, but readerB goes error. i want it can be instanced more than one time in the same time, how to make that readerA and readerB all work?
25884-25884 I: JNI_OnLoad Called
25884-25884 I: FilePath: /sdcard/markers1.jpg, and OpenMode: 0
25884-25884 I: offset is:15079
25884-25884 I: Start to Parser Data
25884-25884 I: Open Succeed
25884-25884 I: FilePath: /sdcard/markers2.jpg, and OpenMode: 0
25884-25884 E: Fired to Open File(code: -1001)
This is my c++ method:
unique_ptr<FJpeg> f(new FJpeg);
static jboolean
FJpeg_open(JNIEnv *env, jobject thiz, jstring fileName, jint model) {
const char *c_path = NULL;
c_path = env->GetStringUTFChars(fileName, NULL);
LOG_I("FilePath: %s, and OpenMode: %d", c_path, model);
int rel = f->openFile(c_path, model);
if (rel < 0) {
LOG_E("Fired to Open File(code: %d)", rel);
return false;
}
LOG_I("Open Succeed");
return true;
}
...
int FJpeg::openFile() {
if (file_path == NULL || *file_path == '\0' || (open_mode != 0 && open_mode != 1)) return ERROR_CODE_UNSUPPORTED_OPERAND;
if (open_mode == 0) {
f_in.open(file_path, ios::in | ios::binary);
if (f_in.fail()) return ERROR_CODE_FILE_NOT_FOUND;
return checkOffsetAndEnd();
} else {
f_out.open(file_path, ios::out | ios::binary);
if (f_out.fail()) return ERROR_CODE_FILE_NOT_FOUND;
return 0;
}
}
It looks like all your Java FJpegStreamReader objects share a single c++ FJpeg object.
You will need to change that to associate each Java object with a unique FJpeg object, for example bystoring either the raw address in a long field in the Java class, or a handle to it and converting both back to a pointer in your Jni methods.
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);
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.
So I'm developing a small project with Cocos2Dx but I'm trying to add Bluetooth functionality, and that implies calling a non-static method to be able to access the Main Activity's association to the Android API. Almost everything that I've seen tells me to follow this procedure:
- Create an instance of the main activity (environment->NewGlobalRef is the one I'm using)
- Get method from activity and execute it (environment->GetObjectClass)
And here's the code. In java we have the following (omitting logical stuff like onCreate, onResume, etc):
public class TSP extends Cocos2dxActivity{
public void CnxAttempt(){
Log.e("TSP_BT","aTTEMPTING!");
}
}
That's it! Just for now, I only want to show a Log message, confirming that the function is executed. Now, the fun part is at C++:
static JNIEnv* getJNIEnv(void){
JNIEnv *env = 0;
// get jni environment
if (gJavaVM->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK){
CCLog("Failed to get the environment using GetEnv()");
}
if (gJavaVM->AttachCurrentThread(&env, 0) < 0){
CCLog("Failed to get the environment using AttachCurrentThread()");
}
return env;
}
typedef struct JniMethodInfo_{
JNIEnv * env; // The environment
jclass classID; // classID
jmethodID methodID; // methodID
} JniMethodInfo; // Struct that stores most of the important information to relate to Java code
static bool getMethodInfo(JniMethodInfo &methodinfo, const char *methodName, const char *paramCode){
jmethodID methodID = 0;
JNIEnv *pEnv = 0;
jobject methodObject = NULL;
bool bRet = false;
do {
pEnv = getJNIEnv();
if (! pEnv){
CCLog("getMethodInfo -- pEnv false");
break;
}
jclass localRef = pEnv->FindClass("org/cocos2dx/tsp/TSP");
if (localRef == NULL) {
CCLog("getMethodInfo -- localRefCls false");
break; // exception thrown
}
gCallbackObject = pEnv->NewGlobalRef(localRef);
if (gCallbackObject == NULL){
CCLog("getMethodInfo -- CallbackOBJ false");
break;
}
jclass classID = pEnv->GetObjectClass(methodObject);
if (!classID){
CCLog("getMethodInfo -- classID false");
break;
}
methodID = pEnv->GetMethodID(classID, methodName, paramCode);
if (!methodID){
CCLog("getMethodInfo -- methodID false");
break;
}
methodinfo.classID = classID;
methodinfo.env = pEnv;
methodinfo.methodID = methodID;
CCLog("getMethodInfo -- methodinfo created");
bRet = true;
} while(0);
return bRet;
}
void CnxAttempt(){
JniMethodInfo methodInfo; // Creating a JniMethodInfo object to store all the data
if (! getMethodInfo(methodInfo, "CnxAttempt", "()V")){
CCLog("getMethodInfo is FALSE :(");
return;
}
methodInfo.env->CallVoidMethod(methodObject,methodInfo.methodID);
methodInfo.env->DeleteLocalRef(methodInfo.classID);
}
And that's it! While calling CnxAttempt on C++, it goes BOOM because it doesn't recognise the method within the Java class and can't get to it...
Can someone give me a hand? If something is not clear please let me know. Thanks a bunch in advance!!
Creating a new global reference does does not create a new object. The difference between local and global references (from the docs) is:
Local references are valid for the duration of a native method call, and are automatically freed after the native method returns. Global references remain valid until they are explicitly freed.
If you want to call a non-static method to an object you need to either pass the object to the native method (if it exists - shouldn't the main activity already exist?), create a new one using the NewObject* functions, or by calling some factory method.
Then get the class object of the object, get the methodID and then call the method.
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.