Adroid JNI:no implementation found in native

参考文章:http://laokaddk.blog.51cto.com/368606/1349386


一  javah引发的问题



BUG:


D/dalvikvm( 1704): Trying to load lib /data/data/com.ulang/lib/libulangaudio.so 0x41052a38
D/dalvikvm( 1704): Shared lib '/data/data/com.ulang/lib/libulangaudio.so' already loaded in same CL 0x41052a38
W/dalvikvm( 1704): No implementation found for native Lcom/ulang/AudioLib;. sayHelloEx ()Ljava/lang/String;
D/AndroidRuntime( 1704): Shutting down VM
W/dalvikvm( 1704): threadid=1: thread exiting with uncaught exception (group=0x409961f8)
E/AndroidRuntime( 1704): FATAL EXCEPTION: main
E/AndroidRuntime( 1704): java.lang.UnsatisfiedLinkError: sayHelloEx
E/AndroidRuntime( 1704):       at com.ulang.AudioLib.sayHelloEx(Native Method)
E/AndroidRuntime( 1704):       at com.ulang.One.onClick(One.java:76)
E/AndroidRuntime( 1704):       at android.view.View.performClick(View.java:3480)
E/AndroidRuntime( 1704):       at android.view.View$PerformClick.run(View.java:13983)
E/AndroidRuntime( 1704):       at android.os.Handler.handleCallback(Handler.java:605)
E/AndroidRuntime( 1704):       at android.os.Handler.dispatchMessage(Handler.java:92)
E/AndroidRuntime( 1704):       at android.os.Looper.loop(Looper.java:137)




发现第二个参数 javah生成为 jclass, 因此出错 ,

要将其换成 jobject则可以.

Javah并不是所有情况都将第二项生成 jclass, 有时也是生成的jobject.

NE:要特别注意!

就比如下面这个


#define __cplusplus
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     com_ulang_AudioLib
* Method:    sayHelloEx
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_ulang_AudioLib_sayHelloEx(JNIEnv *, jclass);//要改为jobject
#ifdef __cplusplus
}
#endif
#endif


二、在实验JNI接口调用的时候,发现时而成功,时而执行就异常,在logcat上就有提示:

no implementation found in native ....

后来搜索了一下网络,发现出现这种情况有可能有几种情况

1.函数名字写错了

2.确认在更改了接口函数的时候,要重新clean一下工程,再rebuild all。


在确认以上两点后,JNI接口调用的no implementation error没有出现了。




三、运行c++生成的.so库,若报以下错误:(既找不到函数)

No implementation found for native Lcom/dgut/android/MainActivity;.stringFromJNI ()Ljava/lang/String;

java.lang.UnsatisfiedLinkError: stringFromJNI

at com.dgut.android.MainActivity.stringFromJNI(Native Method)

解决方法:

为供Java调用的c++函数前加入extern "C" 修饰,如:(NDK example里面的cpp文件也是这么声明的,参考hello-gl2),被extern "C"修饰的变量和函数是按照C语言方式编译和连接的。

就是要用extern “C”把所有的 c++代码按照 c 语言的方式编译和链接

就像这样

test2.h


#ifndef __jnihe__test2__
#define __jnihe__test2__

#include <stdio.h>
#ifdef __cplusplus
extern "C"
{
#endif
void setout(const char*);
#ifdef __cplusplus
}
#endif
#endif /* defined(__jnihe__test2__) */


test2.m


#include "cocos2d.h"
USING_NS_CC;
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include <jni.h>
#include "platform/android/jni/JniHelper.h"
#ifdef __cplusplus
extern "C"
{
#endif
void setout(const char*s)
{
    CCLOG("mmmmmmmmmmmmmmmmmmmmm");
}
void Java_ioss_jnitesthelper_setoutsm(JNIEnv *env,jobject thiz)
{
    setout("s");
}
#ifdef __cplusplus
}
#endif
#endif


要按照 c 语言编译的原因是这样的:作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:void foo( int x, int y );该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数voidfoo( int x, int y )与void foo( int x, float y )编译生成的符号是不相同的,后者为_foo_int_float。

同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编写程序的类成员变量可能与全局变量同名,我们以"."来区分。而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。

因此,若我们没有使用extern "C"修饰函数,按照C语言方式编译和连接,Jni调用将可能找不到该函数。

最后说下JNIEnv *env参数的使用

所有JNI接口的第一个参数是JNIEnv *env, 在C中,使用方法是

(*env)->NewStringUTF(env, "Hello from JNI!");

但在C++中,其调用方法是

env->NewStringUTF("Hello from JNI!");

为什么有这种区别呢,看看jni.h中关于JNIEnv的定义就可以知道了:

#if defined(__cplusplus)

typedef _JNIEnv JNIEnv;

#else

typedef const struct JNINativeInterface* JNIEnv;

#endif

可以看到,对于C和C++,定义有所不同,主要原因是C不支持类,所以采用了一种变通的方法。


Last modification:January 1st, 1970 at 08:00 am
如果看了这个文章可以让你少加会班,可以请我喝杯可乐
已打赏名单
微信公众号

Leave a Comment