Add .so (prebuilt) library from another directory to APK - android

I am trying to include my .so library from another directory. Compiling my project works fine. But when I run it, it gives me
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.company.gimikapp-2/base.apk"],nativeLibraryDirectories=[/data/app/com.company.gimikapp-2/lib/arm, /vendor/lib, /system/lib]]] couldn't find "libtheprebuiltlib.so"
Common solutions I see in SO is this:
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jniLibs']
}
}
Tried both
jniLibs.srcDirs = ['C:\\svn\\sys_libs']
and
jniLibs.srcDirs = ['C:/svn/sys_libs']
How do you actually point it to another directory outside your Android project?
This is my CMakeList.txt:
cmake_minimum_required(VERSION 3.4.1)
add_library( native-lib
SHARED
src/main/cpp/source/native-lib.cpp )
add_library(theprebuiltlib SHARED IMPORTED)
set_target_properties(theprebuiltlib PROPERTIES IMPORTED_LOCATION
C:/svn/sys_libs/libtheprebuiltlib.so)
target_include_directories(
native-lib PRIVATE
src/main/cpp/source/
C:/svn/sys_includes/)
find_library( log-lib
log)
target_link_libraries( native-lib
theprebuiltlib
${log-lib})
And here is my gradle setup for my JNI:
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions"
}
ndk {
abiFilters 'armeabi'
}
}
...
}
...
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
sourceSets {
main {
jniLibs.srcDirs = ['C:/svn/sys_libs']
}
}
}

