使用C++与Android ndk/jni

34
所有的ndk示例都只使用在头文件中声明为extern并在cpp文件中定义的基本C函数。然后,在包含jni回调的C文件中包含头文件后,一切都可以正常工作。
是否可能在android ndk中使用C++类?我的应用程序不会成为本地活动,它仍将有一个重要的java部分,但它将调用本机C代码进行CPU密集型计算(已经用C++编写,具有类和其他C++内容)。
这是我现在的类似于hello-world的结构:
文件“first.h”
#ifndef FIRST_H
#define FIRST_H

class Test
{};

#endif /* FIRST_H */

文件 "second.cpp"

#include <jni.h>
#include "first.h"

#ifdef __cplusplus
extern "C" {
#endif

jint Java_com_example_twolibs_TwoLibs_add( JNIEnv*  env,
                                      jobject  this,
                                      jint     x,
                                      jint     y )
{
    Test t;
    return 0;
}

#ifdef __cplusplus
}
#endif

最后是 Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := libtwolib-second
LOCAL_SRC_FILES := second.cpp

include $(BUILD_SHARED_LIBRARY)

这很基础,但是无法编译。将second.cpp转换成.c文件会在包含头文件时引发错误,我猜这是因为它不是C++文件。

error: expected '=', ',', ';', 'asm' or '__attribute__' before 'Test'

将其更改为.cpp会引发以下错误:
make: *** No rule to make target `/cygdrive/c/android-ndk-r5c/samples/twolibs/jni/second.c', needed by `/cygdrive/c/android-ndk-r5c/samples/two-libs/obj/local/armeabi/objs/twolib-second/second.o'.  Stop.

有什么办法可以让这个东西编译吗?
谢谢。
6个回答

15
您可以使用NDK与C++一起使用,但C++代码文件必须带有 .cpp 扩展名。
从 ANDROID-MK.html 中获悉:
请注意,C++源文件的默认扩展名为“.cpp”。但是定义变量 LOCAL_CPP_EXTENSION 可以指定不同的扩展名。不要忘记初始点(即“。cxx”有效,但不是“cxx”)。

似乎回调C代码(包含从Java端调用Java_com_example_twolibs_TwoLibs_add等调用的文件)必须在.c文件中(而不是.cpp文件中)。否则,它在我的机器上无法编译。因此,当我尝试包含一个包含类声明的头文件时,会出现相同的错误。 - pot2mayo
1
将所有内容转换为.cpp文件会导致一个make错误:***没有规则可以制定目标,这意味着make文件找不到源文件...但如果我将它们转换为.c文件,则可以找到,因此我猜这不是路径问题。 - pot2mayo
它必须编译,但你需要在所有JNI函数中添加“external"C"”。你是否在makefile中更改了源文件扩展名? - Michael
是的,我尝试在jni函数周围添加extern "C"(我还在帖子中更新了我的代码),但仍然出现相同的错误“make: *** No rule to make target...”。 Makefile中的文件扩展名也是正确的。 - pot2mayo
4
好的,我明白了。您的解决方案实际上是正确的,但是我忘记清除一些“.o”文件,因此make仍然在寻找“.c”文件。一旦我清理了所有内容并运行了新的ndk-build,一切都正常了。谢谢! - pot2mayo

3
你需要为Android重新编译所有本地库。你确实需要所有第三方本地库的源代码,因为通常情况下,我们在Android之外编译和链接这些库时,它们会链接到glibc,但不幸的是,由于许可证和性能问题,Android不使用glibc。Android使用一个简化版的glibc,称为libc。它对大多数常用功能具有与glibc相匹配的符号名称。但据我所知,libc没有与strings相关的某些功能,也绝对没有一些posix支持。如果您的本地库正在使用任何已弃用的功能,则必须通过使用libc支持的替代功能并相应地编写库来找到解决方法。
此外,正如你正确指出的那样,你将不得不使用NDK将Java(Android应用程序/框架)与本地世界(C ++)接口起来。
尽管听起来很简单,但根据我的经验,在Android上编译本地库(Android移植)传统上非常耗费时间,没有成功的保证。

谢谢您的回答。实际上,我甚至无法开始移植我的第三方库,因为一旦我在其中使用类,就无法编译出一个简单的类似于“hello-world”的测试。第一步是创建一个几乎为空的本地C++测试类,并在我的jni回调C文件中使用它。只是为了看看是否可以编译。 - pot2mayo

1
关于您的编译错误,看起来您最初把它命名为“second.c”,后来改名为“second.cpp”,但对象文件仍然以“second.c”命名,因此在编译(bdk-build)之前,您需要删除/cygdrive/c/android-ndk-r5c/samples/two-libs/obj/local/armeabi/objs/twolib-second/目录中的*.o和*.d文件。

0
错误:在 "class" 之前期望的是 '=', ',', ';', 'asm' 或 '__ attribute __'。经典情况下,类关键字之前缺少了分号?难以想象。
 int functionname(int p)
 class X { } ;

这很容易导致编译器出现错误信息。常见的复杂因素是它看起来像

 #include "someheader.h"
 class X { } ;

错误在someheader.h或任何递归包含的文件中的最后一个声明中;)


这是我最初的想法,但我检查过了,这不是语法错误。 - pot2mayo

0

编辑 Android.mk

修改 LOCAL_SRC_FILES 的实例,并从每行开头删除 ./。


0

运行:

ndk-build clean

在修改错误的Android.mk之后,否则即使您已经修复了配置,构建仍可能继续失败。

我认为这就是OP在此评论中所指的意思。


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