Like and follow, no longer get lost, your support means a lot to me!

🔥 Hi, I am Ugly. This article has been collected by GitHub · Android-Notebook. Welcome to grow with Peng Chouchou. (Contact info on GitHub)


preface

  • Nowadays, project development is no longer an era of individual operations, but often multi-team, multi-component development. At this point, we publish components & tips for managing components;
  • In this article, I will walk you through the basic concepts of components and the practical application experience of component publishing & snapshot preview & dependency switching. Please be sure to like and follow if you can help, it really means a lot to me.

directory


Front knowledge

The content of this article will cover the following preconditions/related knowledge, thoughtful I have helped you to prepare, please enjoy ~

  • Gradle | advanced article (Project & Task & building life cycle)
  • Gradle | custom holding Gradle plug-in

1. Concept analysis

1.1 What is POM?

Project Object Model (POM) is used to describe the basic information of Project components. A valid POM node contains the following information:

configuration describe For instance (‘ com. Making. Bumptech. Glide: glide: 4.11.0 ‘)
groupId Name of the organization/company com.github.bumptech.glide
ArtifactId ([ˈ ɑ ː t ɪ f æ kt]) Component name glide
version Component version 4.11.0
packaging Packaging format aar

1.2 What is a Repository?

In a project, we need to rely on a variety of two – or three-party repositories, and these dependencies must be stored in a Place called a repository. Using repositories helps us manage project artifacts such as jars, Aar, and so on.

Mainstream build tools have three levels of warehouse concepts:

  • 1. Local repository: Whether using Linux or Windows, the computer will have a directory for storing the dependency files downloaded from the central repository or remote repository;
  • 2. Central repository: The repository provided by the open source community is where the vast majority of open source libraries are stored. Like Maven Central, the Central repository for the Maven community;
  • 3. Private warehouse: the customized warehouse of the company or organization, which can be understood as the storage location of the two-party warehouse.

Dependencies are searched in the following order at build time:

  • 1. Search the local repository. If the local repository cannot be searched, go to Step 2.
  • 2, in the central warehouse and private warehouse search, search order according torepositoriesIn the order declared in. If yes, download the dependent file to the local repository. Otherwise, go to Step 3.
  • 3. If the dependency is not found, an error “Cannot find dependency” is thrown.

How to declare a repository in a project:

Gradle default local repository directory in Windows: C: / Users/Administrator /. Gradle/caches/files/modules – 2-2.1 and under Mac OS is/User/User name /. Gradle/caches/modules/files – 2.1-2. Gradle does not execute remote repositories and central repositories by default. They need to be declared in a build. Gradle file at the project or module level. For example: 0.

Project level build.gradle

Buildscript {repositories {[repository for Gradle plugin]}} allprojects {repositories {[repository for all modules]}}Copy the code

Module level build.gradle

Repositories {[repository for current module dependencies]}Copy the code

Gradle supports multiple types of repositories such as Maven, Ivy, and flatDir. FlatDir specifies the address of the local AAR file. More on this in Section 4.2.

repositories{ maven { url '... ' } ivy { url '... ' } flatDir { dirs '... '}}Copy the code

Gradle has built-in paths to common central repositories that can be accessed directly from functions. Such as:

google()        // https://dl.google.com/dl/android/maven2/
mavenCentral()   // https://repo.maven.apache.org/maven2/
jCenter()       // https://jcenter.bintray.com/
Copy the code

Sometimes, the speed of direct access to the central warehouse is too slow. In this case, try to replace the central warehouse image of a large domestic factory. Such as:

maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' }
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter' }
Copy the code

1.3 What is Gradle plugin?

The concept of “Gradle plugin” and “Gradle” can be quite confusing. Gradle is a build tool that acts as a build environment; Gradle plug-in is essentially a concrete build task, we will build a task modularized out, provided to other projects reuse, is a Gradle plug-in. Such as:

  • Com.android. application: Android application plug-in
  • Com.android. library: Android module plug-in
  • Kotlin – Android: The Android Kotlin plugin
  • Maven & Maven-pulish: A maven plugin

1.4 What is SNAPSHOT used for?

