How to change libs directory in Gradle? - android

I want to integrate Zbar into my application but cant seem to figure out how to accomplish this using the new Android Studio.
I have looked through the example and have copied over the code without any issues. The problem I am having is adding the libs to my project I cant seem to figure out how to do it. Could someone walk me through it?

I'm not particularly familiar with IntelliJ or Gradle but I have figured it out. I used ZBarAndroidSDK-0.2.
Copy the contents of the ZBar SDK libs/ folder into your project's libs/ folder.
Modify your build.gradle (see below) to make sure the jar and native libs are included in your APK.
To make IntelliJ aware of ZBar, add zbar.jar in your project structure. To do this, go to File > Project Structure > Libraries > + Sign > Java and find zbar.jar with the file picker. Add it to your project.
Add the following to your build.gradle (making sure to keep whatever other dependencies you've got):
dependencies {
compile files('libs/android-support-v4.jar')
compile files('libs/zbar.jar')
}
task copyNativeLibs(type: Copy) {
from(new File('libs')) { include '**' }
into new File(buildDir, 'native-libs')
}
tasks.withType(Compile) { compileTask -> compileTask.dependsOn copyNativeLibs }
clean.dependsOn 'cleanCopyNativeLibs'
tasks.withType(com.android.build.gradle.tasks.PackageApplication) { pkgTask ->
pkgTask.jniDir new File(buildDir, 'native-libs')
}
My build.gradle is based on this gist: https://gist.github.com/khernyo/4226923.

Actually, #Michael's answer is correct, it is also obsolete. Now, using gradle all you need to do is to add the lines below in the build.gradle file:
android {
...
sourceSets {
main.jniLibs.srcDirs = ['libs']
test.jniLibs.srcDirs = ['libs']
}
}
or directly put your .so libraries into:
src/main/jniLibs
This way, when you build your application or library, the jni libraries are being copied into destination .jar/.aar file.

If your using gradle 1.1.0 then you must do some modifications to #Michael's answer.
Here is the revised code of gradle file which works for me.
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.0.0'
compile files('libs/zbar.jar')
}
task copyNativeLibs(type: Copy) {
from(new File('libs')) { include '**' }
into new File(buildDir, 'native-libs')
}
tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn copyNativeLibs }
clean.dependsOn 'cleanCopyNativeLibs'
tasks.withType(com.android.build.gradle.tasks.PackageApplication) { pkgTask ->
pkgTask.jniFolders = new HashSet<File>()
pkgTask.jniFolders.add(new File(buildDir, 'native-libs'))
}

I've built ZBarAndroidSDK-0.2 example in Android Studio 2.0 by just opening CameraTest project from example folder.
It restructured the project from eclipse to Android Studio automatically. That is it.

Related

How to generate javadoc for android library when it has dependencies which are also aar libraries?

