android - 如何在 Android 中使用 C++
问题描述
我在这里有两个问题。
- 如何将c++源文件编译成android?
- 如何使用已经生成的 c++ .so 文件
我的开发环境:
- 视窗 10
- 安卓工作室 3.1.4
第一季度
我有我想在 Android 中使用的 MyClass.h 和 MyClass.cpp。我看到的所有示例都是调用 helloJni.cpp 中的 cpp 函数。如何在 helloJni.cpp 中调用其他 .cpp/.h 文件?
这些是我创造的。
应用程序/src/main/cpp/MyClass.h
#ifndef MYCLASS_H_
#define MYCLASS_H_
class MyClass {
public:
MyClass();
virtual ~MyClass();
std::string hello();
};
#endif /* MYCLASS_H_ */
应用程序/src/main/cpp/MyClass.cpp
#include <iostream>
#include "MyClass.h"
#include <string>
using namespace std;
MyClass::MyClass() {
// TODO Auto-generated constructor stub
}
MyClass::~MyClass() {
// TODO Auto-generated destructor stub
}
string MyClass::hello() {
string s = "hello from MyClass";
return s.c_str();
}
应用程序/src/main/cpp/helloJni.cpp
#include <jni.h>
#include <string>
#include "MyClass.h"
extern "C"
JNIEXPORT jstring JNICALL Java_com_example_MainActivity_stringFromHello(
JNIEnv *env,
jobject /* this */) {
MyClass myClass;
return env->NewStringUTF(myClass.hello().c_str());
}
app/src/main/java/com/example/MainActivity.java
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("helloJni");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView jni2 = findViewById(R.id.jni_2);
jni2.setText(stringFromHello());
}
public native String stringFromHello();
}
应用程序/CMakeLists.txt
# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.
cmake_minimum_required(VERSION 3.4.1)
# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add_library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.
add_library( # Specifies the name of the library.
helloJni
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/helloJni.cpp )
add_library( # Specifies the name of the library.
MyClass
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/MyClass.cpp )
# Specifies a path to native header files.
include_directories(src/main/cpp/)
我在 Android Studio 中构建时出错:
Build command failed.
Error while executing process C:\Android\Sdk\cmake\3.6.4111459\bin\cmake.exe with arguments {--build C:SomeDir\android\TestNdk\app\.externalNativeBuild\cmake\debug\x86_64 --target helloJni}
[1/2] Building CXX object CMakeFiles/helloJni.dir/src/main/cpp/helloJni.cpp.o
[2/2] Linking CXX shared library ..\..\..\..\build\intermediates\cmake\debug\obj\x86_64\libhelloJni.so
FAILED: cmd.exe /C "cd . && C:\Android\Sdk\ndk-bundle\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++.exe --target=x86_64-none-linux-android --gcc-toolchain=C:/Android/Sdk/ndk-bundle/toolchains/x86_64-4.9/prebuilt/windows-x86_64 --sysroot=C:/Android/Sdk/ndk-bundle/sysroot -fPIC -isystem C:/Android/Sdk/ndk-bundle/sysroot/usr/include/x86_64-linux-android -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security -std=c++11 -O0 -fno-limit-debug-info -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libatomic.a -nostdlib++ --sysroot C:/Android/Sdk/ndk-bundle/platforms/android-21/arch-x86_64 -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -LC:/Android/Sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/x86_64 -Wl,--no-undefined -Wl,-z,noexecstack -Qunused-arguments -Wl,-z,relro -Wl,-z,now -shared -Wl,-soname,libhelloJni.so -o ..\..\..\..\build\intermediates\cmake\debug\obj\x86_64\libhelloJni.so CMakeFiles/helloJni.dir/src/main/cpp/helloJni.cpp.o -latomic -lm "C:/Android/Sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/x86_64/libc++_static.a" "C:/Android/Sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/x86_64/libc++abi.a" && cd ."
C:SomeDir\android\TestNdk\app\src\main\cpp/helloJni.cpp:18: error: undefined reference to 'MyClass::MyClass()'
C:SomeDir\android\TestNdk\app\src\main\cpp/helloJni.cpp:19: error: undefined reference to 'MyClass::hello()'
C:SomeDir\android\TestNdk\app\src\main\cpp/helloJni.cpp:22: error: undefined reference to 'MyClass::~MyClass()'
C:SomeDir\android\TestNdk\app\src\main\cpp/helloJni.cpp:22: error: undefined reference to 'MyClass::~MyClass()'
clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.
第二季度
如果我已经有一个 .so 文件:
- 我需要创建一个 JNI cpp 文件吗?我跟着here,但它没有提到任何JNI文件?是因为.so文件是用JNI编译的吗?
- 我可以直接从 Activity 调用 .so 文件吗?如果可以,怎么做?
- 是否有一篇好文章可以为我指明正确的方向?
=================================
更新
libCSharedLib.dll
我在 eclipse 中用以下文件编译了一个库:
Foobar.h
#ifndef FOOBAR_H_
#define FOOBAR_H_
class Foobar {
public:
Foobar();
virtual ~Foobar();
int wave();
};
#endif /* FOOBAR_H_ */
Foobar.cpp
#include "Foobar.h"
#include <iostream>
Foobar::Foobar() {
// TODO Auto-generated constructor stub
}
Foobar::~Foobar() {
// TODO Auto-generated destructor stub
}
int Foobar::wave() {
return 1;
}
更新 CMakeList.txt:
# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.
cmake_minimum_required(VERSION 3.4.1)
# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add_library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.
add_library( # Specifies the name of the library.
helloJni
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/helloJni.cpp )
add_library( # Specifies the name of the library.
MyClass
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/MyClass.cpp )
add_library( # Specifies the name of the library.
foobarJni
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/foobarJni.cpp )
add_library(libCSharedLib SHARED IMPORTED)
set_target_properties(libCSharedLib PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libCSharedLib.dll)
# Specifies a path to native header files.
include_directories(src/main/cpp/)
target_link_libraries(helloJni MyClass foobarJni libCSharedLib)
我将 Foobar.h 放入src/main/cpp
foobarJni.cpp (忽略我没有返回 int 值。只是在这里测试)
#include <jni.h>
#include <string>
#include "Foobar.h"
extern "C"
JNIEXPORT jstring JNICALL Java_com_example_MainActivity_stringFoobar(
JNIEnv *env,
jobject /* this */) {
Foobar foobar;
int wave = foobar.wave();
std::string s = "abc";
return env->NewStringUTF(s.c_str());
}
在 MainActivity 中:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("helloJni");
System.loadLibrary("foobarJni");
}
....
}
但我仍然收到错误“未定义对 Foobar 的引用......”
Build command failed.
Error while executing process C:\Android\Sdk\cmake\3.6.4111459\bin\cmake.exe with arguments {--build C:\somedir\TestNdk\app\.externalNativeBuild\cmake\debug\x86_64 --target helloJni}
[1/6] Building CXX object CMakeFiles/foobarJni.dir/src/main/cpp/foobarJni.cpp.o
[2/6] Building CXX object CMakeFiles/MyClass.dir/src/main/cpp/MyClass.cpp.o
[3/6] Linking CXX shared library ..\..\..\..\build\intermediates\cmake\debug\obj\x86_64\libfoobarJni.so
FAILED: cmd.exe /C "cd . && C:\Android\Sdk\ndk-bundle\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++.exe --target=x86_64-none-linux-android --gcc-toolchain=C:/Android/Sdk/ndk-bundle/toolchains/x86_64-4.9/prebuilt/windows-x86_64 --sysroot=C:/Android/Sdk/ndk-bundle/sysroot -fPIC -isystem C:/Android/Sdk/ndk-bundle/sysroot/usr/include/x86_64-linux-android -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security -std=c++11 -O0 -fno-limit-debug-info -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libatomic.a -nostdlib++ --sysroot C:/Android/Sdk/ndk-bundle/platforms/android-21/arch-x86_64 -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -LC:/Android/Sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/x86_64 -Wl,--no-undefined -Wl,-z,noexecstack -Qunused-arguments -Wl,-z,relro -Wl,-z,now -shared -Wl,-soname,libfoobarJni.so -o ..\..\..\..\build\intermediates\cmake\debug\obj\x86_64\libfoobarJni.so CMakeFiles/foobarJni.dir/src/main/cpp/foobarJni.cpp.o -latomic -lm "C:/Android/Sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/x86_64/libc++_static.a" "C:/Android/Sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/x86_64/libc++abi.a" && cd ."
C:\somedir\TestNdk\app\src\main\cpp/foobarJni.cpp:19: error: undefined reference to 'Foobar::Foobar()'
C:\somedir\TestNdk\app\src\main\cpp/foobarJni.cpp:20: error: undefined reference to 'Foobar::wave()'
C:\somedir\TestNdk\app\src\main\cpp/foobarJni.cpp:24: error: undefined reference to 'Foobar::~Foobar()'
C:\somedir\TestNdk\app\src\main\cpp/foobarJni.cpp:24: error: undefined reference to 'Foobar::~Foobar()'
clang++.exe: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.
解决方案
你得到一个未定义的引用,因为在编译时helloJni.cpp
,即使它可以通过标题找到声明,但在链接时缺少定义。您可以通过将 HelloJni 库与 MyClass 库链接来解决此问题,或者将这两个文件作为同一个库的一部分(我会推荐)。
至于你的后续问题,如果你已经有一个 .so 文件,你可以使用它,如果它包含 JNI 代码,可以从 Java 类作为本机代码调用。否则,如果您无法控制该库,则可以添加另一个库,该库将包含围绕该现有库的 JNI“包装器”。你需要记住链接到这个现有的库,否则你会得到与这里相同的错误。
为了从 Java 类调用 JNI 代码,您首先需要加载包含该代码的库,就像加载 helloJni 库一样:
System.loadLibrary("helloJni");
您必须为要使用的每个库执行此操作,并确保可以在运行时找到这些库(这主要意味着它们必须在您的jniLibs
文件夹中)。
至于如何在 Android 中使用本机代码的资源,我建议阅读Android NDK 指南。
推荐阅读
- c# - 我的 c# TCP Sockets 程序被 Windows Defender 隔离
- python - 为具有相似模式的列名创建 matplotlib 子图
- java - 未找到 spring-boot-starter-validation
- python - Python - 到期收益率(金融 - 债券)
- gradle - 如何将 Realm 添加到 Kotlin 多平台库项目的 Android 目标?
- ansible - Ansible 无法运行任何命令或 shell
- brute-force - 使用 Hydra 暴力破解 http 摘要
- javascript - 如何通过dms(discord.js v12)检查用户是否在服务器中具有角色
- microsoft-graph-api - Microsoft Graph API / C# SDK can't find Security Group by Display Name
- c - 如何从字符串中获取最后一个字符