A snapshot is a special version with the biggest difference from a regular version: the snapshot version checks the latest snapshot in the remote repository each time it is built.

  • SNAPSHOT version: 1.0.0-snapshot
  • General version: 1.0.0

Why this design (sacrificing compile time)? In A large software project, multiple teams (or students) often develop different modules together. For example, module A depends on module B, and the two modules are developed in parallel. If module B does not use the snapshot version (for example, version 1.0.0), module A will not receive updates when module B needs to be updated during development. Because the 1.0.0 version of module B has already been downloaded from the local repository of module A, the updated version from the remote repository will not be downloaded repeatedly during the build.

The direct solution is to clear the local repository cache of module A, or to upgrade the version of module B every time it is updated. Obviously, both methods are not flexible, and frequent upgrade is also an abuse of the version number, which is not good for version management. If module B uses the SNAPSHOT version (1.0.0-snapshot), module A checks the remote repository for A new SNAPSHOT of module B every time it is built, ensuring that it always relies on the latest version of module B.

In general, SNAPSHOT is suitable for the fast-paced co-development phase and represents an unstable & in-development version. The regular version is suitable for the official release. If the official release uses SNAPSHOT, it will cause instability of repeated builds of the official version.


2. Maven build lifecycle

We need to use the Maven plugin to publish the libraries. To understand the Maven build lifecycle, we need to follow the following steps:

task phase describe
compile compile Compile source code
test test Executing unit tests
package packaging Create publishing components, such as JARS, aar
install The installation Install component packages to the local repository
deploy / upload The deployment of Upload the component package to the remote repository

— Picture from the Internet


3. How do I publish artifacts?

To publish a component in Gradle, you can use the following two Maven plug-ins:

  • Maven Plugin (old version)
  • Maven Publish Plugin

3.1 Publishing to a Local Repository

We need to use the uploadArchives task of the Maven plugin, and we need to specify the component information. Such as:

Module level build. Gradle

plugins { id 'com.android.library' id 'kotlin-android' id 'maven' } ... UploadArchives {repositories {mavenDeployer {repository(URL: URI ('.. // repository')) Com. Pengxr. Demo: maven: v1.0.0 pom. The groupId = "com. Pengxr. Demo" pom. ArtifactId = "maven pom. Version =" v1.0.0 "}}}Copy the code

After executing Gradle Sync, you can find a task named uploadArchives in the Tasks list of the module in the Gradle window. Perform the task, and when completed, the project will add a Repository directory containing the newly released components.

Notes:

  • If you can’t find the Task list in the Gradle column after upgrading to Android Stidio 4.2, uncheck this item in the Settings:

  • 2. The application module cannot be published
Plugins {id' com.android.application'} plugins {id' com.android.application'}Copy the code

3.2 Setting up a Private Warehouse using Nexus

Publishing components to a local repository can only be used on a standalone basis. In practice, we often need to publish components to other team members. At this point, the component can be published to a LAN private repository. The most common private warehouse management tool is Nexus [ˈneksəs]. Perform the following steps to set up the environment:

  • 1. Download the Nexus installation package: This section uses the Mac environment as an example.

  • 2. Start the Nexus service process: Go to installation directory /nexus-3.30.1-01/bin and run the following command on the terminal:

/nexus start./ Nexus status To stop the service, run the./nexus stop commandCopy the code
  • 3. Open the browserhttp://127.0.0.1:8081/, enter theNexus Management Page

  • 4. Click Sign in in the upper right corner to log in: The default account name is admin. After the first login, a message will be displayed indicating the storage location of the password (copy and paste the password from the file in the corresponding path as instructed).

This list contains all of the Nexus repositories. Click the “Copy” button to Copy the repository URLS. Two of them are commonly used:

Maven-release: Repository of host types with a release policy, used to deploy released versions of internal components; Maven-snapshots: Snapshots of the Shapshot host type and used to deploy snapshots of internal components.

Types: Group (warehouse group), Hosted (host), proxy (proxy), and Virtual (virtual);

Format: maven1, maven2, and nuGET

  • 5. Publish to the specified repository: Add configuration to build.gradle at module level:

Module level build. Gradle

Maven plugin: 'maven' // maven plugin... UploadArchives {repository {repository} // repositories {repository {repository} // repository "Http://127.0.0.1:8081/repository/maven-releases/") {authentication (userName: "admin" and password: Pom.groupid = "com.pengxr.demo" pom.artifactid = "maven" pom.version = "v1.0.0"}}}Copy the code

You can see the newly released library on the Nexus Management platform after the successful release:

  • 6. Dependency libraries: Declare the remote repository in build.gradle at project level and depend on the class libraries in build.gradle at module level.

Project level build. Gradle

MavenCentral allprojects {repositories {Google () () the maven {url "http://127.0.0.1:8081/repository/maven-releases/"}} }Copy the code

Module level build. Gradle

dependencies { ... Implementation 'com. Making. Pengxurui: MavenPuhlish: 1.0.4'}Copy the code

Tip: Of course, in a real project the Nexus will not be configured on the local machine, but on a LAN server.

3.3 Publishing to the Github repository

If you need to open source, then you need to publish to the public repository. This section describes the steps to publish to Github:

  • Add dependencies to the Github Maven plugin in the project level build.gradle:

Project level build. Gradle

dependencies { ... The classpath "com. Making. Dcendents: android - maven - gradle - plugin: 1.5" / / / / making maven plug-in}Copy the code
  • 2. Apply Github Maven plugin to build. Gradle of the release module:

Module level build. Gradle

Apply the plugin: 'com. Making. Dcendents. Android - maven' / / making maven plug-insCopy the code
  • Declare the groupId of the component in the build.gradle of the publishing module:

Module level build. Gradle

Apply the plugin: 'com. Making. Dcendents. Android - maven' / / making maven plug-in group = 'com. Making. Pengxurui' / / lot user nameCopy the code
  • Push the project to Github

  • Create a Release Tag on Github (also create a local Tag and push it to Gtihub) :

  • 6. Upload the project to the JitPack: Open jitpack. IO /, copy the project link to the input field, click Look Up, and wait for the compilation to complete. And that’s it.

  • 7. Dependency libraries: Declare the remote repository in build.gradle at project level and depend on the library in build.gradle at module level.

Project level build. Gradle

allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url "https://jitpack.io" }
    }
}
Copy the code

