Dependencies

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.

What’s a Dependency?

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.

Dependency Scopes

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:

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.

Depending on a Local JAR

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.

What’s an Artifact?

An artifact is usually represented in the form of two files:

Artifacts and Repositories

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.

Major Library Families from Google

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:

Requesting Dependencies

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.

Find What You Need

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.

Configure the Repositories

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:

  1. Add that maven {} closure to the repositories closure in the allprojects closure in your top-level build.gradle file
  2. Add the entire sample repositories closure to your module’s build.gradle file

Either 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.

Identify the Dependencies and Versions

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:

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.

Add the Dependencies

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

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.

What’s In There?

You can roughly divide the contents of the Android Support Library into two major areas:

  1. “Backports” of capabilities added to newer versions of Android and the Android SDK, so they can be used on older devices as well. By using the backported classes, you can get the same abilities on a wider range of devices than you could if you only used the classes in the Android SDK.
  2. New widgets, containers, or other classes that are not going to be in the Android SDK (for ill-defined reasons) but that Google wishes to make available for Android developers.

Example artifacts of the Android Support Library include:

About the Names

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”.

About the -v Suffixes

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.

Getting It

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.

Choosing a Version

Typically, and historically, developers would take a three-tier approach towards the version of these 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:

Attaching It To Your Project

You 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.