首页 > 解决方案 > 如何在 Android 中使用 C++

问题描述

我在这里有两个问题。

  1. 如何将c++源文件编译成android?
  2. 如何使用已经生成的 c++ .so 文件

我的开发环境:

第一季度

我有我想在 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 文件:

=================================

更新

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

foob​​arJni.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.

标签: androidc++cmakeandroid-ndk

解决方案


你得到一个未定义的引用,因为在编译时helloJni.cpp,即使它可以通过标题找到声明,但在链接时缺少定义。您可以通过将 HelloJni 库与 MyClass 库链接来解决此问题,或者将这两个文件作为同一个库的一部分(我会推荐)。

至于你的后续问题,如果你已经有一个 .so 文件,你可以使用它,如果它包含 JNI 代码,可以从 Java 类作为本机代码调用。否则,如果您无法控制该库,则可以添加另一个库,该库将包含围绕该现有库的 JNI“包装器”。你需要记住链接到这个现有的库,否则你会得到与这里相同的错误。

为了从 Java 类调用 JNI 代码,您首先需要加载包含该代码的库,就像加载 helloJni 库一样:

System.loadLibrary("helloJni");

您必须为要使用的每个库执行此操作,并确保可以在运行时找到这些库(这主要意味着它们必须在您的jniLibs文件夹中)。

至于如何在 Android 中使用本机代码的资源,我建议阅读Android NDK 指南


推荐阅读