My application makes calls to a native library, and then within the native library, makes calls back to the Java code. I have the calls TO the native library working correctly ( I'm pretty sure ), but not its telling me that it can't find the Java functions when trying to call them from the C file.
The application doesn't even crash, there is no pop up telling me it closed unexpectedly, it just flashes a black screen and nothing happens.
The code I am trying to reproduce was originally made in C for Palm Pilot, and is being transferred to an Android device. This is the original call in C I am trying to duplicate...
InitRelay(Changeit,getit,putit,flushit,delayit);
The parameters in the call above are function names, the functions are here below:
void Changeit(WORD baud){
ChangeRate(baud);
}
extern Boolean ChangeRate(UInt32 baud)
{...}
Int16 getit(UInt16 t) {...}
void putit( BYTE p ) {...}
void flushit(void){...}
extern void delayit( UInt16 wait ) {...}
This is InitRelay shown in my .c file:
BYTE __stdcall InitRelay(fp_setbaud _setbaud, fp_get _get, fp_put _put, fp_flush _flush, fp_delay _delay){
RelayAPI_SetBaud=_setbaud;
RelayAPI_get=_get;
RelayAPI_put=_put;
RelayAPI_flush=_flush;
RelayAPI_delay=_delay;
....
}
The parameters shown in above are typedefed in my .h file:
typedef void (__stdcall *fp_setbaud)(WORD);
typedef short (__stdcall *fp_get)(WORD);
typedef void (__stdcall *fp_put)(BYTE);
typedef void (__stdcall *fp_flush)(void);
typedef void (__stdcall *fp_delay)(WORD);
The WORD/BYTE/DWORD types are defined in a separate .h file shown here:
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
Now, in my Android code, I call a function I made called InitRelayJava(), all of my data for the application is stored in a separate class called RelayAPIModel. I created a nested class within that to store all of my native functions. I did this so that I can access these functions the same way no matter what Activity the application is currently in.
public class RelayAPIModel {
....
public static class NativeCalls {
static {
System.loadLibrary( "RelayAPI" );
}
public native static byte InitRelayJava();
public native static void FreeRelayJava();
public static void changeItJavaWrapper( short l ) {
mModelService.changeitJava( l );
}
public static void flushItJavaWrapper() {
mModelService.flushitJava();
}
public static void putItJavaWrapper( byte[] p ) {
mModelService.putitJava( p );
}
public static void delayItJavaWrapper( short wait ) {
mModelService.delayitJava( wait );
}
public static short getItJavaWrapper( short s ) {
return mModelService.getitJava( s );
}
}
}
The calls made inside the *Wrapper functions go to a separate class that I have to handle all of the applications bluetooth capabilities. I do not think that those are needed for this problem.
This is what InitRelayJava looks like in my C code...
BYTE Java_my_eti_commander_RelayAPIModel_00024NativeCalls_InitRelayJava( JNIEnv *env, jobject obj ) {
myEnv = (env);
bluetoothClass = (*env)->GetObjectClass( env, obj );
myObject = obj;
changeID = (*myEnv)->GetMethodID( myEnv, myObject, "changeitJavaWrapper", "(S)V" );
getID = (*myEnv)->GetMethodID( myEnv, myObject, "getitJavaWrapper" , "(S)S" );
putID = (*myEnv)->GetMethodID( myEnv, myObject, "putitJavaWrapper" , "(B)V" );
flushID = (*myEnv)->GetMethodID( myEnv, myObject, "flushitJavaWrapper" , "()V" );
delayID = (*myEnv)->GetMethodID( myEnv, myObject, "delayitJavaWrapper" , "(S)V" );
...
}
This is the LogCat I am receiving...
08-02 10:27:32.406: D/dalvikvm(28376): Trying to load lib /data/data/my.eti.commander/lib/libRelayAPI.so 0x40515430
08-02 10:27:32.406: D/dalvikvm(28376): Added shared lib /data/data/my.eti.commander/lib/libRelayAPI.so 0x40515430
08-02 10:27:32.406: D/dalvikvm(28376): No JNI_OnLoad found in /data/data/my.eti.commander/lib/libRelayAPI.so 0x40515430, skipping init
08-02 10:27:32.406: D/dalvikvm(28376): GetMethodID: method not found: Lmy/eti/commander/RelayAPIModel$NativeCalls;.changeitJavaWrapper:(S)V
08-02 10:27:32.413: D/dalvikvm(28376): GetMethodID: method not found: Lmy/eti/commander/RelayAPIModel$NativeCalls;.getitJavaWrapper:(S)S
08-02 10:27:32.413: E/dalvikvm(28376): Class lookup Ljava/lang/NoSuchMethodError; attempted while exception Ljava/lang/NoSuchMethodError; pending
InitRelayJava is a static method - that means your second parameter (obj) is a class object pointer, not a this pointer. So get rid of the following line:
bluetoothClass = (*env)->GetObjectClass( env, obj );
and instead pass obj to GetMethodID(), like this:
changeID = (*myEnv)->GetMethodID( myEnv, obj, "changeitJavaWrapper", "(I)Z" );
EDIT: also, your parameter/return type signatures are wrong. Short is not the same as int, so the signature for changeitJavaWrapper is "(S)Z", for getitJavaWrapper is "()I" as it takes no parameters and returns an int. Be more careful please; it does not take an advanced knowledge of C to get those right, just some self-checking. This project of yours is inching into What have you tried? territory.
Cheat sheet on JNI type codes here.
Let me try and anticipate your next question - you cannot call those methods. Of course you cannot, it's not possible to call a nonstatic method from a static one. Your Java callbacks are all nonstatic, while the native method is static. Either make them static, or make the native method nonstatic and insert the GetObjectClass back.
EDIT2: so you changed your Java methods to static without telling. Now instead of (*env)->GetMethodID() you need to call (*env)->GetStaticMethodID() to get the method ID. Same parameters.
Your method signatures are wrong. Use javap -s classname to get the signatures of the class, for example: javap -s java.lang.String.
You can find javap in your local JDK.
Related
I have an image buffer allocated on the heap in my c++ code that I would like to share with some other c++ objects as well as Java objects through JNI. On the native side im using shared_ptr and I was wondering what is the best way to do so ? my thought is to allocate the buffer on the heap once and share a reference everywhere. I'm taking advantage of smart pointers so that the buffer will be deallocated as soon as all the references go out of scope, but I'm facing an issue when sharing a reference to the java side.
how can I ensure that my java object has a valid reference to the buffer all the time ? how can c++ determine that the reference counter reaches 0 when java object is done using its reference. My concern is to avoid memory leak and also ensure that buffer doesn't get destroyed too soon before getting processed by the java class.
thanks for the help
The general answer is "make the Java object's lifetime influence the lifetime of the C++ object".
Start with the following Java class:
class Refholder implements AutoCloseable {
private long ptr; // the actual pointer
private long shared_ptr; // a pointer to a shared_ptr keeping `ptr` alive
public Refholder(long ptr, long shared_ptr) {
this.ptr = ptr;
this.shared_ptr = shared_ptr;
}
public native void close();
public void finalize() { close(); }
// Other methods to access the contents of `ptr` go here.
};
This will contain both the actual pointer and a pointer to a shared_ptr.
When you want to hand a reference to Java, use the following:
jobject passToJava(JNIEnv *env, std::shared_ptr<Foo> instance) {
jclass cls_Refholder = env->FindClass("Refholder");
jmethodID ctr_Refholder = env->GetMethodID(cls_Refholder, "<init>", "(JJ)V");
// This line increases the reference count and remembers where we put the copy
std::shared_ptr<Foo> *copy = new std::shared_ptr<Foo>(std::move(instance));
jobject ret = env->NewObject(cls_Refholder, ctr_Refholder, copy->get(), copy);
return ret;
}
Finally, the close method is responsible for extracting the shared_ptr and deallocating it:
JNIEXPORT void Java_Refholder_close(JNIEnv *env, jobject obj) {
jclass cls_Refholder = env->GetObjectClass(obj);
jfieldID fld_Refholder_ptr = env->GetFieldID(cls_Refholder, "ptr", "J");
jfieldID fld_Refholder_shared_ptr = env->GetFieldID(cls_Refholder, "shared_ptr", "J");
std::shared_ptr<Foo> *copy = (std::shared_ptr<Foo>*)env->GetLongField(obj, fld_Refholder_shared_ptr);
if (!copy)
return;
env->SetLongField(obj, fld_Refholder_ptr, 0);
env->SetLongField(obj, fld_Refholder_shared_ptr, 0);
delete copy;
}
I have decided to implement both AutoCloseable and finalize because I do not know how your Java code plans to use the reference. If you need deterministic destruction of the C++ object you need to use try-with-resources or explicit calls to the close method. If you do not, at last the finalize will close it.
I am new to using the Android NDK and JNI but have experience in both Java and C++. I'm trying to construct a MWE which shows that running a looping method natively will be faster than on a java class. I have imported the hello-jni sample, converted the JNI code to be C++ compatible and it run as intended.
In the same project I have erased the sample code and started again but not changed the build settings. In order to compare performance, I have created two identical classes which do looping operations on an integer and an array. I have a Java class called TestClassJava and a C++ class (through .h and .cpp files) called TestClassCpp class. These class are as follows:
public class TestClassJava {
// Fields
private int id;
private int[] array;
// Constructor
public TestClassJava(int start_id) { // etc... }
// Methods
public void incrementId() { // etc... }
public void labelArray() { // etc... }
public void loop() {
while (id < 5000000) {
incrementId();
labelArray();
}
}
}
The C++ header:
class TestClassCpp {
private:
int id;
int array[10];
public:
TestClassCpp(int start_id);
~TestClassCpp();
void incrementId();
void labelArray();
void loop();
};
And then the .cpp
#include "TestClassCpp.h"
// Constructor / Destructor
TestClassCpp::TestClassCpp(int start_id) { // etc... }
TestClassCpp::~TestClassCpp() { }
// Methods
void TestClassCpp::incrementId() { // etc... }
void TestClassCpp::labelArray() { // etc... }
void TestClassCpp::loop() {
while (id < 5000000) {
incrementId();
labelArray();
}
}
In the same jni directory as the header and cpp file I have a file called testclasswrapper.cpp which contains the interface code between the java in MainActivity and the TestClassCpp loop() method. In MainActivity I have added
static {
System.loadLibrary("testclasswrapper");
}
public native void nativeLooper();
nativeLooper() is declared in the testclasswrapper.cpp file as
JNIEXPORT void JNICALL
Java_com_example_jnimwe_MainActivity_nativeLooper(JNIEnv *env, jobject instance) {
// Get class
jclass cls = env->FindClass("TestClassCpp");
// Get method id of constructor
jmethodID constructor_method_id = env->GetMethodID(cls, "<init>", "(I)V" );
// Instantiate object from C++ definition
jobject jobj = env->NewObject(cls,constructor_method_id);
// Get loop method id from object
jmethodID mid = env->GetMethodID(cls,"loop","()V");
// Loop
env->CallVoidMethod(jobj,mid);
}
The application builds and deploys but fails to find the class with jclass cls = env->FindClass("TestClassCpp");. I have added the moduleName as testclasswrapper in the build.gradle file.
Error is
JNI DETECTED ERROR IN APPLICATION: JNI GetMethodID called with pending exception 'java.lang.ClassNotFoundException' thrown in unknown throw location
06-23 16:07:14.617 12131-12131/com.example.jnimwe A/art: art/runtime/check_jni.cc:64] in call to GetMethodID
so clearly it doesn't know about the class. I assume therefore that when building, gradle is not including the class definition when it build the library and is just building from the one file (testclasswrapper.cpp) rather than searching for all files and building and linking all of them. Adding an #include "./TestClassCpp.h" in thetestclasswrapper.cpp` is greyed out so the definition of the class is not being included that way.
The original hello-jni example builds using ndk-build as I understand it. Similar answers all refer to a makefile called Application.mk. There is no makefile in the hello-jni sample so there is no makefile in my example either. How do I tell gradle to build the class I have created and link it into the library I'm loading?
I need to integrate C++ files into my Android application project.
I can build the files and .so file is generated.
This is the header file which has the function process().
I need to invoke this method from my .java file.
class PayrollGenerator {
public:
typedef void (* PAYROLL_READY_CALLBACK) (std::vector<int> list, int id);
typedef void (* PROGRESS_CALLBACK) (int progress);
private:
PAYROLL_READY_CALLBACK _dataReadyCallback;
PROGRESS_CALLBACK _progressCallback;
public:
DataProcessor(PAYROLL_READY_CALLBACK dataReadyCallback, PROGRESS_CALLBACK progressCallback);
void process(int data);
};
Two callbacks are there which will give me result and the progress data of the data being processed.
I am not able to design the JNI methods for it.
BTW, I am run simple C++ programs from .java files.
But this one is quite complex to me.
Please help !!
Progress -
I created a C++ file and wrote the wrapper
JNIEnv *global;
jobject impl;
struct DataProcessorStruct {
jobject callback_ptr;
DataProcessor *dataProcessor;
};
void dataReadyCallback(std::vector<jint> processedSamples, jint heartRate){
jintArray arr = global->NewIntArray( processedSamples.size() );
global->SetIntArrayRegion( arr, 0, processedSamples.size(), ( jint * ) &processedSamples[0] );
jclass clazz = global->FindClass("com/app/AudioActivity");
jmethodID method = global->GetMethodID(clazz, "dataReady","[Ljava/util/List;I)V");
global->CallVoidMethod(impl,method,arr,heartRate);
__android_log_print(ANDROID_LOG_INFO, "test", "C-dataReadyCallback");
}
void progressCallback(jint progress){
jclass clazz = global->FindClass("com/app/AudioActivity");
jmethodID method = global->GetMethodID(clazz, "dataProcessProgress","(I)V");
global->CallVoidMethod(impl, method,progress);
__android_log_print(ANDROID_LOG_INFO, "test", "C-progressCallback");
}
JNIEXPORT jlong JNICALL Java_com_app_AudioActivity_createDataProcessorObject (JNIEnv * env, jobject obj){
global = env;
impl = obj;
DataProcessorStruct *cpp_obj = new DataProcessorStruct();
if (cpp_obj == NULL) {
return 0;
}
DataProcessor *csObj = new DataProcessor(dataReadyCallback,progressCallback);
if (csObj == NULL) {
return 0;
}
cpp_obj->dataProcessor = csObj;
cpp_obj->callback_ptr = env->NewGlobalRef(obj);
return (jlong)cpp_obj;
}
JNIEXPORT void JNICALL Java_com_app_processData (JNIEnv * env, jobject obj,jlong objId,jint sample,jint dataLeft){
impl = obj;
DataProcessorStruct *cpp_obj = (DataProcessorStruct *)objId;
DataProcessor *dataProcessor=cpp_obj->dataProcessor;
if (dataProcessor != NULL){
dataProcessor->process(sample,dataLeft);
}
}
The native methods in Java are -
public native long createDataProcessorObject();
public native void processData(long dataProcessor,int sample, int dataLeft);
Is this the right way of doing so ?
And is there any way I don't have to call class Java methods directly from dataReadyCallback() and progressCallback() C++ methods but somehow I can call interface methods which are in Java to get invoked from these C++ methods so that any class listening to these callbacks should get notified and not a particular class?
You can easily define native functions in Java wich are callbacks in your C++ programme. Usually you start declaring a class and some functions in java:
class MyJNative {
static { // static bloc, executed when class is loaded => load your cpp library
System.out.print ("Load library in ");
System.out.println(System.getProperty("java.library.path"));
System.loadLibrary("MyNativeCPP");
}
private static int next_id=1; // every object gets a unique id
private int id; // unique object id
public MyJNative () { // constructor (sets unique id)
id = next_id++;
// ...
}
public native static void doSetup(); // initialisation for your factory function
public native void doSomething(); // example method
...
}
Then you'll let javah generate the header for the native funtions in C++. You shouldn't hand-write the header yourself ! Here a step by step tutorial, and the example for the code above:
...
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: MyJNative
* Method: doSetup
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_MyJNative_doSetup
(JNIEnv *, jclass);
/*
* Class: MyJNative
* Method: doSomething
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_MyJNative_doSomething
(JNIEnv *, jobject);
...
#ifdef __cplusplus
}
#endif
#endif
As you see, JNI is designed to call C++/C functions and not member functions of C++ class. So you need to define some global functions (or static member functions of your class), that will get as a (first) parameter some ID which can identify the C++ object, and will then forward the call to this object.
The example above is simplified, but the idea would be that, the Java class maintains a unique ID. You could imagine that at creation, after setting the ID, it calls a factory function in C++, that would maintain a map<jint, unique_ptr<MyCPPclass>> objects so that all other functions like doSomething() can be invoked with ID as parameter and then call objects[ID].get()->doSomething().
The problem with this approach is the life of your C++ object: you manage it's destruction: C++ objects created on the C++ side won't be garbage collected, so it's difficult to guess when they are no longer needed.
A variant of this approach would be to host ther you "host it" somewhere in a byte array of a java object. But in this case the problem is the other way round: when the Java object is no longer needed, the C++ side won't be informed that the object is destructed and so the destructor won't be called.
As you can see, the JNI design makes it rather difficult to manage objects on both sides. So the safest approach is to manage objects on one side only and on the other side expose only functions that will use/manipulate the object.
When I call a native code for first time it returns proper result, but the second time it returns garbage value.
Here's the native code :
JNIEXPORT jstring JNICALL
Java_com_example_project_NativeCodes_method2(JNIEnv *env, jobject thisObj,
jstring st) {
const char* st1=env->GetStringUTFChars(st,0);
string str=st1;
const int len=str.length();
for(int i=0;i<len;i++){
if (str[i]>=97 && str[i]<=122)
str[i] = str[i]-32;
}
.....
env->ReleaseStringUTFChars(st, st1);
return env->NewStringUTF(str.c_str());
}
And in Java class I declare native method as follows
public class NativeCodes(){
static{
System.loadLibrary("abclib");
}
public synchronized native String method2(String s);
}
In MainActivity.java I call native method like:
String s1,s2,s3,s4;
s1=edttxt1.getText().toString();
s2=edttxt2.getText().toString();
NativeCodes nc=new NativeCodes();
s3=nc.method2(s1);
s4=nc.method2(s2);
While debugging I find s3 gets the proper result whereas s4 receives garbage value.
I think I'm releasing the pointer properly with env->ReleaseStringUTFChars(st, st1);
But if a Log() statement is inserted between two calls for the method, both calls return correct result. For example,
s3=nc.method2(s1);
Log.i("String","Value: "+s3);
s4=nc.method2(s2);
Log.i("String","Value: "+s4);
This gives expected result. But I don't want to insert unnecessary code as there are many such native method calls made.
where am I doing wrong? Any help is highly appreciated.
I have my JNI environment and jobject objects saved locally as of now. I found that for my JNI to run of ICS and up devices, I need to fix my JNI code. This is the error I get:
02-20 10:20:59.523: E/dalvikvm(21629): JNI ERROR (app bug): attempt to use stale local reference 0x38100019
02-20 10:20:59.523: E/dalvikvm(21629): VM aborting
02-20 10:20:59.523: A/libc(21629): Fatal signal 11 (SIGSEGV) at 0xdeadd00d (code=1), thread 21629
I am confused about how to create/destroy these globals, and if I am even doing it right.
My application currently runs fine on all pre-ICS devices using this code:
BYTE Java_my_eti_commander_RelayAPIModel_00024NativeCalls_InitRelayJava( JNIEnv *env, jobject obj ) {
myEnv = (env);
myObject = obj;
changeID = (*myEnv)->GetStaticMethodID( myEnv, myObject, "changeItJavaWrapper", "(S)V" );
getID = (*myEnv)->GetStaticMethodID( myEnv, myObject, "getItJavaWrapper" , "(S)S" );
putID = (*myEnv)->GetStaticMethodID( myEnv, myObject, "putItJavaWrapper" , "(B)V" );
flushID = (*myEnv)->GetStaticMethodID( myEnv, myObject, "flushItJavaWrapper" , "()V" );
delayID = (*myEnv)->GetStaticMethodID( myEnv, myObject, "delayItJavaWrapper" , "(S)V" );
RelayAPI_SetBaud= WrapSetBaud;
RelayAPI_get = WrapGetIt;
RelayAPI_put = WrapPutIt;
RelayAPI_flush = WrapFlushIt;
RelayAPI_delay = WrapDelayIt;
...
}
Under the GetStaticMethodID calls, the RelayAPI_ variables are all function pointers that lead here:
void WrapSetBaud( WORD w ) {
return (*myEnv)->CallStaticVoidMethod( myEnv, myObject, changeID, w );
}
short WrapGetIt( WORD time ) {
return (*myEnv)->CallStaticShortMethod( myEnv, myObject, getID, time );
}
void WrapPutIt( BYTE buff ) {
return (*myEnv)->CallStaticVoidMethod( myEnv, myObject, putID, buff );
}
void WrapFlushIt( void ) {
return (*myEnv)->CallStaticVoidMethod( myEnv, myObject, flushID );
}
void WrapDelayIt( WORD wait ) {
return (*myEnv)->CallStaticVoidMethod( myEnv, myObject, delayID, wait );
}
Finally, it returns to my Java code here:
public static void changeItJavaWrapper( short l ) throws IOException {
mModelService.changeitJava( l );
}
public static void flushItJavaWrapper() {
mModelService.flushitJava();
}
public static void putItJavaWrapper( byte p ) {
mModelService.putitJava( p );
}
public static void delayItJavaWrapper( short wait ) {
mModelService.delayitJava( wait );
}
public static short getItJavaWrapper( short s ) throws IOException {
return mModelService.getitJava( s );
}
I have changed my initializations to:
myEnv = (*env)->NewGlobalRef(env,obj);
myObject = (*env)->NewGlobalRef(env,obj);
But i'm extremely confused with this, as they have the same parameters, and it just doesn't make sense. I can't find documentation for this method anywhere as stupid as that sounds, this tutorial, this page, and the oracle docs don't have any information on the NewGlobalRef method itself.
EDIT
jmethodID changeID;
jmethodID getID;
jmethodID putID;
jmethodID flushID;
jmethodID delayID;
jobject myObject;
jclass bluetoothClass;
JNIEnv *myEnv;
First of all: myEnv = (*env)->NewGlobalRef(env,obj); is just wrong. You mustn't cache this value.
What you're allowed to is to cache method IDs, field IDs, class references, ... (but make sure you clean up this stuff afterwards). But caching these values requires special measures.
Why? The problem is that the JVM is allowed to load and unload classes according to the needs of the program. Therefore it could happen that a class is unloaded as soon as the last instance of the class has been destroyed by the garbage collector. As soon as this happens your cached IDs are not valid anymore. It's possible that the IDs will be the same after the JVM loads the class again but this is not guaranteed.
Solution: If you want to cache these IDs you have to tell the JVM that it's not allowed to unload a class. This is exactly what NewGlobalRef does. You just increment the reference for the reference passed to NewGlobalRef so the reference count never drops to zero and grabage collection is not allowed to clean up the referenced element.
Attention: Creating a NewGlobalRef has a serious drawback: Other than in Java you have to make sure that you call DeleteGlobalRef if you don't need this reference anymore in order to reenable the garbage collection of the reference. (As the garbage collecter is not aware of wether you still need this reference or not) Or in other words: You have to make sure that you cleanup your garbage yourself otherwise you'll leave a memory leak.
I'd also say it's not a good idea to create a global ref for an object (unless you really want to keep the object alive) as this means the object won't ever get into garbage and therefore never will be freed.
Better Variant: If you want to cache these IDs in order to speedup access to a certain object, keep a global reference for the class (using FindClass) and grab the IDs from the class object FindClass returns.
Here's a (incomplete) example of what I mean. I usually create a structure holding all the IDs I need to access a class just to keep my namespaces clean. You can imagine this as follows:
/*! \brief Holds cached field IDs for MyClass.java */
typedef struct MyClass {
int loaded; /*!< Nonzero if the information are valid */
jclass clazz; /*!< Holds a global ref for the class */
jfieldID aField; /*!< Holds the field ID of aField */
}tMyClass;
static tMyClass me = { 0 };
The easiest way is to provide a "connect" function for your object which does the initialization of the structure defined above.
/*! \brief This function fetches field IDs for a specific class in order to have
faster access elsewhere in the code
\param env a valid JNI environment
\return
- 0 if OK
- <0 if an error occured */
int MyClass_connect(JNIEnv *env)
{
jobject theClass = env->FindClass("MyClass");
if (theClass == NULL) goto no_class;
me.clazz = (jclass) env->NewGlobalRef(theClass); // make it global to avoid class unloading and therefore
// invalidating the references obtained.
if (me.clazz == NULL) goto no_memory;
me.aField = env->GetFieldID(me.clazz, "aField", "I") ;
if (me.aField == NULL) goto no_aField;
me.loaded = 1;
return 0;
no_aField:
env->DeleteGlobalRef(me.clazz);
no_memory:
no_class:
return -1;
}
After calling MyClass_connect successfully you can use me.aField to shorten the code to access a the field in your code. Of course you have to provide a disconnect function which is called when MyClass is not required anymore:
void MyClass_disconnect(JNIEnv *env)
{
if (me.loaded == 0) return;
env->DeleteGlobalRef(me.clazz);
memset(me, 0, sizeof(tMyClass));
}
Sorry for this a bit lengthy posting but I hope this helps to solve your confusion a bit and gives you a bit an insight of the inner workings of JNI together with a little receipe how to deal with this efficiently.
Edit: You can find documentation about JNI calls on oracle's website