参考文章: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不支持类,所以采用了一种变通的方法。
版权属于:东哥笔记 - DongGe.org
本文链接:https://dongge.org/blog/176.html
自2017年12月26日起,『转载以及大段采集进行后续编辑』须注明本文标题和链接!否则禁止所有转载和采集行为!