I have a library in C called mylib in the folder
jniLibs/your_architecture/mylib.so
In Java, to load the library, you just have to type that code in your source:
static {
System.loadLibrary("mylib");
}
But how can you load the library in a native C code (in Android Studio) ?
I add that the library don't use the JNI conventions, it's a normal shared library.
If this can help someone, here how to load a library in native code c/c++ :
1 - to avoid java.lang.UnsatisfiedLinkError: dlopen failed: add this to the build.gradle into android block :
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jniLibs']
}
}
Assuming that your lib files are in
src/main/jniLibs/{Architecture}/
( depending where is your jniLibs folder, in my case it is located at app/src/main/ but the most time it is in app/)
2 - In your CMakeList, add you library as SHARED and IMPORTED by adding the following block :
add_library(mylib SHARED
IMPORTED
)
3 - Add target properties to locate you lib mylib.so by adding the following block :
set_target_properties( mylib PROPERTIES
IMPORTED_LOCATION
${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/mylib.so
)
We back forward (/../) here because we concidere that CMakeLists.txt is in
src/main/cpp/
Otherwise, the most time it is in app/ folder, in this case, we don't need to back forward.
So we have to back to main/ before going into jniLibs/
4 - Add you lib mylib to your target link libraries :
target_link_libraries(native-library-jni
..
..
mylib
..
..
)
5 - Finally, to call methods of your lib mylib.so, you have to create/copy-past the header containing these methods signatures and include it in your source file : #include "mylib.h"
You can now call your methods [namespace::]method(args...)
Extra links :
PROJECT_SOURCE_DIR
CMAKE_SOURCE_DIR
Q : Are CMAKE_SOURCE_DIR and PROJECT_SOURCE_DIR the same in CMake?
Related
I have a simple C++ function compiled into a dylib file that I'm trying to run on an Android phone. The function is super simple, it just adds to numbers and returns the result. However, I keep getting this error:
Another exception was thrown: Invalid argument(s): Failed to load dynamic library 'libadd.dylib': dlopen failed: library "libadd.dylib" not found .
I'm really not sure what I'm doing wrong. I've done the following steps:
My Dart implementation:
import 'dart:ffi' as ffi;
import 'dart:io' show Platform, Directory;
import 'package:path/path.dart' as path;
typedef C_ADD = ffi.Int Function(
ffi.Int a, ffi.Int b); // FFI signature of C function
typedef ADD = int Function(int a, int b);
void linkAndCallFunction() {
var libraryPath = path.join(Directory.current.path, "libadd.dylib");
final dylib = ffi.DynamicLibrary.open(libraryPath);
final ADD add = dylib.lookup<ffi.NativeFunction<C_ADD>>("add").asFunction();
final result = add(40, 2);
print(result);
}
I've added these to the build.gradle files:
build.gradle:
buildscript{
ext{
ndkVersion = "25.1.8937393"
}
...
and app/build.gradle:
android {
ndkVersion rootProject.ext.ndkVersion
externalNativeBuild {
cmake {
path "../../lib/CMakeLists.txt"
}
}
This is my CMakeLists.txt file:
cmake_minimum_required(VERSION 3.10.2)
project(add LANGUAGES CXX C)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
add_library(add SHARED ./add.cpp)
and my file structure of the project looks like this:
lib/
- add.cpp
- add.o
- CMakeLists.txt
- libadd.dylib
- main.dart
it also may be worth mentioning that in order to compile add.cpp into a dylib I ran the following commands:
g++ -c add.cpp
ar rvs libadd.dylib add.o
and if you're wondering, add.cpp looks like this:
#define EXPORT extern "C" __attribute__((visibility("default")))
__attribute__((used))
EXPORT
int add(int a, int b){
return a + b;
}
Where is this error coming from? am I compiling to a dylib incorrectly?
The answer to this problem was relatively simple, it just stemmed from my lack of knowledge on how Android actually compiles to a static library. Hopefully it helps someone else who is trying to understand how to setup external C++ code in a flutter program.
The static library is generated automatically and is set up in CMakeLists.txt.
Firstly, I moved all the C++ files into the android folder. Then, I set up CMakeLists.txt like this:
cmake_minimum_required(VERSION 3.10.2)
add_library( add // library name, will create libadd.so
SHARED
add.cpp
)
The problem before was that I was trying to manually compile the file myself, when I should have been letting CMakeLists do it instead. According to the android documentation:
"The convention CMake uses to name the file of your library is as follows:
liblibrary-name.so
For example, if you specify "native-lib" as the name of your shared library in the build script, CMake creates a file named libnative-lib.so. "
https://developer.android.com/studio/projects/configure-cmake
So when I run the program, the static library is created automatically and placed in the correct place. Then, the Dart FFI can find it with the DynamicLibrary.open() function. In this case, CMakeLists will generate a file called libadd.so, and I can callDynamicLibrary.open('libadd.so') and the program works.
Is there a way to pass a constant at compile time into the externalNativeBuild gradle enclosure, and to be able to use it in code?
For example:
externalNativeBuild {
cmake {
arguments "-DTEST=" + rootProject.ext.value
}
}
And the to be able to use it somehow like next:
void foo() {
int bla = TEST;
....
}
Note that the above code is not working, as TEST is not recognized by the c++ compiler, which is what I am trying to solve.
The easiest solution will be to use add_definitions:
add_definitions(-DTEST=${TEST})
to your CMakeLists.txt file.
Another one would be to use configuration file
Basing on Native sample app from Android Studio:
Create file: config.h.in in the same folder as default native-lib.cpp with following contents:
#ifndef NATIVEAPP1_CONFIG_H_IN
#define NATIVEAPP1_CONFIG_H_IN
#cmakedefine TEST ${TEST}
#endif //NATIVEAPP1_CONFIG_H_IN
In CMakeLists.txt add:
# This sets cmake variable to the one passed from gradle.
# This TEST variable is available only in cmake but can be read in
# config.h.in which is used by cmake to create config.h
set(TEST ${TEST})
configure_file( config.h.in ${CMAKE_BINARY_DIR}/generated/config.h )
include_directories( ${CMAKE_BINARY_DIR}/generated/ )
(add it for example, above add_library call)
Finally, add #include "config.h" in you cod (ex. in native-lib.cpp) to use TEST macro.
Rebuild
I have problem when I try to use C++ library in Android studio project.
I am using statically built Qt 5.14.2 for Android.
I have Qt library project which has only 1 class with this 2 functions:
testclass.h
#include <QDebug>
#include <jni.h>
extern "C"
{
JNIEXPORT void JNICALL Java_com_example_MyTestAndroidApp_LibraryClass_log();
void logNormal();
}
testclass.cpp
JNIEXPORT void JNICALL Java_com_example_MyTestAndroidApp_LibraryClass_log()
{
qDebug() << "---> log from Qt library";
}
void logNormal()
{
qDebug() << "---> log from Qt library";
}
An in the .pro file I am using shared or staticlib to switch from shared(.so) and static(.a) library.
TEMPLATE = lib
CONFIG += shared
#CONFIG += staticlib
CONFIG += c++11
After successfully building the lib (shared and static) I got this files(for ABI x86 but others ABIS have similar sizes too):
libTestLibrary.so ---> 3,359 KB
libTestLibrary.a ---> 7 KB
For the shared (.so) library I was able to successfully call the function from android app.
In Android Studio I am creating new Native C++ project. I have Java JNI LibraryClas which is used to load the library and call the log() function from the library. I placed the library in jniLibs folder.
But for the static(.a) library there are a lot of problems. I tried to call the logNormal() function from the C++ part of the android app(from native-lib.cpp). I am not using JNI like in the shared library case. Here is what I tried:
Create new Native C++ project
Created libs(for the static library) and include(for testclass.h) folders inside app\src\main\cpp
Inside CMakeList.txt I have added this:
add_library(TestLibrary STATIC IMPORTED)
set_target_properties(TestLibrary PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libTestLibrary.a)
target_link_libraries( native-lib TestLibrary ${log-lib} )
And I called logNormal(); from native-lib.cpp.
First I got Error about missing Qt5Core library. After adding it the same way like my library I got errors for others missing libraries and files.
I have this questions:
Why static library is so much smaller then the shared library? (7KB vs 3,359KB) I found this which is opposite from my case:
Can I somehow build my static lib with all libraries and files that it need, so I don't get errors for missing libraries?
Can I use JNI to call C++ functions from static lib? Or when calling C++ functions from static lib it must be done from the C++ part of the android project?
Sorry for the long post and more then 1 question. Any hint or help is welcome. Thank you so much in advance.
I'm building a shared library AAR in Anroid Studio (3.4.2)
the name of the build is app-debug_-debug.aar - the project is called app - how do I change the name of the project to foobar so that my library will be called foobar-debug_-debug.aar
also the binary of library.so file with the is called libjni_lib.so and i'd like to rename that to libbarbaz.so too
how to do this?
To change ethe binary name I had to do 2 things
1) change the BINARY_NAME in the CMakeLists.txt file
cmake_minimum_required(VERSION 3.4.1)
SET(BINARY_NAME "my_new_binary_name")
this is then used in a couple of places
add_library( ${BINARY_NAME}
...
target_link_libraries( ${BINARY_NAME}
2) change the System.loadLibrary to call my new binary name
public class Java
{
static
{
System.loadLibrary ("my_new_binary_name");
}
public native static void initialiseMyAPP (Context appContext);
}
And to change the library name, I renamed the file when I was copying it is a host destination
rename { String fileName ->
fileName.replace("old_library_name.aar", "new_library_name.aar")
}
``
I'm trying to add tracing to my C++ project in Android Studio, I'm quite simply following the example in the docs to create a small profiling library in my app: https://developer.android.com/ndk/reference/group/tracing
I get an 'Unused import statement' message on the "#include " line in Android Studio as well as error: use of undeclared identifier 'ATrace_beginSection' compile errors. My CMakeLists.txt file for the library is:
project(profiling)
if(ANDROID)
include_directories(${ANDROID_SYSROOT}/usr/include)
message(STATUS "Including ${ANDROID_SYSROOT}/usr/include")
endif()
set(profiling_SRCS
profiling.cpp
)
set(profiling_HEADERS
profiling.h
)
add_library(profiling STATIC ${profiling_SRCS} ${profiling_HEADERS})
I've checked the cmake log for the "message" entry above, ANDROID_SYSROOT is indeed pointing to the correct location. The library shows up in the app->cpp section in Android Studio, the CMakeLists.txt file shows up in the External Build Files section, I've tried resyncing Gradle, I've tried cleaning and rebuilding, no dice. My targetSdkVersion is set to 27 in my build.gradle.
What am I missing?
Gaaaah. The problem was the minSdkVersion setting in build.gradle - trace.h has this at the top #if __ANDROID_API__ >= 23. For whatever it's worth, I also don't need to include ${ANDROID_SYSROOT}/usr/include in the CmakeLists.txt for this library... Hopefully this will help someone else in the future.
Your cmake file looks not complete. Try to add the path reference point using ${CMAKE_CURRENT_SOURCE_DIR}. E.g.
cmake_minimum_required(VERSION 3.4.1)
project(profiling)
if(ANDROID)
include_directories(${ANDROID_SYSROOT}/usr/include)
message(STATUS "Including ${ANDROID_SYSROOT}/usr/include")
endif()
set(profiling_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/profiling.cpp
)
set(profiling_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/profiling.h
)
add_library(profiling STATIC ${profiling_SRCS} ${profiling_HEADERS})