Dependencies

Artifacts may depend on other artifacts within AndroidX as well as sanctioned third-party libraries. Additionally, artifacts may have toolchain dependencies that are not explicitly specified in their dependencies build configuration or don't appear in their Maven publications (pom or module files).

Versioned artifacts

One of the most difficult aspects of independently-versioned releases is maintaining compatibility with public artifacts. In a monorepo such as Google‘s repository or Android Git at main revision, it’s easy for an artifact to accidentally gain a dependency on a feature that may not be released on the same schedule.

To address this problem, library owners in AndroidX can choose from several types of dependencies:

  • Project project(":core:core") uses the tip-of-tree sources for the androidx.core:core library and requires that they be loaded in the workspace and released at the same time.
  • Playground projectOrArtifact(":core:core") is used for the Playground workflow and will use tip-of-tree sources, if present in the workspace, or SNAPSHOT prebuilt artifacts from androidx.dev otherwise.
  • Pinned "androidx.core:core:1.4.0" uses the prebuilt AAR and requires that it be checked in to the prebuilts/androidx/internal local Maven repository or, when using the Playground workflow, the remote Google Maven repository.

Libraries should prefer pinned dependencies with the lowest possible versions that include the APIs or behaviors required by the library, and should only use project or Playground specs in cases where tip-of-tree APIs or behaviors are required.

Do not upgrade the version of a library's dependency to artificially boost adoption of that version.

Pre-release dependencies

Pre-release suffixes must propagate up the dependency tree. For example, if your artifact has a dependency on an artifact versioned 1.1.0-alpha01 then your artifact must also carry the alpha suffix.

NOTE This does not apply to test dependencies: suffixes of test dependencies do not carry over to your artifact.

Pinned versions

To avoid issues with dependency versioning, pin your dependencies to the oldest stable version of an artifact that includes the necessary APIs. This will ensure that the artifact's release schedule is not accidentally tied to that of another artifact and will allow developers to use older libraries if desired.

dependencies {
   api("androidx.collection:collection:1.0.0")
   ...
}

Artifacts are built and tested against both pinned and tip-of-tree versions of their dependencies to ensure behavioral compatibility.

Tip-of-tree versions

Below is an example of a project dependency, which uses tip-of-tree sources for the dependency rather than a prebuilt JAR or AAR. It ties the artifact's release schedule to that of the dependency artifact because the dependency will need to be released at the same time.

dependencies {
   api(project(":collection"))
   ...
}

Non-public APIs

Artifacts may depend on non-public or restricted APIs exposed within their own artifact or another artifact in the same groupId; however, cross-artifact usages are subject to binary compatibility guarantees. See @RestrictTo APIs for more details.

Dependency versioning policies are enforced at build time in the createArchive task, which ensures that pre-release version suffixes are propagated appropriately. Cross-artifact API usage policies are enforced by the checkApi and checkApiRelease tasks.

Third-party libraries

Artifacts may depend on libraries developed outside of AndroidX; however, they must conform to the following guidelines:

  • Prebuilt must be checked into Android Git
    • prebuilts/maven_repo is recommended if this dependency is only intended for use with AndroidX artifacts, otherwise please use external
  • Prebuilt directory must contains an OWNERS file identifying one or more individual owners (e.g. NOT a group alias)
  • Library must be approved by legal

Please see Jetpack's open-source policy page for more details on using third-party libraries.

Platform extension (sidecar JAR) dependencies

Platform extension or “sidecar JAR” libraries ship as part of the Android system image and are made available to developers through the <uses-library> manifest tag.

Examples include Camera OEM extensions (androidx.camera.extensions.impl) and Window OEM extensions (androidx.window.extensions).

Extension libraries may be defined in AndroidX library projects (see androidx.window.extensions) or externally, ex. in AOSP alongside the platform. In either case, we recommend that libraries use extensions as pinned, rather than project-type, dependencies to facilitate versioning across repositories.

Do not ship extension interfaces to Google Maven. Teams may choose to ship stub JARs publicly, but that is not covered by AndroidX workflows.

Project dependencies on extension libraries must use compileOnly:

build.gradle:

dependencies {
    // Extension interfaces defined in Jetpack
    compileOnly(project(":window:extensions:extensions"))

    // Extension interfaces defined in a stub JAR
    compileOnly(
        fileTree(
            dir: "../wear_stubs",
            include: ["com.google.android.wearable-stubs.jar"]
        )
    )
}

Documentation dependencies must use the stubs configuration:

docs-public/build.gradle:

