0

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
5
  • Does this compile ? Coz there s a space in the static class's name... 'Native Class' Commented Aug 1, 2012 at 17:37
  • Remove space from your static inner class name Commented Aug 1, 2012 at 17:41
  • that was atypo in the SO quetsion, its not like that in my code. Commented Aug 1, 2012 at 17:41
  • There is no space in the class name in my code. I typed all of the code above into the SO question without copy/paste. The typo was in my writing of the question, in my code it is written properly and there is no syntax error Commented Aug 1, 2012 at 17:44
  • @userSeven7s any other ideas? Commented Aug 1, 2012 at 18:01

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

11 Comments

Unfortunately that didn't work, my LogCat did change a bit after the edit though. Check the edit if you find the time. Thanks.
See the edit. AND CHECK FOR THOSE THINGS YOURSELF BEFORE YOU ASK.
I did check for those, sorry. I had made those signatures a week or so ago and thought that they matched.
So did you fix the signatures? Did it help?
For the record, the crushing sense of utter impotence is a fairly regular state of mind in our profession. The trick it channeling it into a nice productive rage.
|
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.