Apparently, you have NDK r17 installed and Android plugin v.3.1.0 or higher (we don't see this in the published fragment of build.gradle).
But you set abiFilters to armeabi, which has been dropped. You should set it to armeabi-v7a, and make sure that libtheprebuiltlib.so is also built for this ABI, or you can download an older version of NDK and in build.gradle dependencies set
classpath 'com.android.tools.build:gradle:3.0.1'
You can force the latest plugin handle armeabi if you set it explicitly:
android {
defaultConfig {
ndk {
abiFilters 'armeabi'
}
}
}
(in your script, it is under android.defaultConfig.externalNativeBuild.ndk, so has no effect).
One mistake in your build.gradle, it should read
android {
sourceSets {
main {
jniLibs.srcDir 'C:/svn/sys_libs'
}
}
}
when you have the file C:\svn\sys_libs\armeabi\libtheprebuiltlib.so. But this does not explain why cmake does not work as expected.

For NDK r16 and Gradle plugin v3.1.2, here is the step by step solution (thanks to #Alex Cohn).
Set android.sourceSets.main.jniLibs.srcDir to point to the location of external .so
android {
sourceSets {
main {
jniLibs.srcDir 'C:/svn/sys_libs'
}
}
}
Make sure that your external .so is located in the above path (step1), appended by correct architecture. In my case, it is armeabi, thus my .so is in
C:/svn/sys_libs/armeabi/libtheprebuiltlib.so
Move ndk from android.defaultConfig.externalNativeBuild.ndk to android.defaultConfig.ndk like so
android {
defaultConfig {
ndk {
abiFilters 'armeabi'
}
}
}
Make sure that CMakeLists.txt points to the complete path with proper architecture.
set_target_properties(theprebuiltlib PROPERTIES IMPORTED_LOCATION
C:/svn/sys_libs/armeabi/libtheprebuiltlib.so)

Related

How to detect cpu architecture of Android via cmake

Brief:
Does anyone know if it possible to configure Cmake to add sub_directory base on Android ABIs (x86,arm64-v8a)?
Something likes:
if (android_x64)
add_subdirectory(${distribution_DIR}/android_x64)
elseif(android_arm64)
add_subdirectory(${distribution_DIR}/android_arm64)
endif()
More Detail:
My scenario is that for a directory structure such as:
AndroidStudioProject
-CMakeLists.txt
-third_party
-android_x64
CMakeListsA.txt
-android_arm64
CMakeListsB.txt
The CMakeLists.txt (Root Project) looks like this:
cmake_minimum_required(VERSION 3.4.1)
# configure import libs
set(distribution_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/out)
add_subdirectory(${distribution_DIR}/android_x64})
add_subdirectory(${distribution_DIR}/android_arm64)
CMakeListA.txt:
# Generated by gn_to_cmake.py.
cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
cmake_policy(VERSION 3.7)
file(WRITE "/third_party//out/android_x64/empty.cpp")
#//:default
set("target" "default")
set("${target}__cxx_srcs" "/third_party/out/android_x64/empty.cpp")
add_custom_target("${target}" SOURCES ${${target}__cxx_srcs})
add_dependencies("${target}"
"build__chip__java__tests_java_build_test"
"src__app"
"src__app__server")
CMakeListB.txt:
# Generated by gn_to_cmake.py.
cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
cmake_policy(VERSION 3.7)
file(WRITE "/third_party//out/android_arm64/empty.cpp")
#//:default
set("target" "default")
set("${target}__cxx_srcs" "/third_party/out/android_arm64/empty.cpp")
add_custom_target("${target}" SOURCES ${${target}__cxx_srcs})
add_dependencies("${target}"
"build__chip__java__tests_java_build_test"
"src__app"
"src__app__server")
externalNativeBuild in Gradle:
android {
compileSdkVersion 29
ndkVersion '22.1.7171670'
defaultConfig {
// ...
externalNativeBuild {
cmake {
targets "default"
}
}
}
// ...
externalNativeBuild {
cmake {
version '3.10.2'
path '../CMakeLists.txt'
}
}
}
I'm hoping that someone with more experience with native development on Android could help me out. Thanks!

Android Gradle plugin 7.0.0 and NDK: UnsatisfiedLinkError

I recently updated Android Gradle Plugin to version 7.0.0 (Gradle version 7.0.2).
Since I did this update, my native library continues to be compiled regularly, but no .so files are generated in my final apk.
In fact, running the app the exception is thrown:
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/it.Ettore.raspcontroller-2/base.apk"],nativeLibraryDirectories=[/data/app/it.Ettore.raspcontroller-2/lib/x86, /data/app/it.Ettore.raspcontroller-2/base.apk!/lib/x86, /system/lib, /vendor/lib]]] couldn't find "libf-native-lib.so"
By downgrading to Android Gradle Plugin version 4.2.2 (Gradle version 6.7.1), everything works fine.
Could it be an Android Gradle Plugin bug or is it my mistake?
build.gradle :
android {
defaultConfig {
externalNativeBuild {
cmake {
cFlags "-fvisibility=hidden"
cppFlags "-fvisibility=hidden"
}
}
ndk {
moduleName "f-native-lib"
}
sourceSets.main {
jni.srcDirs = ['src/main/c']
}
}
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
ndk {
debugSymbolLevel 'SYMBOL_TABLE'
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
CMakeList.txt:
cmake_minimum_required(VERSION 3.4.1)
add_library( # Sets the name of the library.
f-native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/c/mydir/myfile.c
)
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
)
find_library( # Sets the name of the path variable.
z-lib
# Specifies the name of the NDK library that you want CMake to locate.
z
)
target_link_libraries( # Specifies the target library.
f-native-lib
# Links the target library to the log library included in the NDK.
${log-lib}
${z-lib}
)
Activity:
static {
System.loadLibrary("f-native-lib");
}
Had the same issue, gradle plugin version 7.0.2 fixed this
Are you using tasks.whenTaskAdded in your gradle files? There is an issue with gradle plugin 7.0.0. Refer this. Use 7.0.2 or greater for the resolution.

Create native library in android studio

I have created android studio project with c++ support and installed native development kit and other required tools. How to import another prebuilt native library to include in c++ code. I want to do something like this
I have edited cmakelist.txt as given below and now it show shared library under cpp folder.
cmake_minimum_required(VERSION 3.4.1)
set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../distribution)
add_library(lib_gmath STATIC IMPORTED)
set_target_properties(lib_gmath PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/gmath/lib/${ANDROID_ABI}/libgmath.a)
add_library(lib_gperf SHARED IMPORTED)
set_target_properties(lib_gperf PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/gperf/lib/${ANDROID_ABI}/libgperf.so)
add_library(lib_ocr SHARED IMPORTED)
set_target_properties(lib_ocr PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/ocr/lib/${ANDROID_ABI}/libLPROCR.so)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
add_library(hello-libs SHARED
hello-libs.cpp)
target_include_directories(hello-libs PRIVATE
${distribution_DIR}/gmath/include
${distribution_DIR}/gperf/include)
target_link_libraries(hello-libs
android
lib_gmath
lib_gperf
lib_ocr
log)
But i am not able to include classes from that prebuilt library in my c++ code.
There are few things that I should mention here.
I suggest to switch to gradle+clang for building your code. Android.mk is somehow getting old and it is better to use newer build systems if you are starting new project.
Make sure that your prebuilt library can be used in android. If it is not, you need to build it first. For example I built openssl myself because I could not find a suitable prebuilt library.
In case your are using gradle+cmake, here is a simple CMakeLists.txt file for your prebuilt libraries:
cmake_minimum_required(VERSION 3.4.1)
add_library( core-api
SHARED
src/main/cpp/mysource.c )
# prebuilt libraries
add_library( crypto-lib
STATIC
IMPORTED )
set_target_properties( crypto-lib
PROPERTIES IMPORTED_LOCATION
${CMAKE_CURRENT_SOURCE_DIR}/../prebuilt/crypto/${ANDROID_ABI}/lib/libcrypto.a )
include_directories( src/main/cpp/
../prebuilt/crypto/common/include/
../prebuilt/crypto/${ANDROID_ABI}/include/ )
# other libraries
find_library( log-lib
log )
target_link_libraries( core-api
crypto-lib
${log-lib} )
And this is a gradle file that uses this CMakeLists.txt:
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.sdkVersion
defaultConfig {
minSdkVersion 14
targetSdkVersion 27
externalNativeBuild {
cmake {
arguments "-DANDROID_TOOLCHAIN=clang", "-DANDROID_STL=c++_static"
cFlags "-D__STDC_FORMAT_MACROS", "-fno-integrated-as"
}
}
ndk {
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
}
}
buildTypes {
release {
minifyEnabled true
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
externalNativeBuild {
cmake {
cFlags "-Wall","-DDEBUG"
}
}
debuggable true
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}

Linker command failed with exit code 1 when building ffmpeg static libraries

I'm trying to build ffmpeg static libraries and link them to an android project in order to implement a video player. Goal is to make a player capable of receiving video file from various sources similar to p2p file sharing networks. (target api is 21 level which is 1 level short from supposed official solution with MediaSource)
I managed to compile ffmpeg from this repo (all the code used as-is) but later i got stuck on a linking problem. Whenever I try to compile I get list of ffmpeg methods called in my code and a short eloquent message:
Linker command failed with exit code 1
I have no clue how to pass -v flag to the linker in android studio. Would be great if somebody hinted me that.
I use android studio, build with gradle and cmake.
There's my files:
build.gradle (Project)
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
build.gradle (Module)
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
productFlavors {
x86 {
ndk {
abiFilter "x86"
}
}
arm {
ndk {
abiFilters "armeabi-v7a"
}
}
armv7 {
ndk {
abiFilters "armeabi-v7a"
}
}
}
defaultConfig {
applicationId "com.example.ledo.ndkapplication"
minSdkVersion 22
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions"
arguments '-DANDROID_PLATFORM=android-16'
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
splits {
abi {
enable true
reset()
include 'x86', 'armeabi-v7a'
universalApk true
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.android.support:design:25.3.1'
compile 'com.writingminds:FFmpegAndroid:0.3.2'
testCompile 'junit:junit:4.12'
}
CMakeLists.txt
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 2.8)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
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
src/main/cpp/NativePlayer.h
src/main/cpp/NativePlayer.cpp)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
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 )
find_library(png-lib png)
# 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.
#avcodec
#avfilter
#avformat
#avutil
#swresample
#swscale
#${ANDROID_ABI}
message(${ANDROID_ABI})
set(FFMPEG_ROOT_DIR src/main/libs/ffmpeg/${ANDROID_ABI})
add_library(avcodec STATIC IMPORTED)
add_library(avformat STATIC IMPORTED)
add_library(avfilter STATIC IMPORTED)
add_library(avutil STATIC IMPORTED)
add_library(swresample STATIC IMPORTED)
add_library(swscale STATIC IMPORTED)
#SET_TARGET_PROPERTIES(avcodec PROPERTIES LINKER_LANGUAGE C)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/lib/libavcodec.a)
#SET_TARGET_PROPERTIES(avfilter PROPERTIES LINKER_LANGUAGE C)
set_target_properties(avfilter PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/lib/libavfilter.a)
#SET_TARGET_PROPERTIES(avformat PROPERTIES LINKER_LANGUAGE C)
set_target_properties(avformat PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/lib/libavformat.a)
#SET_TARGET_PROPERTIES(avutil PROPERTIES LINKER_LANGUAGE C)
set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/lib/libavutil.a)
#SET_TARGET_PROPERTIES(swresample PROPERTIES LINKER_LANGUAGE C)
set_target_properties(swresample PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/lib/libswresample.a)
#SET_TARGET_PROPERTIES(swscale PROPERTIES LINKER_LANGUAGE C)
set_target_properties(swscale PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/lib/libswscale.a)
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/include )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/lib )
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib}
avcodec
avformat
#avfilter
#swresample
#swscale
#avutil
GLESv2)
I have .a files in following locations:
%PROJECT_DIR%/app/src/libs/ffmpeg/armeabi-v7a
%PROJECT_DIR%/app/src/libs/ffmpeg/armeabi-v7a-neon
%PROJECT_DIR%/app/src/libs/ffmpeg/x86
It doesn't look to me that linker misses files themselves. (I get different out put if I misplace them.)
The error 'Linker command failed with exit code 1' is usually followed by the more detailed error. To find more details, in Xcode click on the error under Buildtime and choose Reveal in log. This should give you extra hint. Without any specific error, it's not possible to know what's the problem.

