Android NDK C++ JNI(未找到本机实现...)

100

我想使用NDK和C++,但好像无法正确地命名方法。我的本地方法如下:

extern "C" {
JNIEXPORT void JNICALL Java_com_test_jnitest_SurfaceRenderer_drawFromJni
(JNIEnv* env, jclass c)
{
   //
}
}

还有一个用extern "C" {}包装的头文件。一切都编译良好,创建了.so文件并将其复制到项目下的libs文件夹中,但在Eclipse中调试和运行时,我不断收到“找不到本地实现”的日志消息。是否有我遗漏的东西,因为所有的NDK示例都是用C语言写的?

谢谢。


你是否使用javah生成JNI存根?如果没有,那么你应该使用它。:-P - C. K. Young
18
很可能是因为您没有调用 System.loadLibrary 方法。 - IgorGanapolsky
1
谢谢您的问题。我今天学到了一件新事情。 - Shady Mohamed Sherif
11个回答

183

有几种原因可能导致"未找到实现"。一种是函数原型名称拼写错误,另一种是完全未能加载.so文件。你确定在使用该方法之前调用了System.loadLibrary()吗?

如果没有定义JNI_OnLoad函数,您可能需要创建一个,并让它输出日志消息来验证是否成功加载库。

您已经避免了最常见的问题——忘记使用extern "C"——因此要么是上述问题,要么是轻微的拼写错误。Java声明是什么样子的?


18
耶稣!你救了我数小时的工作 - 阅读此内容时,我才想起我的loadLibrary调用被注释掉了... - zeboidlund
11
你也救了我!我多次检查了我的函数名和其他一些东西......但我忘记了extern "C",在问题中甚至没有注意到它! - Qwertie
1
现在在Android文档网站上:http://developer.android.com/training/articles/perf-jni.html#faq_ULE - fadden
3
还有一个没有提到的情况:你不能在函数名中使用下划线 '_',因为下划线被解释为包分隔符。因此,只有驼峰命名法的函数名是可能的。 - Nulik
2
@Nulik:如果你将它转义为"1",你可以使用''。 - fadden
显示剩余5条评论

21

这个错误的另一个原因是:未经装饰的本地方法名称不能包含下划线!

例如,我想要导出一个名为AudioCapture_Ping()的C函数。在C中,我的导出声明如下:

JNI_EXPORT int Java_com_obsidian_mobilehashhost_MainActivity_AudioCapture_Ping(JNIEnv *pJniEnv, jobject object);  //Notice the underscore before Ping

以下是我的Java类引用该函数的代码:

package com.obsidian.mobileaudiohashhost;
...
public class MainActivity extends Activity {
    private native int AudioCapture_Ping();  // FAILS
    ...

在去除下划线之前,我无法使Android动态链接到我的本地方法。

JNI_EXPORT int Java_com_obsidian_mobilehashhost_MainActivity_AudioCapturePing(JNIEnv *pJniEnv, jobject object); 

package com.obsidian.mobileaudiohashhost;
...
public class MainActivity extends Activity {
    private native int AudioCapturePing();  // THIS WORKS!
    ...

9
Java语言声明中的'_'符号需要在本地声明中替换为"_1"。请参阅http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/design.html中的表2-1。 - fadden
1
如此明显,但我在调试了几个小时后仍然没能自己解决它...谢谢,你救了我! - Georgi Atsev
@user1222021 我有一个问题,我们可以为项目中的所有活动导出cpp方法吗?我们需要在.cpp文件的方法中指定活动名称吗? - Kostadin Georgiev

16

我也遇到了同样的问题,但是我的错误在文件Android.mk中。我的文件内容如下:

LOCAL_SRC_FILES := A.cpp
LOCAL_SRC_FILES := B.cpp 

但应该有这个:

LOCAL_SRC_FILES := A.cpp
LOCAL_SRC_FILES += B.cpp 

请注意细节,使用+=而不是:=

希望这有所帮助。


2
或者您可以将它们全部添加在一行上,或使用 \\ 换行符。 - IgorGanapolsky

7

在自动生成的Studio示例中,使用了extern "C",但忘记将文件的其余部分(包括后续函数)用{}括起来。只有第一个函数能正常工作。


5

2
你可以在 Android NDK 分发的 hello-gl2 示例中找到 cpp 文件。 - Yenchi
1
@pevik 它又挂了。 - Mygod
@Mygod 已修复 (https://github.com/googlesamples/android-ndk/blob/master/hello-gl2/app/src/main/jni/gl_code.cpp) - pevik
链接又挂了。 - user13107

5

另一个原因是:在android.mk中使用LOCAL_WHOLE_STATIC_LIBRARIES而不是LOCAL_STATIC_LIBRARIES。这样可以防止库优化未使用的API调用,因为NDK无法检测到来自java代码的本地绑定的使用。


2
在 android.mk 中使用 LOCAL_WHOLE_STATIC_LIBRARIES 而不是 LOCAL_STATIC_LIBRARIES 有什么区别? - Josh

3
如果您的包名中包含下划线(_)字符,应在下划线后面写入1(one),如下所示:
MainActivity.java
package com.example.testcpp_2;

native-lib.cpp

JNICALL
Java_com_example_testcpp_12_MainActivity_stringFromJNI(

3
使用javah(Java SDK的一部分)。它是用于此目的的工具(从.class文件生成.h头文件)。

1

我尝试了上面所有的解决方案,但是没有一个能够解决我的构建错误(jni java.lang.UnsatisfiedLinkError: No implementation found for...), 最后我发现我忘记将我的verify.cpp源文件添加到CMakeList.txt add_library segement中(verify.cpp是通过Ctrl + Enter短键自动生成的,可能是其他文件名),希望我的回复能帮助到某些人。

我的构建环境:Gradle + CMake


1

我遇到了同样的问题,我的情况是因为包名中有下划线"RFID_Test"。我重命名了该包名,问题得以解决。 感谢user1222021


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接