In the world of Java outside of Android, reusable components rule the roost. Whether they are simple JARs, are tied in via inversion-of-control (IoC) containers like Spring, or rely on enterprise service buses like Mule, reusable Java components are a huge portion of the overall Java ecosystem. Even full-fledged applications, like Eclipse or NetBeans, are frequently made up of a number of inter-locking components, many of which are available for others to use in their own applications.
Android, too, supports this sort of reuse. In some cases, it follows standard Java approaches. However, in other cases, unique Android aspects, such as resources, steer developers in different directions for reuse.
This chapter will outline what reuse models are in use today and how you can package your own components for reuse.
Understanding this chapter requires that you have read the core chapters of this book.
Android historically has not had a “go-to” place to find reusable components. The Android Arsenal is probably the largest collection at present. Beyond that, look for recommendations in Stack Overflow answers, blog posts, and the like.
There are three main ways that reusable code gets packaged on Android: as a traditional Java JAR, as an Android library project, or (technically) as an APK. The last approach is usually used by apps that have user value in their own right, but also expose some sort of integration API for use by other apps, that you can take advantage of.
Android code that is pure code, without requiring its own resources, can be packaged into a JAR, no differently than can regular Java code outside of Android.
As was covered earlier in the book, to use such a JAR,
just drop it into libs/
. Its contents will be added to your compile path
(so you can reference classes from the library) and its contents will be packaged
in your APK (so those references will work at runtime).
Android code that relies upon resources — such as many reusable UI components, such as custom widgets — cannot be packaged as a simple JAR, as there is no way of packaging the Android resources in that JAR. Instead, Google created the Android library project as the “unit of reuse” for such cases.
Android library projects are sometimes published in full source form (usually
open source projects), and sometimes are published as AARs in an artifact
repository. Eclipse users can readily use the full-source library projects,
but have limited ability
to use AARs. Android Studio users can use either, and AARs may be as simple
as adding a single line to build.gradle
.
Using JARs or library projects fits in the “traditional” model of compile-time reuse. Android’s many IPC mechanisms offer plenty of options for run-time reuse, where your app communicates with another app, having that app do things on your behalf. In this case, the primary unit of reuse is not the JAR, or the library project, but the APK.
For example, the ZXing project publishes the Barcode Scanner app. This app not only allows users to scan barcodes, but allows other apps to scan barcodes, by asking Barcode Scanner to scan the barcodes and return results.
To integrate with such an app, you will need to find the instructions from
the app’s developers on how to do that. Sometimes, they will tell you things
that you would use directly (e.g., “call startActivityForResult()
with
an Intent
that contains…”). Sometimes, they will distribute a client-side
JAR that you can use that wraps up the low-level IPC details into something
a bit easier to consume. For example, ZXing distributes an
IntentIntegrator.java
class file
that you can use that not only handles requesting the scans, but also
helping the user install Barcode Scanner if it is not already installed.
To create a reusable component, you start by getting a working code base, one that implements whatever it is that you desire. From there, you need to choose which of those aforementioned distribution patterns you believe is appropriate:
That, in turn, will drive how you take your code and create such a package. The basics of how to do that for the different alternatives is described in the following sections.
Creating a JAR for a reusable chunk of Android-related code is not significantly different than is creating a JAR for a reusable chunk of “ordinary” Java code.
First, you need a project that represents the “resuable chunk of Android-related code”. An easy way to do this is to just create a standard Android library project, but one where you do not bother creating any resources.
Once the code is ready for distribution, you can create a JAR from the compiled Java classes by your favorite traditional means. The author of this book, for example, adds custom Gradle tasks for this:
// from http://stackoverflow.com/a/19484146/115145
android.libraryVariants.all { variant ->
def name = variant.buildType.name
if (name.equals(com.android.builder.core.BuilderConstants.DEBUG)) {
return; // Skip debug builds.
}
def task = project.tasks.create "jar${name.capitalize()}", Jar
task.dependsOn variant.javaCompile
task.from variant.javaCompile.destinationDir
task.archiveName = "cwac-${task.archiveName}"
task.exclude('com/commonsware/cwac/**/BuildConfig.**')
}
This will create JAR-building Gradle tasks for all non-debug build types,
so you get Gradle tasks like jarRelease
. It specifically excludes BuildConfig
,
which the CWAC libraries never use, but otherwise takes all of the library
classes and packages them in a JAR, named after the library and version,
with a cwac-
prefix.
If your reusable code is pure Java, not involving Android at all, you are welcome
to create a plain Java project and create your JAR from that. The only
major recommendation would be to ensure that you are using some android.jar
from the SDK, rather than a JDK rt.jar
, to ensure that you are sticking
with classes and methods that are in Android’s subset of the Java SE class
library.
In many respects, distributing a standard Android library project is even easier: just ZIP it up. Or, if it is in a public source control repository (e.g., GitHub), reusers can obtain it from that repository.
Of course, this will distribute the source code along with the resources and everything else. This is typical for an open source library project.
Android Studio and Gradle users can create AARs from their library
projects. The assembleRelease
task will create an AAR for the library
in build/outputs/aar
, named after the library and version (e.g.,
pager-0.2.3.jar
).
AARs do not ship Java source code, but rather only binaries. However, AARs are not readily consumable from Eclipse.
It is possible to create an Eclipse-Compatible binary-only library project, one where your source code is replaced by a JAR. This can be useful for proprietary library projects, for example. However, there is one noteworthy limitation with today’s tools: the library project cannot itself depend upon a JAR or another library project.
For simpler library projects, the recipe is straightforward, given an already-existing Android library project:
libs/
of the distribution library project from step #2.src/
directory there).For example, an Ant target to create a distribution ZIP might be:
<target name="jar" depends="release">
<delete file="bin/WhateverYouWantToCallYourLibrary.jar" />
<jar destfile="bin/WhateverYouWantToCallYourLibrary.jar">
<fileset dir="bin/classes">
<exclude name="**/BuildConfig.class" />
<exclude name="**/R.class" />
<exclude name="**/R$*.class" />
</fileset>
</jar>
</target>
<target name="dist" depends="jar">
<copy todir="/tmp/WhateverYouWantToCallYourLibrary/libs">
<fileset dir="libs/" />
</copy>
<copy todir="/tmp/WhateverYouWantToCallYourLibrary/res">
<fileset dir="res/" />
</copy>
<copy
file="bin/WhateverYouWantToCallYourLibrary.jar"
todir="/tmp/WhateverYouWantToCallYourLibrary/libs" />
<copy
file="AndroidManifest.xml"
todir="/tmp/WhateverYouWantToCallYourLibrary" />
<copy file="build.xml" todir="/tmp/WhateverYouWantToCallYourLibrary" />
<copy
file="project.properties"
todir="/tmp/WhateverYouWantToCallYourLibrary" />
<copy file="LICENSE" todir="/tmp/WhateverYouWantToCallYourLibrary" />
<mkdir dir="/tmp/WhateverYouWantToCallYourLibrary/src" />
<zip
destfile="/tmp/WhateverYouWantToCallYourLibrary.zip"
basedir="/tmp/"
includes="WhateverYouWantToCallYourLibrary/**"
whenempty="create" />
<delete dir="/tmp/WhateverYouWantToCallYourLibrary" />
</target>
Assuming the existence of a /tmp/
directory (e.g., OS X or Linux), this will
result in a WhateverYouWantToCallYourLibrary.zip
file in /tmp/
. Along the
way, we:
libs/
and res/
trees from your source library project
to a temporary distribution directorylibs/
subdirectory of the temporary
distribution directoryLICENSE
file for your software
license terms, into the root of the temporary distribution directorysrc/
subdirectory in the temporary distribution directoryMost of your work for this distribution model is in writing and distributing the app to your end users, through the Play Store or your other chosen distribution channels.
In addition to that, you need to either document to reusers what sorts of IPC your app supports, or create a JAR or library project that reusers can use to perform that sort of integration. In the latter case, you would have a separate project representing that JAR or library project that you would distribute using any of the aforementioned approaches.
Of course, there is more to publishing a resuable component than code and perhaps Android resources. The following sections outline some other things to consider as you contemplate offering some code base up for reuse by third parties.
Your reusable code should be accompanied by adequate licensing information.
The first license you should worry about is your own. Is your component open source? If so, you will want to ship a license file containing those terms. If your component is not open source, make sure there is a license agreement shipped with the component that lets the reuser know the terms of use.
Bear in mind that not all of your code necessarily has to have the same license. For example, you might have a proprietary license for the component itself, but have sample code be licensed under Apache License 2.0 for easy copy-and-paste.
You may need to include licenses for third party libraries that you have to ship along with your own JAR. Obviously, those licenses would need to give you redistribution rights — otherwise, you cannot ship those libraries in the first place.
Sometimes, the third party licenses will impact your project more directly, such as:
If you are expecting people to reuse your code, you are going to have to tell them how to do that. Usually, these sorts of packages ship documentation with them, sometimes a clone of what is available online. That way, developers can choose the local or hosted edition of the documentation as they wish.
Note that generated documentation (e.g., Javadocs) may still need to be shipped or otherwise supplied to reusers, if you are not providing the source code in the package. Without the source code, reusers cannot regenerate the Javadocs.
Many open source projects eschew formal documentation in favor of simple JavaDocs, plus “documentation in the form of a test suite” or “documentation in the form of sample apps”. While test suites and sample apps are useful supplements, they are not always an effective replacement for written documentation. And, while JavaDocs are useful for reference material, they are often difficult to comprehend for those trying to get started with the code and not knowing where to begin.
Make sure that your Java code is in a package that is likely to
be distinct from any others that reusers might already have. Typically,
this means that the package name is based on a domain name that
you control, much like the package name for Android apps themselves.
Whatever you do, please do not publish your own code as android.*
,
unless you are contributing this code to the Android open source
project, as android.*
is reserved for use by Android itself.
(The author of this book would also appreciate it if you would not use
com.commonsware.*
)
Also, be careful about the names of your resources. While your Java
code resides in its own namespace, your resources are pooled with all
other resources in use by the app. As a result, if you decide to
reference R.layout.main
thinking that it will be your main.xml
layout resource, it might actually be replaced by a main.xml
resource
written by the app developer. You may wish to use some sort of a prefix
convention on your resource names to reduce the odds of accidental
collision:
abs__
vpi__