Writing Android Libraries
Emanuele Zattin
@emanuelez
April 9, 2015
DroidCon Italy
Emanuele Zattin
Born and raised in Padova, Italy
Living in Copenhagen, Denmark since 2005
Automation enthusiast
Jenkins CI contributor since 2010
Java developer at Realm
About me
Embedded on-device mobile database
Easy — saves app makers months of time
Cross-platform — iOS & Android for now. More are coming
Fast — way faster than existing solutions
About Realm
Modularity
Reusability
Vanity
Why would you want to write a library?
Your code depends on Android idioms:
• UI
• Looper/Handler
• Sensors
• Native code
• Many more!
Why writing an Android library?
How hard can it be right?
Just fire Android Studio up and start a new project!
Step one: Getting started!
Android Studio supports the creation of:
Step one: Getting started!
Application Library
New project
✓ ✗
New module
✓ ✓
Option 1: Android Studio
1. Create a new application project
2. Add a library module
3. Remove the application module
Step one: Getting started!
Option 2: The Command Line
$ android create lib-project -t 1 -k it.droidcon.awesomelib -p . -g -v 1.1.3
-t: target (Use android  list  targets to get a list of target ids)
-k: package name
-p: path to the project
-g: make it a Gradle project (requires SDK >= 19)
-v: version of the Android Gradle plugin to use
Step one: Getting started!
Step one: Getting started!
Step two: Code, code, code!
API Design
It’s a huge subject that goes far beyond this presentation but here
are some pointers and reference:
Effective Java 2, Joshua Bloch
How To Design A Good API and Why it Matters, Joshua Bloch
Step two: Code, code, code!
Characteristics of a good API (Joshua Bloch)
• Easy to learn
• Easy to use, even without documentation
• Hard to misuse
• Easy to read and maintain code that uses it
• Sufficiently powerful to satisfy requirements
• Easy to extend
• Appropriate to audience
Step two: Code, code, code!
Testing is universally important but even more so for libraries
Testing an Android library is just like testing an Android application
Yes, JUnit 3 is not so good and lacks a particularly important
feature when testing libraries: parametric tests
Luckily there’s a solution: Burst
Step three: Test
Automate your tests!
Jenkins is a wonderful tool for this.
It offers more than one thousand plugins some of which
specialised for Android development.
Step three: Test
Some must-have Jenkins plugins include:
• Job Config History plugin
• Git plugin
• Gradle plugin
• Android Emulator plugin
• Jenkins comes with JUnit and Matrix job support out of the box
Step three: Test
Another useful way to test your library (and showcase it) is to write
one or more example apps.
Running monkey on the app ensures it doesn’t suffer from crashes
and ANRs
A useful Gradle plugin:

https://github.com/novoda/gradle-android-command-plugin
Step three: Test
Aar vs Jar
• Aar is supported by Gradle and Android Studio
• Aar is not supported by Ant and Eclipse
• Using local Aar files is not trivial
• Do you want to support Eclipse? Use Jar!
Step four: Publish
The Android Gradle plugin will generate an Aar file
How to generate a Jar instead?

