如何在Android中使用C ++

时间:2018-09-13 13:26:53

标签: android c++ cmake android-ndk

我在这里有两个问题。

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

我的开发环境:

  • Windows 10
  • Android Studio 3.1.4

第一季度

我有要在Android中使用的MyClass.h和MyClass.cpp。我看到的所有示例都是在helloJni.cpp中调用cpp函数。如何在helloJni.cpp中调用其他.cpp / .h文件?

这些是我创造的。

app / src / main / cpp / MyClass.h

#ifndef MYCLASS_H_
#define MYCLASS_H_

class MyClass {
    public:
        MyClass();
        virtual ~MyClass();
        std::string hello();
};

#endif /* MYCLASS_H_ */

app / 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();
}

app / 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();

}

app / 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文件吗?如果可以,怎么办?
  • 是否有一篇很好的文章指出了我正确的方向?

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

更新

我使用以下文件在Eclipse中编译了libCSharedLib.dll库:

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.

1 个答案:

答案 0 :(得分:2)

您会得到未定义的引用,因为在编译helloJni.cpp时,即使由于头文件而可以找到声明,链接时也会丢失定义。您可以通过将HelloJni库与MyClass库链接来解决此问题,或者将两个文件都放在同一个库中(我建议这样做)。

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

要从Java类中调用JNI代码,首先需要像对helloJni lib一样加载包含该代码的库:

System.loadLibrary("helloJni");

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

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