I have android library project which depends on other android library projects. I need to generate javadoc for library but it fails because gradle puts to javadoc classpath path to .aar locations but javadoc expects .jar files.
simplified gradle file:
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
configurations {
javadocDeps
}
defaultConfig {
minSdkVersion 7
targetSdkVersion 23
versionCode 1
versionName "0.1.0"
}
}
dependencies {
compile 'com.android.support:support-v4:23.2.0'
compile 'com.android.support:appcompat-v7:23.2.0'
compile 'com.nineoldandroids:library:2.4.0'
compile 'com.annimon:stream:1.0.7'
javadocDeps 'com.android.support:support-annotations:23.2.0'
javadocDeps 'com.nineoldandroids:library:2.4.0'
javadocDeps 'com.android.support:support-v4:23.2.0'
}
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
task javadoc(type: Javadoc, dependsOn: explodeAars) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
classpath += configurations.javadocDeps
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives javadocJar
archives sourcesJar
}
3 solutions possible:
1) somehow to add to the classpath path classes.jar from every aar library it depends build/intermidiates/exploded-aar/library/version/jars/classes.jar
I don't know how to include these paths in javadoc task.
2) manually unpack classes.jar from aar file and add them to classpath of javadoc task
3) very dirty hack - hardcoded paths to library - but I think this is so WRONG.
How to achieve 1 or 2 with gradle dsl?
I managed to automate the solution of Guillaume Perrot by extracting the classes.jar contained in each AAR file, and adding it to the classpath of the javadoc task.
It seems to work for AAR dependencies and AAR modules on Android Studio 2.3 and Gradle 3.3
import java.nio.file.Files
import java.nio.file.Paths
import java.io.FileOutputStream
import java.util.zip.ZipFile
task javadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += configurations.compile
classpath += configurations.provided
afterEvaluate {
// Wait after evaluation to add the android classpath
// to avoid "buildToolsVersion is not specified" error
classpath += files(android.getBootClasspath())
// Process AAR dependencies
def aarDependencies = classpath.filter { it.name.endsWith('.aar') }
classpath -= aarDependencies
aarDependencies.each { aar ->
// Extract classes.jar from the AAR dependency, and add it to the javadoc classpath
def outputPath = "$buildDir/tmp/aarJar/${aar.name.replace('.aar', '.jar')}"
classpath += files(outputPath)
// Use a task so the actual extraction only happens before the javadoc task is run
dependsOn task(name: "extract ${aar.name}").doLast {
extractEntry(aar, 'classes.jar', outputPath)
}
}
}
}
// Utility method to extract only one entry in a zip file
private def extractEntry(archive, entryPath, outputPath) {
if (!archive.exists()) {
throw new GradleException("archive $archive not found")
}
def zip = new ZipFile(archive)
zip.entries().each {
if (it.name == entryPath) {
def path = Paths.get(outputPath)
if (!Files.exists(path)) {
Files.createDirectories(path.getParent())
Files.copy(zip.getInputStream(it), path)
}
}
}
zip.close()
}
The solution from #rve is now broken on Android Studio 2.3 / Gradle 3.3 as the exploded-aar no longer exists (with no alternative inside the build directory).
If the aar you depend on is not a module in your project, you will need first to extract the classes.jar before referencing it in the classpath (basically re-create intermediates/exploded-aar manually).
If the aar you depend on is just another module in your project you can also make your javadoc task depends on the compile task of that module and reference the intermediates/classes/release of that module (if you make javadoc depends on assembleRelease for example). An example of that workaround: https://github.com/Microsoft/mobile-center-sdk-android/pull/345/files
I really wish someone comes up with a better solution though.
This only works for Android Studio older than 2.3 and/or Gradle older than 3.3
To add the JARs from the AARs you can add the following doFirst to the javadoc task:
task javadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
}
.doFirst {
classpath += fileTree(dir: "$buildDir/intermediates/exploded-aar/", include:"**/classes.jar")
}
It will add all .jar files from all the AARs to the javadoc classpath. (option 1 from your proposed solutions)
This is how I solved this issue, using zipTree. Configuration: Gradle 4.10, Gradle Plugin: 3.3.2, Android Studio: 3.4.
task javadoc(type: Javadoc) {
doFirst {
configurations.implementation
.filter { it.name.endsWith('.aar') }
.each { aar ->
copy {
from zipTree(aar)
include "**/classes.jar"
into "$buildDir/tmp/aarsToJars/${aar.name.replace('.aar', '')}/"
}
}
}
configurations.implementation.setCanBeResolved(true)
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
classpath += configurations.implementation
classpath += fileTree(dir: "$buildDir/tmp/aarsToJars/")
destinationDir = file("${project.buildDir}/outputs/javadoc/")
failOnError false
exclude '**/BuildConfig.java'
exclude '**/R.java'
}
I am running the new Android Studio 3.0-beta7, and tried to use #nicopico's answer, but it failed with a number of different errors, so here's an adaptation of it that doesn't rely on the non-existent java.nio utilities.
task javadoc(type: Javadoc) {
failOnError false
source = android.sourceSets.main.java.srcDirs
// Also add the generated R class to avoid errors...
// TODO: debug is hard-coded
source += "$buildDir/generated/source/r/debug/"
// ... but exclude the R classes from the docs
excludes += "**/R.java"
// TODO: "compile" is deprecated in Gradle 4.1,
// but "implementation" and "api" are not resolvable :(
classpath += configurations.compile
afterEvaluate {
// Wait after evaluation to add the android classpath
// to avoid "buildToolsVersion is not specified" error
classpath += files(android.getBootClasspath())
// Process AAR dependencies
def aarDependencies = classpath.filter { it.name.endsWith('.aar') }
classpath -= aarDependencies
aarDependencies.each { aar ->
System.out.println("Adding classpath for aar: " + aar.name)
// Extract classes.jar from the AAR dependency, and add it to the javadoc classpath
def outputPath = "$buildDir/tmp/exploded-aar/${aar.name.replace('.aar', '.jar')}"
classpath += files(outputPath)
// Use a task so the actual extraction only happens before the javadoc task is run
dependsOn task(name: "extract ${aar.name}").doLast {
extractEntry(aar, 'classes.jar', outputPath)
}
}
}
}
// Utility method to extract only one entry in a zip file
private def extractEntry(archive, entryPath, outputPath) {
if (!archive.exists()) {
throw new GradleException("archive $archive not found")
}
def zip = new java.util.zip.ZipFile(archive)
zip.entries().each {
if (it.name == entryPath) {
def path = new File(outputPath)
if (!path.exists()) {
path.getParentFile().mkdirs()
// Surely there's a simpler is->os utility except
// the one in java.nio.Files? Ah well...
def buf = new byte[1024]
def is = zip.getInputStream(it)
def os = new FileOutputStream(path)
def len
while ((len = is.read(buf)) != -1) {
os.write(buf, 0, len)
}
os.close()
}
}
}
zip.close()
}
It bothers me that we need all this code to produce a freaking javadoc for a library, but at least I got this working. However, I do need to find a workaround for configuration.api and configuration.implementation not being resolvable.
All of the solutions listed here are out of date if you are developing an Android app/library using Kotlin. To generate javadocs as well as documentation in several other formats, use KDoc and Dokka:
https://kotlinlang.org/docs/kotlin-doc.html
https://kotlin.github.io/dokka/1.5.0/
https://github.com/Kotlin/dokka
I posted a solution for this problem at Android AAR depending on AAR fails with javadoc generation. I think Johann comment that the listed solutions are out of date is probably correct, but mike192 solution looks pretty good, although I think it might have a problem handling androidx dependencies. I haven't tried KDoc and Dokka yet, but in looking at the documentation, that looks promising. Hopefully it works for android java libraries. The android studio's built-in javadoc tool (2021.2.1) has issues handling that module type; hence the need to build a custom javadoc task to work around those issues.