Module level build. Gradle

dependencies { ... Implementation 'com. Making. Pengxurui: MavenPuhlish: 1.0.4'}Copy the code

Stomping pit record:

  • Delete the uploadArchives task after applying the Github Maven plugin to build.gradle, otherwise the build will report an error.
Exception is: java.lang.IllegalAccessError: tried to access method org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier.<init>(Ljava/lang/String; Ljava/lang/String; Ljava/lang/String;) V from class org.gradle.api.plugins.AndroidMavenPlugin$8Copy the code

3.4 Publishing binaries

With the new Maven plug-in, you can publish components directly as specified binaries. Such as:

apply plugin: 'maven-publish' publishing {publications {[task name](MavenPublication) {groupId MAVEN_GROUP_ID artifactId MAVEN_ARTIFACTID Version MAVEN_VERSION artifact([file path])}} Repositories {repositories {MAVEN_RELEASE_URL}}}Copy the code

4. Practical application

4.1 Encapsulating generic release scripts

As projects become more componentized and more components need to be published to Maven repositories, it becomes necessary to encapsulate Maven’s publishing capabilities into a common script. Here are the steps:

  • Step 1: Encapsulate the release script:

maven.gradle

apply plugin: 'maven' uploadArchives {repositories {mavenDeployer {def isSnapShot = bool.valueof (MAVEN_IS_SNAPSHOT) Def versionName = MAVEN_VERSION if (isSnapShot) {versionName += "-snapshot"} component information POM. groupId = MAVEN_GROUP_ID Version = versionName // snapshotRepository path snapshotRepository(url: uri(MAVEN_SNAPSHOT_URL)) { authentication(userName: MAVEN_USERNAME, password: Repository (url: uri(MAVEN_RELEASE_URL)) {authentication(userName: MAVEN_USERNAME, password: MAVEN_USERNAME) } println("###################################" + "\nuploadArchives = " + pom.groupId + ":" + pom.artifactId + ":" + pom.version + "." + pom.packaging + "\nrepository =" + (isSnapshot ? MAVEN_SNAPSHOT_URL : MAVEN_RELEASE_URL) + "\n###################################" ) } } }Copy the code

The script reads the MAVEN_IS_SNAPSHOT configuration parameter and, if true, apends the -snapshot suffix to the version number to indicate the SNAPSHOT version. Then two repositories are declared: repository(…) SnapshotRepository (…) The snapshot repository address is declared. Maven automatically publishes components with version numbers with the -snapshot suffix to snapshotRepository(…) Repository, which automatically distributes the official and snapshot versions to different repositories.

Of course, instead of snapshotRepository(…) There are ways to do this:

def url = isSnapShot ? MAVEN_SNAPSHOT_URL : MAVEN_RELEASE_URL
repository(url: url) {
    authentication(userName: MAVEN_USERNAME, password: MAVEN_USERNAME)
}
Copy the code
  • Step 2: Declare project-level configuration parameters:

Project level gradle. The properties

MAVEN_SNAPSHOT_URL = /Users/pengxurui/workspace/public/DemoHall/snapshotRepository
MAVEN_RELEASE_URL = /Users/pengxurui/workspace/public/DemoHall/releaseRepository
MAVEN_USERNAME = 
MAVEN_PASSWORD = 
MAVEN_IS_SNAPSHOT = true
MAVEN_GROUP_ID = com.pengxr.demo
...
Copy the code
parameter describe
MAVEN_SNAPSHOT_URL Snapshot repository address
MAVEN_RELEASE_URL Publish warehouse address
MAVEN_USERNAME Warehouse account
MAVEN_PASSWORD Warehouse password
MAVEN_IS_SNAPSHOT Snapshot version or not
MAVEN_GROUP_ID Name of the organization/company
MAVEN_ARTIFACTID The name of the component (in the publish module configuration)
MAVEN_VERSION The version of the component (in the release module configuration)
  • Step 3: Apply the script in the publishing module

Module level build. Gradle

apply from: '.. /maven.gradle' ...Copy the code
  • Step 4: Publish module configuration parameters (module level configuration parameters overwrite project level configuration parameters)

Module level gradle. The properties

MAVEN_ARTIFACTID = maven MAVEN_VERSION = v1.0.0 MAVEN_IS_SNAPSHOT = true...Copy the code

After completing the above steps and Sync, you can find the uploadArchives task under this module in the Gradle window and publish:

Executing tasks: [uploadArchives] in project /Users/pengxurui/workspace/public/DemoHall/MavenPublish/lib > Configure project :lib # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # uploadArchives = com. Pengxr. Demo: maven: v1.0.0 - the SNAPSHOT. Jar repository =/Users/pengxurui/workspace/public/DemoHall/snapshotRepository ################################### > Task :lib:preBuild UP-TO-DATE ...Copy the code
  • Step 5: Dependent components: declare the dependency repository in build.gradle at project level, and declare the dependency in build.gradle at module level:

Project level build. Gradle

allprojects { repositories { maven { url MAVEN_RELEASE_URL } maven { url MAVEN_SNAPSHOT_URL } ... }}Copy the code

Module level build. Gradle

Dependencies {implementation "com. Pengxr. Demo: maven: v1.0.0 +"}Copy the code

The + (+) in version v1.0.0+ indicates the maximum version number that depends on. The official version takes precedence. For example, there are three v1.0.0, V1.0.0.1, and V1.0.1-snapshot libraries in the remote repository, then v1.0.0+ depends on v1.0.0.1.

What’s the difference between + and -snapshot?

The + sign affects the selection of the library version, while -snapshot affects whether to update the latest version to the remote repository.

The complete code and demo project you can download directly to view: MavenPublish download path. The warehouses configured in the Demo are all local warehouses. In the actual project, you need to replace them with private warehouses in your company.

4.2 Referencing the Local AAR package

Sometimes we rely directly on aar files provided by third or second parties. Such as:

- aarlib \ libs - lib-debug-aar - build.gradle // api(name: 'lib-debug', ext: 'the aar) output: Unable to resolve the dependency for' : aarlib @ debugUnitTest/compileClasspath: Could not resolve: lib - the debug.Copy the code

However, this does not succeed. You need to declare the Flat Directory Directory address of the AAR in the build.gradle file. You can put it inside the Android {} node, or directly in the root node, and the effect is the same. Such as:

Aarlib module build. Gradle

dependencies {
    ...
    api(name: 'lib-debug', ext: 'aar')
}

repositories {
    flatDir {
        dirs "libs"
    }
}
Copy the code

Now you can rely on it successfully. However, if there is another aarlib-dependent module that needs to rely on lib-debug.aar, there will still be a problem:

- app
  - build.gradle // implementation project(':aarlib')
|
- aarlib
  \ libs
      - lib-debug-aar
  - build.gradle // api(name: 'lib-debug', ext: 'aar')
Copy the code

At this point, you also need to declare the Flat Directory Directory address of the AAR in the app module.

App module build. Gradle

dependencies {
    ...
    implementation project(':aarlib')
}

repositories {
    flatDir {
        dirs project(':aarlib').file('libs')
    }
}
Copy the code

4.3 Referencing a Local AAR Package (Optimized)

If the project component structure is simple, the approach in Section 4.2 (p. 42) is sufficient to address the problem of local references to aArs. You need to declare repositories. FlatDir {} in each module’s build.gradle. Is there any way to make it better?

  • Method 1: Change direct dependency to indirect dependency: Create a module to encapsulate an AAR and provide an API for external appearance

  • Method 2: Place aar files in one folder and declare the repository address in the project level build.gradle:

Project level build. Gradle

Allprojects {repositories {Google () mavenCentral() flatDir {dirs project(':aarlib').file('libs')} allprojects {repositories {Google () mavenCentral() flatDir {dirs project(':aarlib').file('libs') Within}}Copy the code

Module level build. Gradle

API (name: 'lib-debug', ext: 'aar') implementation(name: 'lib-debug', ext: 'aar') // Do not allow indirect dependencies on AARCopy the code
  • Method 3: Secondary packaging aar: The above method works well for a single project, but if your project includes multiple projects, it is still a bit troublesome. Is there any way to optimize it? You can repackage aar files and publish them to Maven repositories, so you don’t need to declare Flat local repositories.
- aarpacker
  \ libs
      - lib.aar
      - lib2.aar
  - build.gradle
Copy the code

aarpacker build.gradle

apply plugin: 'maven-publish' def libPath = project.getProjectDir().getAbsolutePath() publishing { publications { Lib1 (MavenPublication) {groupId MAVEN_GROUP_ID artifactId "lib" version "v1.0.0" artifact(libPath + "/libs/lib.aar")} Lib2 (MavenPublication) {groupId MAVEN_GROUP_ID artifactId "lib2" version "v1.0.0" artifact(libPath + "/libs/lib2.aar") }} publication 'maven' Failed to publish publication 'maven' to repository 'maven' // > Authentication scheme 'all'(Authentication) is not supported by protocol 'file' // credentials(PasswordCredentials) { // username = MAVEN_USERNAME // password = MAVEN_PASSWORD // } } } }Copy the code

4.4 Merge references to Library Modules

When the Library Module is compiled, the AAR file is eventually generated, but it does not contain code or resources from other Library Modules that compile/implementation references. However, in componentization development, there are times when the AAR file we want to publish needs to contain the contents of the Library Module. How to do this? There are two ways:

  • Method 1: Python script merge: Write a Python script to decompress all related resources into a Library Module and regenerate the AAR file
  • Method 2: Gradle compilation task: adjust Gradle compilation task to realize all related resources are packaged into aar.

4.5 Host project debug class library

Add switch field:

dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) if (useLocalLib.toBoolean()) implementation project(":lib") else implementation 'com. Pengxr. Demo: maven: v1.0.0 +'}Copy the code

The resources

  • Maven, Maven Plugin, Maven Publish Plugin, jitpack. IO — official documents
  • Maven tutorial – Novice tutorial
  • Android Studio publishing projects to Maven repositories (the latest and most complete 3 ways) – by Open-Xu
  • Use Gradle to publish artifacts to the Maven repository – by hardbound Mechanics
  • Elegant debugging of SDK with Maven — written by the technical team of The Dance group
  • 51 Credit Card Android Architecture Evolution Practices — by 51 Technical Team
  • The Componentized Architecture of Android (Chapter 6) — by Cang Wang

Creation is not easy, your “three company” is the biggest power of Ugly, we see next time!