简体   繁体   中英

How to use c++ in Android

I have two questions here.

  1. How to compile c++ source file into android?
  2. How to use an already generated c++ .so file

My Dev environment:

  • Windows 10
  • Android Studio 3.1.4

Q1

I have MyClass.h and MyClass.cpp that I want to use in Android. All of the example I have seen is to call the cpp function inside helloJni.cpp. How do i call other .cpp/.h files in helloJni.cpp??

These are what I have created.

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/)

Error when i built in 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

If I already have an .so file:

  • Do i need to create a JNI cpp file? I followed here , but it didnt mentioned of any JNI file? is it because the .so file is compiled with JNI?
  • Can I call .so file directly from Activity? If can, how?
  • Is there a good article that points me in a right direction?

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

UPDATE

I compiled a libCSharedLib.dll library in eclipse with the following files:

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;
}

Updated 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)

I placed Foobar.h into src/main/cpp

foobarJni.cpp (ignore that I didn't return the int value. just testing here)

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

In MainActivity:

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("helloJni");
        System.loadLibrary("foobarJni");
    }
....
}

But I'm still getting the error "undefined reference to 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.

You get an undefined reference because when compiling helloJni.cpp , even though it can find the declarations thanks to the headers, the definitions are missing at link time. You can solve this either by linking your HelloJni library against the MyClass library, or make both files part of the same library (which I would recommend).

As for your followup question, if you already have an .so file, you can use it if it contains JNI code that will make it possible to invoke as native code from a Java class. Otherwise, and if you don't have control over that library, you could add another library which would contain a JNI "wrapper" around this existing library. You need to remember to link against this existing library, or you will get the same error you get here.

In order to call JNI code from a Java class, you first need to load the library that contain that code just as you do for your helloJni lib:

System.loadLibrary("helloJni");

You have to do this for every library you want to use, and make sure the libraries can be found at runtime (this mostly means that they must be in your jniLibs folder).

As for a resource on how to use native code in Android, I'd recommend reading the Android NDK Guide .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM