I want to add another lib into android-ndk hello-libs example.
In CMakeLists.txt, I add:
# this is from the hello-libs sample code
add_library(lib_gperf SHARED IMPORTED)
set_target_properties(lib_gperf PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/gperf/lib/${ANDROID_ABI}/libgperf.so)
########## I add this after the sample code: ###########
add_library(lib_py SHARED IMPORTED)
set_target_properties(lib_py PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/gperf/lib/${ANDROID_ABI}/libpython.so)
And this:
target_link_libraries(
hello-libs
android
lib_gperf
#### this line ######
lib_py
log)
And copy libpython.so in the directory where libgperf.so located:
Also copy the python headers into the include directory:
When I click the run button:
java.lang.UnsatisfiedLinkError: dlopen failed: library "/Users/finn/Downloads/hello-libs/app/src/main/cpp/../../../../distribution/gperf/lib/arm64-v8a/libpython.so" not found
at java.lang.Runtime.loadLibrary0(Runtime.java:1016)
at java.lang.System.loadLibrary(System.java:1657)
at com.example.hellolibs.MainActivity.<clinit>(MainActivity.java:36)
at java.lang.Class.newInstance(Native Method)
at android.app.Instrumentation.newActivity(Instrumentation.java:1174)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2747)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2931)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1620)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:173)
at android.app.ActivityThread.main(ActivityThread.java:6698)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:782)
The path exists in my computer, but why the apk use my computer path, but not the android device path?
And I use the Android device file explorer, the lib is in the directory:
Then how can I make the apk use the right path?
Or I miss something to add?
I encounter the exact same issue and I discovered that when the .so file is built, the library link for the python library is wrong while the link for gperf library is ok.
Link error for python library
Both libraries are imported with the exact same method in the cmake so it doesn't make sense.
I friend of mine told me it is a bug from ninja and they provided me a "turn-around" solution. You have to import the python library as if it was an Android-NDK provided one (like Android or Log).
The library should be put in the NDK libraries, which should be located at <NDK PATH>/toolchains/llvm/prebuilt/<OS_related_folder>/sysroot/usr/lib/<ABI targeted>/<minSdkVersion/
Where:
- NDK path is the location of your NDK folder.
- OS_related_folder is the os_named folder (in my case windows-x86_64).
- ABI targeted is the ABI to which your library is compiled for (arm-linux-androideabi, aarch64-linux-android, etc).
- minSdkVersion is the number of your min SDK version of your project.
This information was found from CMakeCache.txt, in the folder `\app.cxx\cmake\debug\\'. When using find_library for log, the path of the library is shown
Change CMakeLists.txt to only provide library include, and directly link the library by it's name (python2.7 for libpython2.7.so)
cmake_minimum_required(VERSION 3.4.1)
add_library( native-lib SHARED native-lib.cpp)
include_directories( ${CMAKE_CURRENT_LIST_DIR}/../../../libs/python/include/ )
find_library( log-lib log ) # Optional
target_link_libraries( native-lib python2.7 ${log-lib} )
Since the python library isn't natively provided by Android, you will need to pack it to the APK by changing the jnLibs folders (see documentation)
Following these steps should fix the issue
Resulting library link in .so file
Obviously, this is not a good solution. I hope my answer will draw more attention on this issue and somebody will provide a real solution to avoid such tweaks
Assume path /Users/finn/Downloads/hello-libs/app/src/main/cpp/../../../../distribution/gperf/lib is correct, then you can configure your JNI libs like below:
sourceSets {
release {
jniLibs.srcDirs += ["/Users/finn/Downloads/hello-libs/app/src/main/cpp/../../../../distribution/gperf/lib"]
}
debug {
jniLibs.srcDirs += ["/Users/finn/Downloads/hello-libs/app/src/main/cpp/../../../../distribution/gperf/lib"]
}
}
Try to change /Users/finn/Downloads/hello-libs/app/src/main/cpp/../../../../distribution/gperf/lib if it is not your correct path to the jni libs.
Related
I got some C code I have to integrate into my Android project. It depends on a library (.so), and I also have the .h files.
I copied the libs and include directories into the project's cpp package to make them easier to find.
To make it easier I tried starting with the HelloJNI project Android Studio offers and followed the instructions here.
This is the CMakeLists.txt:
#given from HelloJNI
cmake_minimum_required(VERSION 3.4.1)
add_library(hello-jni SHARED
hello-jni.c)
# Include libraries needed for hello-jni lib
target_link_libraries(hello-jni
android
log)
#my own additions now:
add_library( # Specifies the name of the library.
libgdndk
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
IMPORTED)
set_target_properties( # Specifies the target library.
libgdndk
# Specifies the parameter you want to define.
PROPERTIES IMPORTED_LOCATION
# Provides the path to the library you want to import.
${CMAKE_BINARY_DIR}/libs/${ANDROID_ABI}/libgdndk.so )
include_directories(${CMAKE_BINARY_DIR}/inc/)
target_link_libraries( hello-jni libgdndk app-glue ${libgdndk} )
This results in an error:
ninja: error: 'libs/armeabi-v7a/libgdndk.so', needed by 'C:/workspace/android/HelloJNI/app/build/intermediates/cmake/arm7/debug/obj/armeabi-v7a/libhello-jni.so', missing and no known rule to make it
You seem to be linking your imported library to the hello-jni target twice. Also, using ${} in the target_link_libraries() command for the libgdndk library is not necessary. You have already defined a CMake target libgdndk for the library, so you can just do this:
target_link_libraries( hello-jni libgdndk app-glue )
I finally got some help from the publisher of the lib, the (working) result looks quite similar to the code i tried, so maybe it was just the location of the library that was at fault.
add_library( gdndk SHARED IMPORTED )
include_directories( $ENV{userprofile}/AppData/Local/Android/sdk/extras/blackberry/dynamics_sdk/sdk/libs/handheld/gd/inc)
set_target_properties(gdndk PROPERTIES IMPORTED_LOCATION $ENV{userprofile}/AppData/Local/Android/sdk/extras/blackberry/dynamics_sdk/sdk/libs/handheld/gd/libs/${ANDROID_ABI}/libgdndk.so )
target_link_libraries( hello-jni gdndk )
I'm trying to link the OpenSSL .so prebuilt files so I can use it in my native C files. In my project, I've created native_hello.c file and it works fine, the C code runs, but when I'm trying to
System.loadLibrary("crypto");
System.loadLibrary("ssl");
in my java code to load the OpenSSL .so files, the app crashes with error
java.lang.UnsatisfiedLinkError: dlopen failed: library "libcrypto.so" not found
I went ahead and decompiled my source apk file, and sure enough there were no libcrypto.so or libssl.so files in lib folder.
I'm using CMake for linking native files:
CMakeLists.txt
cmake_minimum_required(VERSION 3.6.0)
set(distribution_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/main)
add_library(lib_crypto SHARED IMPORTED)
set_target_properties(lib_crypto PROPERTIES IMPORTED_LOCATION ${distribution_DIR}/jniLibs/${ANDROID_ABI}/lib/libcrypto.so)
# add the open ssl lib
add_library(lib_ssl SHARED IMPORTED)
set_target_properties(lib_ssl PROPERTIES IMPORTED_LOCATION ${distribution_DIR}/jniLibs/${ANDROID_ABI}/lib/libssl.so)
add_library(native-test SHARED
${distribution_DIR}/jni/native_hello.c)
target_include_directories(native-test PRIVATE ${distribution_DIR}/jniLibs/${ANDROID_ABI}/include)
target_link_libraries(native-test
lib_crypto
lib_ssl)
In related questions one of the solutions is to use sourceSets in build.gradle but with the following code snippet the .so files are still missing:
sourceSets {
main {
// let gradle pack the shared library into apk
jniLibs.srcDirs = ['src/main/jniLibs']
}
}
I think that the problem is pretty easy, but I could not figure out how to solve it. Any help would be appreciated
Turned out that structure of my jniLibs was something like:
arm64-v8a
include
...
lib
libcrypto.so
libssl.so
x86
include
...
lib
...
opposed to:
arm64-v8a
include
...
libcrypto.so
libssl.so
x86
include
...
...
Which solves initial problem.
Im using prebuilt android native library: libcrypto.a.
Library is compiled for armeabi, armeabi-v7a and x86.
Structure:
-app
- CMakeLists.txt
- libs
- armeabi
- armeabi-v7a
- x86
Each folder (armeabi, armeabi-v7a and x86) contains folders lib(contains libcrypto.a) and include (contains header files).
CMake code:
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
add_library(crypto STATIC IMPORTED)
set_target_properties(crypto
PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/lib/libcrypto.a)
target_link_libraries( # Specifies the target library.
native-lib
crypto
# Links the target library to the log library
# included in the NDK.
${log-lib})
In my own native-lib i wanna use libcrypto.a. But when i wanna import header file f.e. #include "openssl/md5.h" android studio does not see that file.
And offer me to include one of THREE files from different abis:
"../../../libs/x86/include/openssl/md5.h"
"../../../libs/armeabi/include/openssl/md5.h"
"../../../libs/armeabi-v7a/include/openssl/md5.h"
Is there any way to create one version of my library (native-lib), include only 1 header and let android studio to choose abi automatically?
Smth like:
#include "openssl/md5.h"
but in the same time use 3 abi version.
Or i should use prebuilt included libraries not like that?
#EDIT1
target_include_directories(crypto INTERFACE ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/include)
Not working, getting CMake error.
Before that i have used
target_include_directories(${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/include/)
You need to add an include directory so that the compiler can find openssl/md5.h.
The most idiomatic way to do this would be:
target_include_directories(crypto INTERFACE
${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/include)
Unfortunately, CMake is silly, and doesn't actually let you do this yet on imported library targets, so you have to set the property manually, like so:
set_target_properties(crypto
PROPERTIES
IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/lib/libcrypto.a
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/include
)
That adds that include directory to the interface of crypto, which means it also gets used when building native-lib, as properties which are preceded with INTERFACE are propagated to the users of that target. Using INTERFACE properties is the natural way in CMake to propagate usage requirements to consumers of a library.
Could you please try to one of the codes below:
include_directories("${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/include")
Or
target_include_directories(native-lib "${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/include")
If I'm not wrong, target_include_directories include files to a specific target. Since you are importing the header file in your native-lib files, target_include_directories must aim the native-lib.. and not crypto.. Those header files are not necessary to crypto... but to native-lib.
For any case, you can set include_directories which will add those header files to all targets (native-lib and crypto).
Also, remember:
1 - Edit your CMakeLists.txt
2 - Perform a project sync
3 - Only after that, open your native-lib.cpp file and try to import the file header file again.
As the title suggests I am trying to link a native .so to an android studio project. I have gone through the docs in android developer website and some more articles but unsuccessful in connecting the .so file with the project.
Whenever I try to run the code I get the following error
CMake Error: The following variables are used in this project, but
they are set to NOTFOUND. Please set them or make sure they are set
and tested correctly in the CMake files: testlib
Here is my CMake file
cmake_minimum_required(VERSION 3.4.1)
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
add_library(testlib SHARED IMPORTED)
set_property(TARGET testlib PROPERTY IMPORTED_LOCATION "E:/project/Remote_Native/remote_attempt_1/app/libs/armeabi-v7a/libremotedesktop_client.so")
#find_path(testlib E:/project/Remote_Native/remote_attempt_1/app/libs/armeabi-v7a/RemoteDesktop.h)
find_library(testlib E:/project/Remote_Native/remote_attempt_1/app/libs/armeabi-v7a/libremotedesktop_client.so)
#add_library(remote SHARED IMPORTED)
#set_target_properties(remote PROPERTIES IMPORTED_LOCATION libs/${ANDROID_ABI}/libremotedesktop_client.so )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib}
${testlib})
target_include_directories()
I have four .so files each for arm64, armeabi, armeabi-v7a, x86. I have harcoded the armeabi-v7a lib in the path, android studio is throwing the above mentioned error when I do that. My actual aim is to dynamically load the library based on the chip in the phone. I am pretty sure that my current code is not achieving that.
Here are my queries
How to solve the error that I am getting? I have tried giving both relative and absolute path but to no avail I am getting the same error.
How do I add a .so and a .h file into a native android studio project? That has variations based on the chip on which the code is running?
When I directly add the .h file to the native folder I can reference the classes and functions in that header in my C code but I am unable to run the code. I have a getInstance() method in the .h file. Whenever I call the
getInstance() function it says undefined refernce to getInstance(). What I understand from that is the '.h' file is linked correctly but the definition of the function of the .h files which are actually present in the .so files are not linked. I believe this will be solved if question 1 and 2 are answered.
Is it necessary for all native android projects to have a .mk file? I didn't add one to my project and think it might be one of the cause for the error that I am getting.
You don't need find_library in your case. For log, the library is resolved by NDK for you; for libremotedesktop_client.so, you know the exact path.
Here is the CMakeLists.txt that will work for you:
cmake_minimum_required(VERSION 3.4.1)
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
add_library(remote SHARED IMPORTED)
set_property(TARGET remote PROPERTY IMPORTED_LOCATION "E:/project/Remote_Native/remote_attempt_1/app/libs/${ANDROID_ABI}/libremotedesktop_client.so")
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
log
remote)
Note that using full path (E:/project…) in CMake script is not the best practice; you probably can express the path to this library somehow relative to the path of your CMakeLists.txt, which is ${CMAKE_CURRENT_SOURCE_DIR}.
1-2). First of all, add set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) at the start of your CMakeLists.txt (just after cmake_minimum_required(...)) to allow another search paths for libraries. After this you can find lib just using standard find_library:
find_library(FOO_LIBRARY
foo # Lib's full name in this case - libfoo.so
PATHS path/to/lib/)
if (NOT FOO_LIBRARY)
message(FATAL_ERROR "Foo lib not found!")
endif()
You can use variable ANDROID_ABI to get particular library version, if directory with libraries organised in this way:
- path/to/lib
- armeabi-v7a
- libfoo.so
- arm64-v8a
- libfoo.so
- x86
- libfoo.so
- x86_64
- libfoo.so
So, search path in this case:path/to/lib/${ANDROID_ABI}/
To include headers in project just use include_directories:
include_directories(path/to/headers)
4). .mk files only needed if you use ndk-build (so you don't need any)
I am currently writing JNI code for an Android application to use some legacy native code. I chose to use CMake to give it a try (this is my first time using it).
I would like to serialize a quite complex C structure to JSON format using Jansson library. This would to make it more simple to expose it to Java.
Here is my question: How is it possible to import Jansson in my project as a dependency for my own code ?
I tried to import Jansson in my own sources and use a add_subdirectory clause to make it build. As a result, I can see some intermediates CMake files in the output but no actual compiled file.
Here is what my CMake file looks like :
cmake_minimum_required(VERSION 3.4.1)
include_directories(
src/main/cpp/xxx/Include
)
add_subdirectory("src/main/cpp/jansson")
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
# Associated headers in the same location as their source
# file are automatically included.
src/main/cpp/xxx/file1.c
src/main/cpp/xxx/file2.c
src/main/cpp/xxx/file3.c
src/main/cpp/xxx/file4.c
src/main/cpp/native-lib.cpp )
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
Any help will be welcomed !!
Okay, so I was just missing to declare the dependency of my library on Jansson. This is made by adding its project name to the existing target_link_library clause.
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
jansson
${log-lib} )
In case someone else (well maybe my future self) stumbles on the same problem, here are all the necessary steps to add a native dependency in an Android CMake file:
Find a library already supporting CMake
Import library code in your project (I used git submodule)
Add add_subdirectory clause that points to the folder containing the library CMake file
Add include files to include_directories (hopefully, the imported library set a variable for that, such as JANSSON_INCLUDE_DIRS in my case
Add dependency by adding the library project name to the target_link_libraries clause
Once this is done: poof ! magic ! everything works fine !