0

我的应用程序调用本地库,然后在本地库中调用 Java 代码。我对本机库的调用正常工作(我很确定),但它没有告诉我在尝试从 C 文件调用它们时找不到 Java 函数。

该应用程序甚至没有崩溃,没有弹出窗口告诉我它意外关闭,它只是闪烁黑屏并且没有任何反应。

我试图重现的代码最初是用 C 语言为 Palm Pilot 制作的,并且正在转移到 Android 设备上。这是 CI 中的原始调用,我试图复制...

InitRelay(Changeit,getit,putit,flushit,delayit);

上面调用中的参数是函数名,函数如下:

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 ) {...}

这是我的.c文件中显示的 InitRelay:

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;
    ....
}

上面显示的参数是在我的.h文件中定义的:

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

这些WORD/BYTE/DWORD类型在此处显示的单独.h文件中定义:

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;

现在,在我的 Android 代码中,我调用了一个名为 InitRelayJava() 的函数,应用程序的所有数据都存储在一个名为RelayAPIModel. 我在其中创建了一个嵌套类来存储我的所有本机函数。我这样做是为了无论应用程序当前处于什么活动中,我都可以以相同的方式访问这些功能。

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

在函数内部进行的*Wrapper调用转到一个单独的类,我必须处理所有应用程序的蓝牙功能。我认为这个问题不需要这些。

这就是 InitRelayJava 在我的 C 代码中的样子......

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"  );
...
}

这是我收到的 LogCat...

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
4

2 回答 2

1

InitRelayJava是一个静态方法 - 这意味着您的第二个参数 ( obj) 是一个类对象指针,而不是一个this指针。所以摆脱以下行:

bluetoothClass = (*env)->GetObjectClass( env, obj );

而是传递obj给 GetMethodID(),如下所示:

changeID = (*myEnv)->GetMethodID( myEnv, obj, "changeitJavaWrapper", "(I)Z"  ); 

编辑:另外,您的参数/返回类型签名是错误的。Short 与 int 不同,因此 for 的签名changeitJavaWrapper是“(S)Z”,forgetitJavaWrapper是“()I”,因为它不带参数并返回一个 int。请多加小心;不需要高深的 C 知识就能做到这些,只需进行一些自我检查。你的这个项目正在慢慢进入你尝试了什么?领土。

JNI 类型代码的备忘单在这里

让我试着预测你的下一个问题——你不能调用这些方法。当然你不能,不可能从静态方法调用非静态方法。您的 Java 回调都是非静态的,而本地方法是静态的。要么将它们设为静态,要么将本机方法设为非静态并插入GetObjectClass后面。

EDIT2:所以您将Java方法更改为静态而不告诉。现在,(*env)->GetMethodID()您不需要调用(*env)->GetStaticMethodID()来获取方法 ID。相同的参数。

于 2012-08-01T18:24:56.100 回答
0

你的方法签名是错误的。用于javap -s classname获取类的签名,例如:javap -s java.lang.String. 您可以javap在本地 JDK 中找到。

于 2012-08-02T13:21:03.640 回答