Gradle NDK to specify an 'include' directive in generated Android.mk

When you have
android {
defaultConfig {
ndk {
moduleName "yourlib"
stl "stlport_static"
ldLibs "log", "z", "m"
cFlags "-I/some/include/dir/"
}
...
}
...
}
in your build.gradle then Gradle will compile the files in src/main/jni/ and it will generate an Android.mk in build/ndk/debug/Android.mk.
However, in my case, I'm trying to compile some C++ files compiled against OpenCV.
I have this working when I manually create the Android.mk file and run the ndk-build command. But I want to do it via Gradle / Android Studio automatically.
When doing this manually, I include the libraries to link against. I do this, in the manually created Android.mk, with the line:
include /path/to/the/opencv/directory/sdk/native/jni/OpenCV.mk
However, in Android's Gradle plugin, I am unsure of how to add this 'include' directive in the generated Android.mk file.
Can anyone point me in the right Gradle-directive direction to add this line to the generate file? Thanks.
I've found that that the build process will pull everything in from below the ./src/main/jni folder. So, I've placed symlinks there to include and src folders elsewhere - the src files will be enumerated into the .mk file by the build process and the inc files will be scooped up by the compiler. Perhaps its a bit hacky:
android {
defaultConfig {
ndk {
moduleName "yourlib"
cFlags "-std=c99 -I${project.buildDir}/../src/main/jni/inc"
}
...
}
...
}
I also have different cFlags depending upon debug build. This seems to be valid gradle, but doesn't want to build with android-studio. It will build with the gradlew command tho:
android {
defaultConfig {
ndk {
moduleName "yourlib"
cFlags "-std=c99 -I${project.buildDir}/../src/main/jni/inc"
}
...
}
...
buildTypes {
release {
debuggable false
jniDebugBuild false
ndk {
moduleName "yourlib"
}
}
debug {
debuggable true
jniDebugBuild true
ndk {
moduleName "yourlib"
ldLibs "log"
cFlags "-g -D_DEBUG"
}
}
}
}
I hope it can help you (android-studio 0.8.6).

Categories

Resources