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")
}
``
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 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?
Starting from an empty Qt for Android project, created with Qt Creator, how do I execute native Java code?
For example, if I have this java method:
class HelloJava {
public static String sayHello(int a, int b) {
return "Hello from Java, a+b=" + (a+b);
}
}
And I have this Qt method which should call the HelloJava.sayHello() method:
QString call_HelloJava_sayHello(int a, int b) {
// What goes in here?
}
Where do I put the Java code in my project, and what goes inside the call_HelloJava_sayHello() method?
The Qt doc is pretty verbose on the subject:
// Java class
package org.qtproject.qt5;
class TestClass
{
static String fromNumber(int x) { ... }
static String[] stringArray(String s1, String s2) { ... }
}
// C++ code
// The signature for the first function is "(I)Ljava/lang/String;"
QAndroidJniObject stringNumber = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/TestClass",
"fromNumber"
"(I)Ljava/lang/String;",
10);
// the signature for the second function is "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"
QAndroidJniObject string1 = QAndroidJniObject::fromString("String1");
QAndroidJniObject string2 = QAndroidJniObject::fromString("String2");
QAndroidJniObject stringArray = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/TestClass",
"stringArray"
"(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"
string1.object<jstring>(),
string2.object<jstring>());
So the sig of your function should be "(I;I;)Ljava/lang/String;"
And also this:
ANDROID_PACKAGE_SOURCE_DIR: This variable can be used to specify a
directory where additions and modifications can be made to the default
Android package template. The androiddeployqt tool will copy the
application template from Qt into the build directory, and then it
will copy the contents of the ANDROID_PACKAGE_SOURCE_DIR on top of
this, overwriting any existing files. The update step where parts of
the source files are modified automatically to reflect your other
settings is then run on the resulting merged package. If you, for
instance, want to make a custom AndroidManifest.xml for your
application, then place this directly into the folder specified in
this variable. You can also add custom Java files in
ANDROID_PACKAGE_SOURCE_DIR/src.
Note: When adding custom versions of the build files (like
strings.xml, libs.xml, AndroidManifest.xml, etc.) to your project,
make sure you copy them from the package template, which is located in
$QT/src/android/java. You should never copy any files from the build
directory, as these files have been altered to match the current build
settings.
So it seems you need to specify that and the java files are automatically deployed.
Just add this to your .pro file (assuming your java sources are placed at /path/to/project/myjava/src:
android {
ANDROID_PACKAGE_SOURCE_DIR=$$_PRO_FILE_PWD_/android
QT += androidextras
}
_PRO_FILE_PWD_ is a qmake variable that will resolve to the project directory.