Accessing field or calling methods in Activity from JNI - android

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

Android jni not use singleton mode

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.

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);

JNI Method not found

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.

How to make a proper call from Android to a non-static function in Java? (Cocos2Dx in the mix)

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.

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