Avoid packing .jar into Android Library (.aar)

My project is an Android Library which depends on Dropbox's android library.
dependencies {
...
provided fileTree(dir: '../Libraries/Dropbox', include: ['*.jar'])
...
}
Everything works well excepts Gradle puts all the .jar files from Dropbox into my output .aar file.
MyLib.aar
|-classes.jar
|-AndroidManifest.xml
|-...
|-libs
|-bcprov-jdk16-146.jar
|-commons-logging-1.1.1.jar
|-dropbox-android-sdk-1.6.1
|-json_simple-1.1.jar
How can I avoid this?
something like this might help you:
android.libraryVariants.all { variant ->
variant.outputs.each { output ->
def packageLib = output.getPackageLibrary()
packageLib.exclude('libs/externalLibrary.jar')
}
}
inside android {} block
Why do you want to avoid this? When you give your library to someone, they have all the dependencies already in one file.
You can include the dependencies via
compile 'com.dropbox.core:dropbox-core-sdk:1.7.7'
compile 'com.googlecode.json-simple:json-simple:1.1.1'
compile 'commons-logging:commons-logging:1.2'
compile 'org.bouncycastle:bcprov-jdk16:1.46'
in your build.gradle file and remove it from the libs folder. Do the same with the other dependencies. This way they will not be packaged into your .aar file.

Gradle Android unit tests that depend on an 'aar'

Want to be able to depend on an AAR for my unit tests in a Gradle-Android project. I do not want to manually include the external library source or binaries inside my project (for multiple reasons).
We can depend on remote aar files like this in the build.gradle:
compile 'com.facebook:facebook-android-sdk:3.6.0:#aar'
But when using the gradle test method described here: https://stackoverflow.com/a/16952507/821636 (Since Jake Wharton's plugin, isn't quite there yet), trying to include an aar as a dependency for that method like this:
testLocalCompile 'com.facebook:facebook-android-sdk:3.6.0:#aar
Does not seem to actually include the aar in the classpath for tests (run with ./gradlew check) since I get NoClassDefFoundError for classes in the aar. NOTE: When jar dependencies are included this way, they work.
Thinking there must be something in that localTest task that needs to add the aar extension since it's not the default type of dependency (jar).
Here is copy of that task copied from the SO answer referenced above:
task localTest(type: Test, dependsOn: assembleDebug) {
testClassesDir = sourceSets.testLocal.output.classesDir
workingDir = "${rootProject.projectDir}/app/src/main"
android.sourceSets.main.java.srcDirs.each { dir ->
def buildDir = dir.getAbsolutePath().split('/')
buildDir = (buildDir[0..(buildDir.length - 4)] + ['build', 'classes', 'debug']).join('/')
sourceSets.testLocal.compileClasspath += files(buildDir)
sourceSets.testLocal.runtimeClasspath += files(buildDir)
}
classpath = sourceSets.testLocal.runtimeClasspath
}
I ran into this problem and found a solution - include the classes.jar from the exploded bundle (.aar) in the build folder. I don't think will help with finding resources in .aar dependencies though.
testLocalCompile fileTree(dir: "$project.buildDir/exploded-bundles", include: "**/classes.jar")
Edit: Since Android Gradle build tools 0.9.0 the dependency has changed to:
androidTestCompile fileTree(dir: "$project.buildDir/exploded-aar", include: "**/classes.jar")
As of Android Gradle plugin 0.12.2:
testLocalCompile fileTree(dir: "$project.buildDir/intermediates/exploded-aar/", include:"**/classes.jar")

Include .so library in apk in android studio [duplicate]

