前一段时间做过jni的调用,也成功通过了。最近在开发一个新的项目的时候用前一个项目的so库,反复提示UnsatisfiedLinkerror+库名字。无奈各种google下发现是因为新项目路径有所不同导致。只能自己在当前项目重新编译生成库文件。
温习一遍编译过程,非常简单:首先生成了要调用的java文件 我的是 。到项目的bin目录下运行命令行:javah -classpath classes -d jni xx.xx.xx.ESInterface 生成头文件 xx.xx.xx.ESInterface.h。将此头文件复制一份改成相应的c文件xx.xx.xx.ESInterface.c。然后就是相应的c文件的实现了。
关于c文件的实现说下,如果你之前已经写好了一个c文件,比如叫做:a.c。可以在ESInterface.c中调用a.c中的函数。省的再写一遍了。。不过调用要符合jni编程规范,jni 数据类型跟纯c是不一样的,至少外观不一样。我这儿也犯2了,直接调用a.c中的函数,当然不成功,还是问的一个同事才醒悟。给出一个调用的示例:
/*
* Class: xx.xx.xx.ESInterface
* Method: getDCGGMaxMoney
* Signature: ([Ljava/lang/Object;[III)F
*/
JNIEXPORT jfloat JNICALL Java_com_esun_cheetah_view_personal_lib_ESInterface_getDCGGMaxMoney (JNIEnv* env , jclass obj , jobject touZhuData_ , jintArray orgGGTypeArr , jint beiShu , jint allGameNum) {
const int size = (*env)->GetArrayLength(env, touZhuData_);
struct touZhuData dataArray[size];
int k=0;
for(k=0;k<size;k++)
{
jobject* objTmp = (*env)->GetObjectArrayElement(env, touZhuData_, k);
//struct touZhuData* data = (struct touZhuData*)malloc(sizeof(struct touZhuData));
jclass objectClass = (*env) -> FindClass(env , "com/esun/libc/TouZhuData");
// 胜赔率
jfieldID winPL = (*env) -> GetFieldID(env ,objectClass,"winPL","F");
// 平赔率
jfieldID drawPL = (*env) -> GetFieldID(env ,objectClass,"drawPL","F");
// 负赔率
jfieldID losePL = (*env) -> GetFieldID(env ,objectClass,"losePL","F");
// 是否为胆码
jfieldID isDan = (*env) -> GetFieldID(env ,objectClass,"isDan","I");
// 是否为有效场次
jfieldID isEff = (*env) -> GetFieldID(env ,objectClass,"isEff","I");
dataArray[k].winPL = (*env) -> GetFloatField(env , objTmp , winPL);
dataArray[k].drawPL = (*env) -> GetFloatField(env , objTmp , drawPL);
dataArray[k].losePL = (*env) -> GetFloatField(env , objTmp , losePL);
dataArray[k].isDan = (*env) -> GetIntField(env , objTmp , isDan);
dataArray[k].isEff = (*env) -> GetIntField(env , objTmp , isEff);
//dataArray[k] = data;
}
jint* pValue = (*env) -> GetIntArrayElements(env ,orgGGTypeArr , NULL);
jsize len = (*env) -> GetArrayLength(env ,orgGGTypeArr);
int* array[len];
int i=0;
for( i=0;i<len;i++)
{
array[i] = pValue[i];
}
return (jfloat)getDCGGMaxMoney(dataArray , array , beiShu , allGameNum);
}
当然,仅仅这样还是不够的。既然调用了额外的c函数自然要在Android.mk中搞一下了:
LOCAL_SRC_FILES := a.c (这个是被调用的c文件)
LOCAL_SRC_FILES +:xx.xx.xx.ESInterface.c (这个是你的jni的c文件)
好了,最后给出Android.mk的完整内容:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libesunlib
LOCAL_SRC_FILES := a.c
LOCAL_SRC_FILES +::xx.xx.xx.ESInterface.c
LOCAL_C_INCLUDES := E:\android-ndk-r7\sources\libesunlib\jni
include $(BUILD_SHARED_LIBRARY)
各个含义网上的介绍很多,在此不提。
最后编译so文件:
将上面的4个文件:
xx.xx.xx.ESInterface.h
xx.xx.xx.ESInterface.c
a.c
Android.mk
所在的文件夹放到 ndk 的sources 下,或者apps下等都行,运行你的cygwin: $NDK/ndk-build 搞定。($NDK 是你自己配置的路径,嫌麻烦的话直接cd到ndk-build的目录下也行)。
但是,但是,运行,竟然报UnsatisfiedLinkerror+函数名 错误。再搞,返现函数名啥的都没有问题呀,包含头文件之类的也ok。。。
最后将ESInterface.c中 从.h中拷贝过来的
#ifndef _Included_com_esun_cheetah_view_personal_lib_ESInterface
#define _Included_com_esun_cheetah_view_personal_lib_ESInterface
#ifdef __cplusplus
extern "C" {
#endif
之类的统统拿掉,重新编译,ok,运行,ok。
温习一遍编译过程,非常简单:首先生成了要调用的java文件 我的是 。到项目的bin目录下运行命令行:javah -classpath classes -d jni xx.xx.xx.ESInterface 生成头文件 xx.xx.xx.ESInterface.h。将此头文件复制一份改成相应的c文件xx.xx.xx.ESInterface.c。然后就是相应的c文件的实现了。
关于c文件的实现说下,如果你之前已经写好了一个c文件,比如叫做:a.c。可以在ESInterface.c中调用a.c中的函数。省的再写一遍了。。不过调用要符合jni编程规范,jni 数据类型跟纯c是不一样的,至少外观不一样。我这儿也犯2了,直接调用a.c中的函数,当然不成功,还是问的一个同事才醒悟。给出一个调用的示例:
/*
* Class: xx.xx.xx.ESInterface
* Method: getDCGGMaxMoney
* Signature: ([Ljava/lang/Object;[III)F
*/
JNIEXPORT jfloat JNICALL Java_com_esun_cheetah_view_personal_lib_ESInterface_getDCGGMaxMoney (JNIEnv* env , jclass obj , jobject touZhuData_ , jintArray orgGGTypeArr , jint beiShu , jint allGameNum) {
const int size = (*env)->GetArrayLength(env, touZhuData_);
struct touZhuData dataArray[size];
int k=0;
for(k=0;k<size;k++)
{
jobject* objTmp = (*env)->GetObjectArrayElement(env, touZhuData_, k);
//struct touZhuData* data = (struct touZhuData*)malloc(sizeof(struct touZhuData));
jclass objectClass = (*env) -> FindClass(env , "com/esun/libc/TouZhuData");
// 胜赔率
jfieldID winPL = (*env) -> GetFieldID(env ,objectClass,"winPL","F");
// 平赔率
jfieldID drawPL = (*env) -> GetFieldID(env ,objectClass,"drawPL","F");
// 负赔率
jfieldID losePL = (*env) -> GetFieldID(env ,objectClass,"losePL","F");
// 是否为胆码
jfieldID isDan = (*env) -> GetFieldID(env ,objectClass,"isDan","I");
// 是否为有效场次
jfieldID isEff = (*env) -> GetFieldID(env ,objectClass,"isEff","I");
dataArray[k].winPL = (*env) -> GetFloatField(env , objTmp , winPL);
dataArray[k].drawPL = (*env) -> GetFloatField(env , objTmp , drawPL);
dataArray[k].losePL = (*env) -> GetFloatField(env , objTmp , losePL);
dataArray[k].isDan = (*env) -> GetIntField(env , objTmp , isDan);
dataArray[k].isEff = (*env) -> GetIntField(env , objTmp , isEff);
//dataArray[k] = data;
}
jint* pValue = (*env) -> GetIntArrayElements(env ,orgGGTypeArr , NULL);
jsize len = (*env) -> GetArrayLength(env ,orgGGTypeArr);
int* array[len];
int i=0;
for( i=0;i<len;i++)
{
array[i] = pValue[i];
}
return (jfloat)getDCGGMaxMoney(dataArray , array , beiShu , allGameNum);
}
当然,仅仅这样还是不够的。既然调用了额外的c函数自然要在Android.mk中搞一下了:
LOCAL_SRC_FILES := a.c (这个是被调用的c文件)
LOCAL_SRC_FILES +:xx.xx.xx.ESInterface.c (这个是你的jni的c文件)
好了,最后给出Android.mk的完整内容:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libesunlib
LOCAL_SRC_FILES := a.c
LOCAL_SRC_FILES +::xx.xx.xx.ESInterface.c
LOCAL_C_INCLUDES := E:\android-ndk-r7\sources\libesunlib\jni
include $(BUILD_SHARED_LIBRARY)
各个含义网上的介绍很多,在此不提。
最后编译so文件:
将上面的4个文件:
xx.xx.xx.ESInterface.h
xx.xx.xx.ESInterface.c
a.c
Android.mk
所在的文件夹放到 ndk 的sources 下,或者apps下等都行,运行你的cygwin: $NDK/ndk-build 搞定。($NDK 是你自己配置的路径,嫌麻烦的话直接cd到ndk-build的目录下也行)。
但是,但是,运行,竟然报UnsatisfiedLinkerror+函数名 错误。再搞,返现函数名啥的都没有问题呀,包含头文件之类的也ok。。。
最后将ESInterface.c中 从.h中拷贝过来的
#ifndef _Included_com_esun_cheetah_view_personal_lib_ESInterface
#define _Included_com_esun_cheetah_view_personal_lib_ESInterface
#ifdef __cplusplus
extern "C" {
#endif
之类的统统拿掉,重新编译,ok,运行,ok。