The Aar actually contains our Jar already!
task generateJar(type: Copy) {
group 'Build'
description 'blah blah...'
dependsOn assemble
from 'build/intermediates/bundles/release/classes.jar'
into 'build/libs'
rename('classes.jar', 'awesome-library.jar')
}
Step four: Publish
Where to publish?
Step four: Publish
Bintray requires a source Jar
task androidSourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
}
Step four: Publish
Bintray also requires a Javadoc Jar
android.libraryVariants.all { variant ->
task("javadoc${variant.name.capitalize()}", type: Javadoc) {
description "Generates Javadoc for $variant.name."
group 'Docs'
source = variant.javaCompile.source
ext.androidJar = files(plugins
.findPlugin(“com.android.library")
.getBootClasspath())
classpath = files(variant.javaCompile.classpath.files) +
ext.androidJar
exclude '**/BuildConfig.java'
exclude '**/R.java'
}
}
Step four: Publish
Bintray also provides a Gradle plugin for the actual publishing
https://github.com/bintray/gradle-bintray-plugin
The configuration is not trivial, and in the beginning it might be
easier to just do the release manually on the binary website
Step four: Publish
Annotation processing is a functionality of javac used for scanning
and processing annotations at compile time
You can write your own annotation processor.
Problems that annotation processing is good at solving:
• Boilerplate removal
• Introspection removal
Advanced Topics: Annotation Processor
Popular Android libraries using annotation processing:
• Dagger
• Butter Knife
• Autovalue/Autoparcel
• Realm
Advanced Topics: Annotation Processor
Bad news!
The Android API does not support the 

javax.annotation.processing package
Advanced Topics: Annotation Processor
Create two new java sub-projects:
• annotations (used both by the library and the processor)
• annotations processor
Advanced Topics: Annotation Processor
The Jar task will need to be modified:
task androidJar(type: Jar) {
dependsOn assemble
group 'Build'
description ‘blah blah’
from zipTree(
'build/intermediates/bundles/release/classes.jar')
from zipTree(
'../annotations-processor/build/libs/processor.jar')
from zipTree(
'../annotations/build/libs/annotations.jar')
}
Advanced Topics: Annotation Processor
The javadoc tasks will also have to be modified:
android.libraryVariants.all { variant ->
task("javadoc${variant.name.capitalize()}", type: Javadoc) {
description "Generates Javadoc for $variant.name."
group 'Docs'
source = variant.javaCompile.source
source "../annotations/src/main/java"
ext.androidJar = files(plugins
.findPlugin(“com.android.library")
.getBootClasspath())
classpath = files(variant.javaCompile.classpath.files)
+ ext.androidJar
exclude '**/BuildConfig.java'
exclude '**/R.java'
}
}
Advanced Topics: Annotation Processor
NDK
Advanced Topics: Native code
WARNING  [Project:  :lib]  
Current  NDK  support  is  deprecated.  
Alternative  will  be  provided  in  the  future.
Advanced Topics: Native code
Advanced Topics: Native code
Solution 1
Ignore Google’s warning and keep using the Gradle NDK support.
It works* but there is one missing feature: no ldFlags
ndk  {  
      moduleName  "sanangeles"  
      cFlags  "-­‐DANDROID_NDK  -­‐DDISABLE_IMPORTGL"  
      ldLibs  "GLESv1_CM",  "dl",  "log"  
      stl  "stlport_static"  
}  
*for some definition of “works”
Advanced Topics: Native code
Solution 2
Gradle native plugin
Gotchas:
• Requires extra logic to handle standalone toolchains
• It might soon become obsolete
Advanced Topics: Native code
How to include the native libraries in the Jar file?
$ tree
.
!"" META-INF
#   $"" MANIFEST.MF
!"" com
#   $"" amazing-library
#   $"" AmazingLibrary.class
$"" lib
!"" armeabi
#   $"" amazing-library.so
!"" armeabi-v7a
#   $"" amazing-library.so
$"" x86
$"" amazing-library.so
Advanced Topics: Native code
How to generate the jar file in Gradle?
task  androidJar(type:  Jar,  dependsOn:  ['assemble'])  {  
      group  'Build'  
      description  ‘blah  blah'  
      from  zipTree('build/intermediates/bundles/release/classes.jar')  
      from(file('src/main/jniLibs'))  {  
            into  'lib'  
      }  
}  
Advanced Topics: Native code
• Embrace Gradle
• Explore Gradle plugins
• Automate your tests
• Bintray is the go-to solution for publishing
• Writing libraries rocks!
Takeaways
Questions?

Writing Android Libraries

  • 1.
    Writing Android Libraries EmanueleZattin @emanuelez April 9, 2015 DroidCon Italy
  • 2.
    Emanuele Zattin Born andraised in Padova, Italy Living in Copenhagen, Denmark since 2005 Automation enthusiast Jenkins CI contributor since 2010 Java developer at Realm About me
  • 3.
    Embedded on-device mobiledatabase Easy — saves app makers months of time Cross-platform — iOS & Android for now. More are coming Fast — way faster than existing solutions About Realm
  • 4.
  • 5.
    Your code dependson Android idioms: • UI • Looper/Handler • Sensors • Native code • Many more! Why writing an Android library?
  • 6.
    How hard canit be right? Just fire Android Studio up and start a new project! Step one: Getting started!
  • 7.
    Android Studio supportsthe creation of: Step one: Getting started! Application Library New project ✓ ✗ New module ✓ ✓
  • 8.
    Option 1: AndroidStudio 1. Create a new application project 2. Add a library module 3. Remove the application module Step one: Getting started!
  • 9.
    Option 2: TheCommand Line $ android create lib-project -t 1 -k it.droidcon.awesomelib -p . -g -v 1.1.3 -t: target (Use android  list  targets to get a list of target ids) -k: package name -p: path to the project -g: make it a Gradle project (requires SDK >= 19) -v: version of the Android Gradle plugin to use Step one: Getting started!
  • 10.
  • 11.
    Step two: Code,code, code!
  • 12.
    API Design It’s ahuge subject that goes far beyond this presentation but here are some pointers and reference: Effective Java 2, Joshua Bloch How To Design A Good API and Why it Matters, Joshua Bloch Step two: Code, code, code!
  • 13.
    Characteristics of agood API (Joshua Bloch) • Easy to learn • Easy to use, even without documentation • Hard to misuse • Easy to read and maintain code that uses it • Sufficiently powerful to satisfy requirements • Easy to extend • Appropriate to audience Step two: Code, code, code!
  • 14.
    Testing is universallyimportant but even more so for libraries Testing an Android library is just like testing an Android application Yes, JUnit 3 is not so good and lacks a particularly important feature when testing libraries: parametric tests Luckily there’s a solution: Burst Step three: Test
  • 15.
    Automate your tests! Jenkinsis a wonderful tool for this. It offers more than one thousand plugins some of which specialised for Android development. Step three: Test
  • 16.
    Some must-have Jenkinsplugins include: • Job Config History plugin • Git plugin • Gradle plugin • Android Emulator plugin • Jenkins comes with JUnit and Matrix job support out of the box Step three: Test
  • 17.
    Another useful wayto test your library (and showcase it) is to write one or more example apps. Running monkey on the app ensures it doesn’t suffer from crashes and ANRs A useful Gradle plugin:
 https://github.com/novoda/gradle-android-command-plugin Step three: Test
  • 18.
    Aar vs Jar •Aar is supported by Gradle and Android Studio • Aar is not supported by Ant and Eclipse • Using local Aar files is not trivial • Do you want to support Eclipse? Use Jar! Step four: Publish
  • 19.
    The Android Gradleplugin will generate an Aar file How to generate a Jar instead?
 The Aar actually contains our Jar already! task generateJar(type: Copy) { group 'Build' description 'blah blah...' dependsOn assemble from 'build/intermediates/bundles/release/classes.jar' into 'build/libs' rename('classes.jar', 'awesome-library.jar') } Step four: Publish
  • 20.
  • 21.
    Bintray requires asource Jar task androidSourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs } Step four: Publish
  • 22.
    Bintray also requiresa Javadoc Jar android.libraryVariants.all { variant -> task("javadoc${variant.name.capitalize()}", type: Javadoc) { description "Generates Javadoc for $variant.name." group 'Docs' source = variant.javaCompile.source ext.androidJar = files(plugins .findPlugin(“com.android.library") .getBootClasspath()) classpath = files(variant.javaCompile.classpath.files) + ext.androidJar exclude '**/BuildConfig.java' exclude '**/R.java' } } Step four: Publish
  • 23.
    Bintray also providesa Gradle plugin for the actual publishing https://github.com/bintray/gradle-bintray-plugin The configuration is not trivial, and in the beginning it might be easier to just do the release manually on the binary website Step four: Publish
  • 24.
    Annotation processing isa functionality of javac used for scanning and processing annotations at compile time You can write your own annotation processor. Problems that annotation processing is good at solving: • Boilerplate removal • Introspection removal Advanced Topics: Annotation Processor
  • 25.
    Popular Android librariesusing annotation processing: • Dagger • Butter Knife • Autovalue/Autoparcel • Realm Advanced Topics: Annotation Processor
  • 26.
    Bad news! The AndroidAPI does not support the 
 javax.annotation.processing package Advanced Topics: Annotation Processor
  • 27.
    Create two newjava sub-projects: • annotations (used both by the library and the processor) • annotations processor Advanced Topics: Annotation Processor
  • 28.
    The Jar taskwill need to be modified: task androidJar(type: Jar) { dependsOn assemble group 'Build' description ‘blah blah’ from zipTree( 'build/intermediates/bundles/release/classes.jar') from zipTree( '../annotations-processor/build/libs/processor.jar') from zipTree( '../annotations/build/libs/annotations.jar') } Advanced Topics: Annotation Processor
  • 29.
    The javadoc taskswill also have to be modified: android.libraryVariants.all { variant -> task("javadoc${variant.name.capitalize()}", type: Javadoc) { description "Generates Javadoc for $variant.name." group 'Docs' source = variant.javaCompile.source source "../annotations/src/main/java" ext.androidJar = files(plugins .findPlugin(“com.android.library") .getBootClasspath()) classpath = files(variant.javaCompile.classpath.files) + ext.androidJar exclude '**/BuildConfig.java' exclude '**/R.java' } } Advanced Topics: Annotation Processor
  • 30.
  • 31.
    WARNING  [Project:  :lib]  Current  NDK  support  is  deprecated.   Alternative  will  be  provided  in  the  future. Advanced Topics: Native code
  • 32.
  • 33.
    Solution 1 Ignore Google’swarning and keep using the Gradle NDK support. It works* but there is one missing feature: no ldFlags ndk  {        moduleName  "sanangeles"        cFlags  "-­‐DANDROID_NDK  -­‐DDISABLE_IMPORTGL"        ldLibs  "GLESv1_CM",  "dl",  "log"        stl  "stlport_static"   }   *for some definition of “works” Advanced Topics: Native code
  • 34.
    Solution 2 Gradle nativeplugin Gotchas: • Requires extra logic to handle standalone toolchains • It might soon become obsolete Advanced Topics: Native code
  • 35.
    How to includethe native libraries in the Jar file? $ tree . !"" META-INF #   $"" MANIFEST.MF !"" com #   $"" amazing-library #   $"" AmazingLibrary.class $"" lib !"" armeabi #   $"" amazing-library.so !"" armeabi-v7a #   $"" amazing-library.so $"" x86 $"" amazing-library.so Advanced Topics: Native code
  • 36.
    How to generatethe jar file in Gradle? task  androidJar(type:  Jar,  dependsOn:  ['assemble'])  {        group  'Build'        description  ‘blah  blah'        from  zipTree('build/intermediates/bundles/release/classes.jar')        from(file('src/main/jniLibs'))  {              into  'lib'        }   }   Advanced Topics: Native code
  • 37.
    • Embrace Gradle •Explore Gradle plugins • Automate your tests • Bintray is the go-to solution for publishing • Writing libraries rocks! Takeaways
  • 38.