Как использовать C ++ в Android - PullRequest
0 голосов
/ 13 сентября 2018

У меня здесь два вопроса.

  1. Как скомпилировать исходный файл c ++ в Android?
  2. Как использовать уже созданный файл C ++ .so

Моя среда разработки:

  • Windows 10
  • Android Studio 3.1.4

Q1

У меня есть MyClass.h и MyClass.cpp, которые я хочу использовать в Android. Весь пример, который я видел, - это вызов функции cpp внутри helloJni.cpp. Как мне вызвать другие .cpp / .h файлы в helloJni.cpp ??

Это то, что я создал.

приложение / SRC / главная / CPP / MyClass.h

#ifndef MYCLASS_H_
#define MYCLASS_H_

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

#endif /* MYCLASS_H_ */

приложение / SRC / главная / 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 / главная / 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());
}

приложение / SRC / главная / Java / COM / пример / 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.

Q2

Если у меня уже есть файл .so:

  • Нужно ли мне создавать cpp файл JNI? Я следовал здесь , но там не упоминалось ни о каком файле JNI? потому что .so файл скомпилирован с JNI?
  • Можно ли вызвать .so файл напрямую из Activity? Если можно, то как?
  • Есть ли хорошая статья, которая указывает мне правильное направление?

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

UPDATE

Я собрал библиотеку 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.

1 Ответ

0 голосов
/ 13 сентября 2018

Вы получаете неопределенную ссылку, потому что при компиляции helloJni.cpp, хотя он может найти объявления благодаря заголовкам, определения отсутствуют во время ссылки.Вы можете решить эту проблему, либо связав свою библиотеку HelloJni с библиотекой MyClass, либо сделав оба файла частью одной и той же библиотеки (что я бы порекомендовал).

Что касается вашего дополнительного вопроса, если у вас уже есть .soфайл, вы можете использовать его, если он содержит код JNI, который позволит вызывать как нативный код из класса Java.В противном случае, и если у вас нет контроля над этой библиотекой, вы можете добавить другую библиотеку, которая будет содержать JNI-оболочку вокруг этой существующей библиотеки.Вам нужно не забыть связать эту существующую библиотеку, иначе вы получите ту же ошибку, что и здесь.

Чтобы вызвать код JNI из класса Java, сначала необходимо загрузить библиотеку, содержащую этот код.так же, как вы делаете для своей библиотеки helloJni:

System.loadLibrary("helloJni");

Вы должны сделать это для каждой библиотеки, которую хотите использовать, и убедиться, что библиотеки могут быть найдены во время выполнения (это в основном означает, что они должны быть вВаша папка jniLibs).

Что касается ресурса о том, как использовать собственный код в Android, я бы рекомендовал прочитать Руководство по Android NDK .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...