While you are writing some code for an app, the vast majority of the code that is the app comes from other developers. Some might be teammates on your development team, but far more comes from outsiders: Google and other Android developers.
Some of this you have seen already. You did not write Activity
, TextView
,
and similar classes. Instead, they came from the Android SDK, written (primarily)
by Google.
Beyond the Android SDK, though, there are thousands of libraries that developers have access to, including many from Google itself. We add these as dependencies in our projects, to use their code alongside ours.
Roughly speaking, the code and assets that make up an app come from three sources.
The source that you tend to focus on personally is the code that you and people that you know are writing for this app.
There is the source that comes from your compileSdkVersion
, representing the
Android SDK that you are linking to.
Everything else, generally speaking, is a dependency.
From a pure technical standpoint, dependencies are listed in build.gradle
files in dependencies
closures.
One dependencies
closure appears in the project-level build.gradle
file,
inside of a buildscript
closure:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Those list places where Gradle plugins come from. You are always depending upon the Android Gradle Plugin, and some other developers publish Gradle plugins that you may elect to use in the future.
However, the dependencies
closure that we tend to think about the most
is the one in our module’s build.gradle
file, such as app/build.gradle
,
such as this closure:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
Here, there are three types of statements:
implementation
says “here is a dependency that I want to use for everything”androidTestImplementation
says “here is a dependency that I want to use
for instrumentation tests”testImplementation
says “here is a dependency that I want to use for
unit testing”There are other possibilities, and we will be exploring those and the testing-related
statements later in this book. For now, we will focus on implementation
statements.
The first of those implementation
statements is:
implementation fileTree(dir: 'libs', include: ['*.jar'])
This pulls in any JAR files that happen to be in the libs/
directory of this
module.
JARs, as you probably know, are libraries containing Java code, as created by
standard Java build tools (javac
, jar
, etc.). For the first decade-plus
of Java’s existence, we distributed reusable bits of code in the form of JAR
files. You would download a JAR from a Web site, drop it into your project,
and through something like this implementation
statement, say that your project
should use the JAR.
In Android, the contents of these JARs are packaged into your APK. And, whatever
public
classes happen to be in those JARs are available to you at compile time.
However, in general, using plain JARs nowadays is considered to be a bad idea. There is no information in a JAR about:
Instead, nowadays, you should try to use artifacts, rather than bare JAR files.
An artifact is usually represented in the form of two files:
Artifacts are housed in artifact repositories. Those repositories not only contain the artifacts, but they organize the artifacts for easy access. This includes organizing them by version, so you can request a specific version of an artifact and get it, instead of an older (or possibly newer) version of that same artifact.
Some artifact repositories are public. A typical Android project will use two such repositories:
There are other general-purpose artifact repositories, such as Maven Central or jitpack.io. There are specialty repositories, such as the one used by the author of this book for his artifacts. And there can be private repositories, such as ones used by organizations for their own private artifacts, used by their private projects.
Beyond what strictly is part of the Android SDK, Google publishes a lot of libraries that developers can opt into by declaring them as dependencies.
These include:
ConstraintLayout
library, which we will explore in
an upcoming chapter
With all that as background, let’s explore a bit more about how those
implementation
statements are working and how you can add your own
for other dependencies that you may want to use.
First, you need to identify the dependencies that address whatever problems you need to solve.
Many dependencies will be covered in this book. Yet more are covered in the aforementioned Android’s Architecture Components book. A few more are in the developer documentation.
The biggest catalog of open source dependencies is the Android Arsenal. Here you can browse and search for dependencies. Each listing will contain links to where you can find out more about that particular library, typically in the form of a GitHub repository.
If you are using one of Google’s libraries, a project created in Android Studio 3.0+ should be set up with the proper artifact repository already. Your project also comes pre-configured to pull from JCenter, where many open source libraries are from. As a result, for the vast majority of libraries, you do not need to configure an additional artifact repository.
But, sometimes you do.
For example, the author of this book has published a number of open source
libraries, collectively referred to as the CommonsWare Android Components (CWAC).
They are published in the author’s own artifact repository. When you visit
the GitHub repository for one of these libraries, you will see that in the
installation instructions, it has a sample repositories
closure:
repositories {
maven {
url "https://s3.amazonaws.com/repo.commonsware.com"
}
}
You can do one of two things:
maven {}
closure to the repositories
closure in the allprojects
closure in your top-level build.gradle
filerepositories
closure to your module’s build.gradle
fileEither of these will make this artifact repository available to you for that module. The first approach makes it available for all modules in your app, which may become useful if later you create a library module.
So, when you review the documentation for a dependency, it should indicate what artifact repository to use, and you need to ensure that you are set up to use that repository.
Each artifact has an identifier made up of three pieces: a group ID, an artifact ID within the group, and a version number.
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
The first implementation
statement shown above pulls in bare JARs located
in your project’s filesystem. The remaining statements in this dependencies
closure represent artifacts. The three ones that begin with com.android
are from
Google, while the one that starts with junit
is an open source testing library
called JUnit.
You will see that all four of those artifacts have the three-part identifier:
com.android.support.constraint
)constraint-layout
)1.0.2
)Technically, the version number can contain wildcards. For example, while
1.0.2
indicates a specific version, 1.0.+
says “pick the latest among
all versions that start with 1.0
”. This is convenient for getting patches,
but it means that on some random day, all of a sudden, you are using a new
version of the library, and that might cause problems. Typically, we do not
use wildcards, but instead just keep tabs on when the artifacts get new
versions. Android Studio will help with this, highlighting artifacts that
have newer versions available.
Then, you just need to add the appropriate implementation
lines for whatever
dependencies that you wish to add. So, for example, if you wanted to add
the CWAC-NetSecurity library to your project, in addition to adding that CWAC
repository to your repositories list, you could add:
implementation 'com.commonsware.cwac:netsecurity:0.4.4'
to your dependencies
closure.
Note that many libraries showing sample code for adding them to your build.gradle
file will show a slightly different syntax:
compile 'com.commonsware.cwac:netsecurity:0.4.4'
That is because Android Studio 3.0 and Gradle 4.1 switched to a new syntax for
specifying dependencies. compile
was replaced by implementation
. If you
use compile
, your Gradle build script will still work… for now. Eventually,
support for compile
will be dropped, and so you should aim to use implementation
instead of compile
going forward.
Also note that some of the sample projects in this book will use compile
,
as the book slowly adjusts to the new syntax.
We will see lots of dependencies
closures throughout the rest of this book,
showing different artifacts that we can depend upon.
The Android Support Library is a series of artifacts distributed by Google, containing general-purpose classes (in JARs and Android library projects) that are not part of the Android SDK, but are available to Android developers.
You can roughly divide the contents of the Android Support Library into two major areas:
Example artifacts of the Android Support Library include:
appcompat-v7
, which is a backport of the action bar, a concept that we will
discuss in an upcoming chapter
recyclerview-v7
, which is the home of the RecyclerView
widget
that serves as an alternative to ListView
and GridView
support-compat
, with compatibility classes to make it easier to support both
old versions of Android and new onessupport-core-ui
, offering some widgets and containers,
such as ViewPager
support-fragment
, providing a backport of fragments
What this book refers to as the “Android Support Library” has many names.
It was originally referred to as the Android Compatibility Library, at a time when it only contained backports. Once Google started adding in things that were not strictly related to “compatibility”, they started changing the name to try to be more generic. Right now, “Android Support” seems to be fairly consistent, either used standalone or in the form of “Android Support Library” or “Android Support Libraries”.
Some artifact names have version suffixes, like recyclerview-v7
. Others do
not, such as support-fragment
. The -v7
naming convention used to indicate
the minSdkVersion
supported by that artifact. Originally, recyclerview-v7
worked back to API Level 7 and did not support older versions of Android
than that.
However, that was a fairly inflexible system. Not only has Google abandoned it for newer artifacts, but the actual value no longer has meaning. In particular, the entire Android Support Library only supports back to API Level 15 at this point, with a few exceptions that do not go back even that far.
Google is now distributing its libraries via their own Maven-style artifact
repository. With Android Studio 3.0, this repository is what you get from
that google()
statement in the repositories
closures.
For most libraries for most versions that you are likely to encounter,
the google()
repository will have what you need. You can visit
the Google Maven Repository
site to see what artifacts and versions are served from it.
Originally, these libraries were distributed via the Android Support Repository, one that you would have on your own hard drive, downloaded via the SDK Manager.
Typically, and historically, developers would take a three-tier approach towards the version of these libraries:
compileSdkVersion
25.3.0
to 25.3.1
— developers would update to that version, as usually the patches
fix bugscompileSdkVersion
,
developers would move to the matching major version of the libraries (e.g., when
moving to compileSdkVersion 26
, switch to version 26.1.0
of the libraries)That is still a fine pattern… if your minSdkVersion
is 15 or higher. For
many developers, that will be the case.
However, if you are aiming to still support Android 2.x devices, you have a problem:
starting with the v26 edition of the Support Library, the oldest version of
Android supported by the libraries is Android 4.0.3, API Level 15.
This means that you will need to stick with compileSdkVersion 25
and the v25
edition of the Support Library until such time as you either:
minSdkVersion
to 15 or higherYou can add references to the Android Support Library’s libraries — whether those
libraries are simple JARs or Android library projects — via a few lines in your
dependencies
closure, referencing the artifacts from the Android Support Repository.
Here are the implementation
statements for some of the artifacts in
the Android Support Library:
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:cardview-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:exifinterface:28.0.0'
implementation 'com.android.support:gridlayout-v7:28.0.0'
implementation 'com.android.support:leanback-v17:28.0.0'
implementation 'com.android.support:mediarouter-v7:28.0.0'
implementation 'com.android.support:palette-v7:28.0.0'
implementation 'com.android.support:percent:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.android.support:support-annotations:28.0.0'
Also, while you could add all of these to your project, that is not necessary. Only attach dependencies for libraries that you are actually using. Having unused libraries in your project just increases your APK size for no good reason. Hence, most projects will have only a subset of the aforementioned lines.
Note that, in general, when using the Android Support libraries,
you should set your compileSdkVersion
and targetSdkVersion
to be the same as the major
version of the library. So, for a 26.1.0
version of the library,
your compileSdkVersion
should be 26
.