dependencies {
  stubs("androidx.window:window-extensions:1.0.0-alpha01")
  stubs(
    fileTree(
        dir: "../wear/wear_stubs/",
        include: ["com.google.android.wearable-stubs.jar"]
    )
  )
}

See Packaging and naming for details about defining extension interfaces in Jetpack projects.

Types of dependencies

AndroidX allows dependencies to be specified as api or implementation with a “pinned” Maven spec (ex. androidx.core:core:1.0.0) or a “tip-of-tree” project spec (ex. project(":core:core")).

Projects used in Playground, the experimental GitHub workflow, should use a “recent” project or artifact spec (ex. projectOrArtifact(":core:core")) which will default to tip-of-tree when used outside of the Playground workflow or a pinned SNAPSHOT artifact otherwise.

Regardless of which dependency spec is used, all projects are built against tip-of-tree dependencies in CI to prevent regressions and enforce Jetpack's compatible-at-head policy.

api versus implementation

api-type dependencies will appear in clients' auto-complete as though they had added the dependency directly to their project, and Studio will run any lint checks bundled with api-type dependencies.

Dependencies whose APIs are exposed in a library‘s API surface must be included as api-type. For example, if your library’s API surface includes AccessibilityNodeInfoCompat then you will use an api-type dependency on the androidx.core:core library.

NOTE Libraries that provide client-facing lint checks, including annotation-experimental, must be included as api-type to ensure that lint checks are run in the clients' dependent projects.

implementation-type dependencies will be included in the classpath, but will not be made available at design time (ex. in auto-complete) unless the client explicitly adds them.

Constraints

Dependency constraints ensure that when certain libraries are used together, they meet certain requirements. Defining a constraint on a library does not pull that library into the classpath.

In Jetpack, we use constraints to ensure that atomically-grouped libraries all resolve to the same version (example) and, in rare cases, to avoid conflicts resulting from classes being moved between artifacts (example).

core/core-ktx/build.gradle:

dependencies {
    // Atomic group
    constraints {
        implementation(project(":core:core"))
    }
}

In extremely rare cases, libraries may need to define a constraint on a project that is not in its studiow project set, ex. a constraint between the Paging and Compose libraries. As a workaround, libraries may hard-code the Maven coordinate using a version variable (example) to indicate the tip-of-tree version.

paging/paging-common/build.gradle:

dependencies {
    // Atomic group
    constraints {
        implementation("androidx.paging:paging-compose:${LibraryVersions.PAGING_COMPOSE}")
    }
}

Dependency considerations

Generally, Jetpack libraries should avoid dependencies that negatively impact developers without providing substantial benefit. Libraries should consider the implications of their dependencies, including:

  • Large dependencies where only a small portion is needed (e.g. APK bloat)
  • Dependencies that slow down build times through annotation processing or compiler overhead
  • Dependencies which do not maintain binary compatibility and conflict with semantic versioning guarantees
  • Dependencies that are intended for server environments and don't interact well with the Android build toolchain (e.g. R8) or runtime (e.g. ART)

Kotlin

Kotlin is strongly recommended for new libraries and the Kotlin stdlib will already be present in the transitive dependencies of any library that depends on androidx.annotations.

plugins {
    id("AndroidXPlugin")
    id("kotlin-android")
}

dependencies {
    implementation(libs.kotlinStdlib)
}

Java-based libraries may migrate to Kotlin, but they must be careful to maintain binary compatibility during the migration. Metalava does not cover all possible aspects of migration, so some manual work will be required.

Kotlin reflect

Reflection in libraries is only allowed for backwards compatibility support and those users should use Java reflection. kotlin-reflect is very costly at runtime and should never be used.

Kotlin coroutines

The Kotlin coroutines library adds around 100kB post-shrinking. New libraries that are written in Kotlin should prefer coroutines over ListenableFuture, but existing libraries must consider the size impact on their clients. See Asynchronous work with return values for more details on using Kotlin coroutines in Jetpack libraries.

dependencies {
    implementation(libs.kotlinCoroutinesAndroid)
}

GSON

GSON relies heavily on reflection and interacts poorly with app optimization tools like R8. Instead, consider using org.json which is included in the Android platform SDK.

Guava

The full Guava library is very large and should only be used in cases where there is a reasonable assumption that clients already depend on full Guava.

For example, consider a library androidx.foo:foo implemented using Kotlin suspend funs and an optional androidx.foo:foo-guava library that provides ListenableFuture interop wrappers with a direct dependency on kotlinx.coroutines:kotlinx-coroutines-guava and a transitive dependency on Guava.

