I would like to know how to build my APK so that my libraries are installed/copied under /data/data/<package name>/lib when installing the APK.
I am using Android Studio 4.0.1
I build an APK and confirm when I extract it that I have a
lib/arm64-v8a folder that contains my jni libs.
I want that after I install my APK, some other applications on the same device can access the libs that I have in the lib/arm64-v8a. The /data/data/<package name> folder seems to be the right place for this, hence my question; if there is another approach for making my libraries accessible on device, I am open to it.
I set android.bundle.enableUncompressedNativeLibs=false
in my gradle.properties file based on this discussion even
though I didn't see any effect using it with true or false.
I set android:extractNativeLibs="true" in my Manifest
However, when I adb install my apk, the /data/data/<package name>/ folder on my device only contains a cache and code_cache folder, both of which are empty.
I do see under /data/app/ a folder with my libraries but they include strings that are randomly set each time I install the APK:
/data/app/~~sFE8-eNknFAkhKJ-1S03lg==/<package name>-rsDLNgpi4vult7yVBrPNOQ==/
base.apk
lib/arm64/
<my .so in there>
These random strings prevent me from accessing reliably my libraries from any other application. (If there is a way to not have these random strings as part of the name and /data/app is the recommended location for my libraries to be shared with other applications, I am interested in that alternate approach.)
I have also installed other prebuilt APK and with these, I do see the libraries under /data/data/<package name>/lib like I want. This to me indicates that my issue is related to how I build my APK with AS 4.0.1, not to my device, its Android version, or how I install the APK.
Here is my gradle file:
import java.util.regex.Matcher
import java.util.regex.Pattern
apply plugin: 'com.android.application'
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
lintOptions {
checkReleaseBuilds false
abortOnError false
}
sourceSets.main {
jniLibs.srcDirs = ['libs']
jni.srcDirs = []
}
defaultConfig {
applicationId "com.example.myexample"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
version "3.17.0"
cppFlags ""
arguments "-DANDROID_STL=c++_shared"
}
}
ndk {
moduleName "myexample"
abiFilter "arm64-v8a"
ldLibs "log"
}
packagingOptions {
exclude "lib/arm64-v8a/libassets.so"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
version "3.17.0"
path "src/main/cpp/CMakeLists.txt"
// There can be only one CMAKE per project: https://developer.android.com/studio/projects/gradle-external-native-builds
}
}
}
task copyFiles(type: Copy) {
description = 'Testing a manual copy operation (confirmed it works)...'
from file("../dsp/external/ship/libmyexample_skel.so")
into file("$buildDir/res/raw/")
}
project.afterEvaluate {
preBuild.dependsOn copyFiles
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
Something else I tried: I created manually a src/main/jniLibs.arm64-v8a folder in which I place a .so. That .so appears in the my apk when I extract it, but my /data/data/<package name>/ folder doesn't have a lib nevertheless.
I think you need to add this code to add libs in the appropriate folder like /data/data/<package name>/lib
add this to app's build.gradle file into android{} tag
sourceSets.main {
jniLibs.srcDirs = ['libs']
jni.srcDirs = []
}
this to CMakeList.txt file
cmake_minimum_required(VERSION 3.4.1)
find_library( # Sets the name of the path variable.
log-lib
log)
set(distribution_DIR ${CMAKE_SOURCE_DIR}/libs)
add_library(yourLibName
SHARED
...//list of your c/c++ files here from jni folder
)
target_link_libraries(yourLibName ${log-lib})
as per developer.android.com
Note: When making changes to the Gradle configuration, make sure to
apply your changes by clicking Sync Project in the toolbar.
Additionally, when making changes to your CMake or ndk-build script
file after you have already linked it to Gradle, you should sync
Android Studio with your changes by selecting Build > Refresh Linked
C++ Projects from the menu bar.
I read many threads how to add a *.so library to Android Studio, but none of them works, especially when it comes to the point of text: This does not work with the newer xxx (Android Studio, gradle, ...)
Can we make a fresh start please. I got:
Android Studio 0.6.0
From Project Structure I see:
SDK Location:
/usr/share/android-studio/data/sdk
/usr/lib/jvm/default-java
Project:
Gradle version 1.10
Android Plugin Version 0.11.+
Modules/app:
Properties:
Compile Sdk Version 19
Build Tools Version 19.1.0
Dependencies:
{dir=libs, include=[*.jar]} Compile
{dir=libs, include=[*.so]} Provided
m com.android.support: appcompat -v7:19.+ Compile
I got the *.so files pre-compiled and at the demo app they are working. I have to change the source code of the app, so I need to rebuild with the same *.so files.
Adding .so Library in Android Studio 1.0.2
Create Folder "jniLibs" inside "src/main/"
Put all your .so libraries inside "src/main/jniLibs" folder
Folder structure looks like,
|--app:
|--|--src:
|--|--|--main
|--|--|--|--jniLibs
|--|--|--|--|--armeabi
|--|--|--|--|--|--.so Files
|--|--|--|--|--x86
|--|--|--|--|--|--.so Files
No extra code requires just sync your project and run your application.
Reference
https://github.com/commonsguy/sqlcipher-gradle/tree/master/src/main
Current Solution
Create the folder project/app/src/main/jniLibs, and then put your *.so files within their abi folders in that location. E.g.,
project/
├──libs/
| └── *.jar <-- if your library has jar files, they go here
├──src/
└── main/
├── AndroidManifest.xml
├── java/
└── jniLibs/
├── arm64-v8a/ <-- ARM 64bit
│ └── yourlib.so
├── armeabi-v7a/ <-- ARM 32bit
│ └── yourlib.so
└── x86/ <-- Intel 32bit
└── yourlib.so
Deprecated solution
Add both code snippets in your module gradle.build file as a dependency:
compile fileTree(dir: "$buildDir/native-libs", include: 'native-libs.jar')
How to create this custom jar:
task nativeLibsToJar(type: Jar, description: 'create a jar archive of the native libs') {
destinationDir file("$buildDir/native-libs")
baseName 'native-libs'
from fileTree(dir: 'libs', include: '**/*.so')
into 'lib/'
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn(nativeLibsToJar)
}
Same answer can also be found in related question: Include .so library in apk in android studio
Solution 1 : Creation of a JniLibs folder
Create a folder called “jniLibs” into your app and the folders containing your *.so inside.
The "jniLibs" folder needs to be created in the same folder as your "Java" or "Assets" folders.
Solution 2 : Modification of the build.gradle file
If you don’t want to create a new folder and keep your *.so files into the libs folder, it is possible !
In that case, just add your *.so files into the libs folder (please respect the same architecture as the solution 1 : libs/armeabi/.so for instance) and modify the build.gradle file of your app to add the source directory of the jniLibs.
sourceSets {
main {
jniLibs.srcDirs = ["libs"]
}
}
You will have more explanations, with screenshots to help you here ( Step 6 ):
http://blog.guillaumeagis.eu/setup-andengine-with-android-studio/
EDIT It had to be jniLibs.srcDirs, not jni.srcDirs - edited the code. The directory can be a [relative] path that points outside of the project directory.
*.so library in Android Studio
You have to generate jniLibs folder inside main in android Studio projects and put your all .so files inside. You can also integrate this line in build.gradle
compile fileTree(dir: 'libs', include: ['.jar','.so'])
It's work perfectly
|--app:
|--|--src:
|--|--|--main
|--|--|--|--jniLibs
|--|--|--|--|--armeabi
|--|--|--|--|--|--.so Files
This is the project structure.
This is my build.gradle file, Please note the line
jniLibs.srcDirs = ['libs']
This will include libs's *.so file to apk.
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
// Move the tests to tests/java, tests/res, etc...
instrumentTest.setRoot('tests')
// Move the build types to build-types/<type>
// For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
// This moves them out of them default location under src/<type>/... which would
// conflict with src/ being used by the main source set.
// Adding new build types or product flavors should be accompanied
// by a similar customization.
debug.setRoot('build-types/debug')
release.setRoot('build-types/release')
}
Android NDK official hello-libs CMake example
https://github.com/googlesamples/android-ndk/tree/840858984e1bb8a7fab37c1b7c571efbe7d6eb75/hello-libs
Just worked for me on Ubuntu 17.10 host, Android Studio 3, Android SDK 26, so I strongly recommend that you base your project on it.
The shared library is called libgperf, the key code parts are:
hello-libs/app/src/main/cpp/CMakeLists.txt:
// -L
add_library(lib_gperf SHARED IMPORTED)
set_target_properties(lib_gperf PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/gperf/lib/${ANDROID_ABI}/libgperf.so)
// -I
target_include_directories(hello-libs PRIVATE
${distribution_DIR}/gperf/include)
// -lgperf
target_link_libraries(hello-libs
lib_gperf)
app/build.gradle:
android {
sourceSets {
main {
// let gradle pack the shared library into apk
jniLibs.srcDirs = ['../distribution/gperf/lib']
Then, if you look under /data/app on the device, libgperf.so will be there as well.
on C++ code, use: #include <gperf.h>
header location: hello-libs/distribution/gperf/include/gperf.h
lib location: distribution/gperf/lib/arm64-v8a/libgperf.so
If you only support some architectures, see: Gradle Build NDK target only ARM
The example git tracks the prebuilt shared libraries, but it also contains the build system to actually build them as well: https://github.com/googlesamples/android-ndk/tree/840858984e1bb8a7fab37c1b7c571efbe7d6eb75/hello-libs/gen-libs
I have solved a similar problem using external native lib dependencies that are packaged inside of jar files. Sometimes these architecture dependend libraries are packaged alltogether inside one jar, sometimes they are split up into several jar files. so i wrote some buildscript to scan the jar dependencies for native libs and sort them into the correct android lib folders. Additionally this also provides a way to download dependencies that not found in maven repos which is currently usefull to get JNA working on android because not all native jars are published in public maven repos.
android {
compileSdkVersion 23
buildToolsVersion '24.0.0'
lintOptions {
abortOnError false
}
defaultConfig {
applicationId "myappid"
minSdkVersion 17
targetSdkVersion 23
versionCode 1
versionName "1.0.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
jniLibs.srcDirs = ["src/main/jniLibs", "$buildDir/native-libs"]
}
}
}
def urlFile = { url, name ->
File file = new File("$buildDir/download/${name}.jar")
file.parentFile.mkdirs()
if (!file.exists()) {
new URL(url).withInputStream { downloadStream ->
file.withOutputStream { fileOut ->
fileOut << downloadStream
}
}
}
files(file.absolutePath)
}
dependencies {
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:design:23.3.0'
compile 'net.java.dev.jna:jna:4.2.0'
compile urlFile('https://github.com/java-native-access/jna/blob/4.2.2/lib/native/android-arm.jar?raw=true', 'jna-android-arm')
compile urlFile('https://github.com/java-native-access/jna/blob/4.2.2/lib/native/android-armv7.jar?raw=true', 'jna-android-armv7')
compile urlFile('https://github.com/java-native-access/jna/blob/4.2.2/lib/native/android-aarch64.jar?raw=true', 'jna-android-aarch64')
compile urlFile('https://github.com/java-native-access/jna/blob/4.2.2/lib/native/android-x86.jar?raw=true', 'jna-android-x86')
compile urlFile('https://github.com/java-native-access/jna/blob/4.2.2/lib/native/android-x86-64.jar?raw=true', 'jna-android-x86_64')
compile urlFile('https://github.com/java-native-access/jna/blob/4.2.2/lib/native/android-mips.jar?raw=true', 'jna-android-mips')
compile urlFile('https://github.com/java-native-access/jna/blob/4.2.2/lib/native/android-mips64.jar?raw=true', 'jna-android-mips64')
}
def safeCopy = { src, dst ->
File fdst = new File(dst)
fdst.parentFile.mkdirs()
fdst.bytes = new File(src).bytes
}
def archFromName = { name ->
switch (name) {
case ~/.*android-(x86-64|x86_64|amd64).*/:
return "x86_64"
case ~/.*android-(i386|i686|x86).*/:
return "x86"
case ~/.*android-(arm64|aarch64).*/:
return "arm64-v8a"
case ~/.*android-(armhf|armv7|arm-v7|armeabi-v7).*/:
return "armeabi-v7a"
case ~/.*android-(arm).*/:
return "armeabi"
case ~/.*android-(mips).*/:
return "mips"
case ~/.*android-(mips64).*/:
return "mips64"
default:
return null
}
}
task extractNatives << {
project.configurations.compile.each { dep ->
println "Scanning ${dep.name} for native libs"
if (!dep.name.endsWith(".jar"))
return
zipTree(dep).visit { zDetail ->
if (!zDetail.name.endsWith(".so"))
return
print "\tFound ${zDetail.name}"
String arch = archFromName(zDetail.toString())
if(arch != null){
println " -> $arch"
safeCopy(zDetail.file.absolutePath,
"$buildDir/native-libs/$arch/${zDetail.file.name}")
} else {
println " -> No valid arch"
}
}
}
}
preBuild.dependsOn(['extractNatives'])
I have android studio 4.1.2 and when I tried to set the ndk path in File -> Project Structure -> Sdk Location it would ask me to download the ndk even though it was already installed, I set the NDK_HOME, ANDROID_NDK_HOME and PATH environmental variables and nothing. The only thing that worked for me was manually setting the ndk version in the local.properties, creating a jniLibs (yes with this exact name) folder with the ABIs and their respective .so files and specifying the abi filters and ndk version in the build.gradle. Hope this saves someone else from a headache.
local.properties
ndk.dir=C\:\\Users\\<user>\\AppData\\Local\\Android\\Sdk\\ndk\\22.0.7026061
build.gradle
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.example.myapplication"
minSdkVersion 22
targetSdkVersion 30
versionCode 1
versionName "1.0"
ndk.abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
sourceSets {
main {
jniLibs.srcDir 'jniLibs'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
ndkVersion '22.0.7026061'
}
...\app\src\main\
Inside the jniLibs folder
To use native-library (so files)
You need to add some codes in the "build.gradle" file.
This code is for cleaing "armeabi" directory and copying 'so' files into "armeabi" while 'clean project'.
task copyJniLibs(type: Copy) {
from 'libs/armeabi'
into 'src/main/jniLibs/armeabi'
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn(copyJniLibs)
}
clean.dependsOn 'cleanCopyJniLibs'
I've been referred from the below.
https://gist.github.com/pocmo/6461138
I have a project that uses the non-experimental version of the Android Gradle plugin (com.android.tools.build:gradle:2.1.3). I have some native code that I build into a shared library libnative.so.
The problem is that now I want to link libnative.so against a prebuilt library. Let's call this file libfoo.so. If I set the jniLibs.srcDir to the folder containing libfoo.so for the different ABIs I see that it gets bundled into my APK.
However I don't understand how I am supposed to link against it successfully. If I add "foo" to ldLibs I get a linker error saying that it can't find the library. This is probably because the folder containing libfoo.so is not included as a search path to the linker.
How am I supposed to do this? Is it simply not possible without switching to the experimental gradle plugin?
This is my gradle file. I stripped away the part that probably is irrelevant for this problem:
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "com.foo.bar"
minSdkVersion 15
targetSdkVersion 23
versionCode 11
versionName "0.0.11"
def foodir = "${project.buildDir}/../../foo"
ndk {
moduleName "native"
ldLibs "foo"
stl "gnustl_static"
}
sourceSets {
main {
jniLibs.srcDirs = ["${foodir}"]
}
}
}
}
I've inherited a really messy android project with a lot of NDK dependencies and having a lot of problems with getting gradle to correctly link and include all .so and .a files into the resulting apk.
The project consists of some java code that sets up some activities and call into a big NDK library, built from C++ which in turn links with a dosens of 3rd party library (prebuilt or built from source).
I have managed to make it build with the latest gradle experimental plugin, but for some reason, my module isn't included in the apk while my 3rd party .so files are even though I can see that gradle have built my module into a .so file which it have placed in the build directory.
My build.gradle looks like this:
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion = 23
buildToolsVersion = "22.0.1"
defaultConfig.with {
applicationId = "<removed>"
minSdkVersion.apiLevel = 7
targetSdkVersion.apiLevel = 23
versionCode = 1
versionName = "1.0"
}
ndk {
moduleName = "XM"
CFlags.add("-I${file("src/main/jni")}".toString())
cppFlags.addAll(["-I${file("../../3rd_part/android/osg")}".toString()])
cppFlags.addAll(["-I${file("../../3rd_part/android/opus")}".toString()])
ldFlags.add("-L${file("src/main/jniLibs/armeabi-v7a")}".toString())
ldLibs.addAll(["osgdb_jpeg", "osgdb_freetype", "jpeg","argsub_es", "jnigraphics", "android", "log"])
stl = "gnustl_shared"
abiFilters.add("armeabi-v7a")
}
sources {
main {
jni {
source {
srcDir "../../core"
srcDir "src/main/jni"
}
}
}
}
lintOptions.abortOnError = false
}
android.buildTypes {
release {
minifyEnabled = false
proguardFiles.add(file('proguard-rules.txt'))
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:support-v4:23.0.0'
compile 'com.android.support:appcompat-v7:23.0.0'
compile 'com.android.support:design:23.0.0'
compile 'com.wunderlist:sliding-layer:1.1.1'
}
So to sum up my question: why does gradle build my module (libXM.so), place it into build/libs/xM/shared/armeabi-v7a/debug, but not include it into my final apk file?
I didn't get several things: if you want to build a shared library with gradle, then you have to use apply plugin: 'com.android.model.library' and not application
If you want to build an application and use prebuilt library, then you have to write something like this:
sources {
main {
jni {
source {
srcDir "../../core"
srcDir "src/main/jni"
}
dependencies{
library "XM" linkage "shared"
}
And, of course, to set ldFlags previously.
I built some test c and c++ programs using gradle following these examples
I was also able to setup android studio with the experimental features to build a android project calling native functions with the help of this answer
I know that I can build this project using the android.useDeprecatedNdk=true
and provide my android.mk file for android studio to compile and build the app.
Is it possible with the experimental feature to build this same program? This project is built with CMAKE and there are some compilation steps that output files that needs to link. ndk-build creates these files but I can't find a way to do it with with android experimental features.
I am attempting for testing purposes and to build a non trivial example of building a more complex project. I decided to try to build the zlib library.
Here is my build.gradle
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion = 23
buildToolsVersion = "23.0.1"
defaultConfig.with {
applicationId = "me.test.testnative_exp"
minSdkVersion.apiLevel = 10
targetSdkVersion.apiLevel = 23
versionCode = 1
versionName = "1.0"
buildConfigFields.with {
create() {
type = "int"
name = "VALUE"
value = "1"
}
}
}
}
compileOptions.with {
sourceCompatibility=JavaVersion.VERSION_1_7
targetCompatibility=JavaVersion.VERSION_1_7
}
android.ndk {
moduleName = "hello-jni"
cppFlags += "-I${file("src/main/jni/zlib")}".toString()
stl = "stlport_static"
}
android.buildTypes {
release {
minifyEnabled = false
proguardFiles += file('proguard-rules.pro')
}
}
android.sources {
main {
java {
source {
srcDir 'src'
}
}
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.android.support:design:23.0.1'
}
my project structure is
app/
build/
libs/
src/
src/main/testActivity.java
jni/zlib <- the zlib folder is the one that I downloaded from the zlib website.
jni/hello-jni.c
After syncing the build.gradle I get a link error on the header in file:
infbak9.c cannot find #include "zutil.h"
but both files are in the zlib directory under jni.
Is it possible to use the android studio experimental ndk build to compile this project?
there is good project in github that i use its structure in mine, it is nice example for start on "how use experimental(v_0.4.0) plugin in complex project?" :
https://github.com/frogermcs/FlatBuffs
*there are zlib in android NDK, just add appropriate cflag and ldLibs.add("z") in gradle. and in your native code include <zlib.h>
and a good experimental gradle config from google :
https://github.com/googlesamples/android-ndk/blob/229cbe86238d401bb06166b8dfadec8198532589/native-codec/app/build.gradle
you might check this repo for a group of samples:
https://github.com/googlesamples/android-ndk
your issue might be able to fixed by adding something as[from Teapot sample there]:
cppFlags.addAll(['-I' + "${ndkDir}/sources/android/cpufeatures",'-I' + file('src/main/jni/ndk_helper')])
just replace the directory with your own path "zutil.h" etc. you may also use the latest gradle-experimental version to simplify your script -- like removing ".with" for defaultConfig.with, delete java 7 version section( the latest android studio could handle java 8 ), those ".toString()" thing is also wordy so the samples have changed style to use "+" [those is not problem for your build error though]