This question already has answers here:
Android studio, gradle and NDK
(23 answers)
Closed 9 years ago.
I am trying my hands on developing a simple android application in which I am trying to use sqlcipher, which uses .so libraries internally. I have read the documentation on how to use sqlcipher with android app. I have followed the steps and it compiles without any error. But, at runtime it throws UnsatisfiedLinkError.
Googling around for it, I found that, gradle doesn't support .so libraries yet, but I found a hack here which I am trying to use. But it throws compile time error at line #40 on the gist which is,
tasks.withType(com.android.build.gradle.PackageApplicationTask) { pkgTask ->
pkgTask.jniDir new File(buildDir, 'native-libs')
}
saying
Could not find property 'com' on Project 'MyProject'
Here I am posting code from my build.gradle file.
buildscript {
repositories {
maven { url 'http://repo1.maven.org/maven2' }
}
dependencies {
classpath 'com.android.tools.build:gradle:0.4'
}
}
apply plugin: 'android'
dependencies {
compile files('libs/android-support-v4.jar')
compile files('libs/commons-codec.jar')
compile files('libs/guava-r09.jar')
compile files('libs/sqlcipher.jar')
}
targetCompatibility = 1.6
sourceCompatibility = 1.6
android {
target = 'android-14'
compileSdkVersion 17
buildToolsVersion "17.0.0"
defaultConfig {
minSdkVersion 9
targetSdkVersion 16
}
}
task copyNativeLibs(type: Copy) {
from(new File(project(':MyProject').buildDir, 'native-libs')) { include '**/*.so' }
into new File(buildDir, 'native-libs')
}
tasks.withType(Compile) { compileTask -> compileTask.dependsOn copyNativeLibs }
clean.dependsOn 'cleanCopyNativeLibs'
tasks.withType(com.android.build.gradle.PackageApplicationTask) { pkgTask ->
pkgTask.jniDir new File(buildDir, 'native-libs')
}
Can, anybody please help me on what I have done wrong or what should I do to include those .so libraries in my apk?
As I am new to android development and gradle, please apologize me if I have misunderstood something.
I've tried the solution presented in the accepted answer and it did not work for me.
I wanted to share what DID work for me as it might help someone else. I've found this solution here.
Basically what you need to do is put your .so files inside a a folder named lib (Note: it is not libs and this is not a mistake). It should be in the same structure it should be in the APK file.
In my case it was:
Project:
|--lib:
|--|--armeabi:
|--|--|--.so files.
So I've made a lib folder and inside it an armeabi folder where I've inserted all the needed .so files. I then zipped the folder into a .zip (the structure inside the zip file is now lib/armeabi/*.so) I renamed the .zip file into armeabi.jar and added the line compile fileTree(dir: 'libs', include: '*.jar') into dependencies {} in the gradle's build file.
This solved my problem in a rather clean way.
To include native libraries you need:
create "jar" file with special structure containing ".so" files;
include that file in dependencies list.
To create jar file, use the following snippet:
task nativeLibsToJar(type: Zip, description: 'create a jar archive of the native libs') {
destinationDir file("$buildDir/native-libs")
baseName 'native-libs'
extension 'jar'
from fileTree(dir: 'libs', include: '**/*.so')
into 'lib/'
}
tasks.withType(Compile) {
compileTask -> compileTask.dependsOn(nativeLibsToJar)
}
To include resulting file, paste the following line into "dependencies" section in "build.gradle" file:
compile fileTree(dir: "$buildDir/native-libs", include: 'native-libs.jar')
I had the same problem. Check out the comment in https://gist.github.com/khernyo/4226923#comment-812526
It says:
for gradle android plugin v0.3 use "com.android.build.gradle.tasks.PackageApplication"
That should fix your problem.

Jar files in libs folder are not used in Android Gradle build

i just started to play with the gradle build system for Android.
However i'm not able build one of my projects. It depends on a jar in the libs/ folder.
Doing gradle build fails in the compileDebug task because all of the classes from the jar file are missing.
It is a library project! Here is my build.gradle file:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.2'
}
}
apply plugin: 'android-library'
android {
target='android-16'
sourceSets {
main {
manifest {
srcFile 'AndroidManifest.xml'
}
java {
srcDir 'src'
}
res {
srcDir 'res'
}
assets {
srcDir 'assets'
}
resources {
srcDir 'src'
}
}
}
}
Am i missing something obvious?
Goddchen
just found the answer myself:
Seems like the current version of the Android Gradle plugin doesn't look for jars in the libs/ folder. So you have to add them yourself:
dependencies {
compile files('libs/mylib.jar')
}
or
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
}
Place this within the android namespace like this:
android {
target = "android-15"
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
}
}
Just in case none of the solutions worked for you, you may want to try to provide the path to the *.jar manually:
compile files('../<your-library-folder>/<your-library>.jar')
The '../' part means that Gradle will search for the library starting from the root projects folder (not app module).

Categories

Resources