Libraries that only need ListenableFuture may instead depend on the standalone com.google.guava:listenablefuture artifact. See Asynchronous work with return values for more details on using ListenableFuture in Jetpack libraries.

Protobuf

Note: It is preferred to use the wire library for handling protocol buffers in Android libraries as it has a binary stable runtime. An example of its usage can be found here.

Protocol buffers provide a language- and platform-neutral mechanism for serializing structured data. The implementation enables developers to maintain protocol compatibility across library versions, meaning that two clients can communicate regardless of the library versions included in their APKs.

The Protobuf library itself, however, does not guarantee ABI compatibility across minor versions and a specific version must be used with a library to avoid conflict with other dependencies used by the developer. To do this, you must first create a new project to repackage the protobuf runtime classes, and then have it as a dependency in the project you generate protos in. In the project that generates protos, you must also relocate any import statements containing com.google.protobuf to your target package name. The AndroidXRepackagePlugin abstracts this for you. An example of its use to repackage the protobuf runtime library can be found here and its associated use in the library that generates protos can be found here.

Additionally, the Java API surface generated by the Protobuf compiler is not guaranteed to be stable and must not be exposed to developers. Library owners should wrap the generated API surface with well-documented public APIs that follow an appropriate language-specific paradigm for constructing data classes, e.g. the Java Builder pattern.

Open-source compatibility

Jetpack's open-source guidelines require that libraries consider the open-source compatibility implications of their dependencies, including:

  • Closed-source or proprietary libraries or services that may not be available on AOSP devices
  • Dependencies that may prevent developers from effectively isolating their tests from third-party libraries or services

Primary artifacts, e.g. workmanager, must not depend on closed-source components including libraries and hard-coded references to packages, permissions, or IPC mechanisms that may only be fulfilled by closed-source components.

Optional artifacts, e.g. workmanager-gcm, may depend on closed-source components or configure a primary artifact to be backed by a closed-source component via service discovery or initialization.

Some examples of safely depending on closed-source components include:

  • WorkManager's GCM Network Manager integration, which uses manifest metadata for service discovery and provides an optional artifact exposing the service.
  • Downloadable Fonts integration with Play Services, which plugs in via a ContentProvider as a service discovery mechanism with developer-specified signature verification for additional security.

Note that in all cases, the developer is not required to use GCM or Play Services and may instead use another compatible service implementing the same publicly-defined protocols.

Toolchain dependencies

Toolchain dependencies are typically specified by the AndroidX build system and are often limited, if any, configuration on behalf of library owners.

Kotlin language

Several projects within AndroidX depend on aspects of the Kotlin compiler that do not guarantee binary compatibility, which means (1) Kotlin updates within AndroidX may be more complicated and (2) Kotlin updates may be more complicated for external clients.

For this reason, we try to separate (1) and (2) by pinning the Kotlin language and API versions until the new compiler has been in use in AndroidX for at least three months.

Library owners may in limited cases update their Kotlin language version early by specifying the kotlinVersion DSL property:

androidx {
    kotlinVersion KOTLIN_1_9
}

Note that this propagates the version requirement to all dependencies and is not appropriate for low-level libraries.

Java language

The Java language level determines the minimum version of the Java runtime required for lint checks and other host-side libraries like compilers.

To avoid causing issues for clients, we try to separate Java compiler or runtime updates from language level by pinning the Java language level to the second most-recent stable LTS version. In extreme cases, however, we may be required to move to a more recent version because of a dependency like AGP or Gradle.

Library owners may, in cases where clients are unable to update their Java version, temporarily pin their Java language version to a lower value by specifying the compatibility DSL properties:

javaExtension.apply {
    // TODO(b/12345678) Remove this once clients are able to update.
    sourceCompatibility = VERSION_17
    targetCompatibility = VERSION_17
}

When doing so, library owners must file a bug and establish a timeline to un-pin and rejoin the rest of AndroidX.

Desugaring and R8/D8

Currently, the highest Java language level supported for Android libraries is Java 1.8 (`VERSION_1_8``) via D8/R8 desugaring. See Use Java 8 language features and APIs for more details.

AndroidX does not currently support library API desugaring, so the use of Java 8 APIs requires increasing the library's minSdk.

Android SDK

The AndroidX Core & Tooling team automatically updates the compileSdk value following the first public release of a stable SDK, e.g. following SDK finalization during the Beta stage of platform SDK development.

Library owners must not attempt to pin their compileSdk to a lower value.

Libraries that are developed against extension SDKs may pin their compileSdk to a higher value, e.g. 34-ext5 when the rest of AndroidX is using 34.