Build tools

1. What are build tools?

In programming operations, we often encounter a lot of non-programming project management work. Such as downloading dependencies, compiling source code, unit testing, project deployment, and so on. In general, small projects we can do this manually, while large projects are more complicated. So we need build tools. Build tools are tools that help us implement a range of project management, testing, and deployment operations. A program that automates the process of generating executable applications from source code (e.g., Android App generation APK). Build tools include compiling, wiring, and packaging code into usable or executable form.

In general, build automation is the act of writing or automating a large number of tasks that are routine for software developers, such as:

  • Download dependencies.
  • Compile the source code into binary code.
  • Package the generated binaries.
  • Unit testing.
  • Deploy to the production system.

2. Why use build tools?

For example, if you want to write a Java program, the usual steps are compile, test, and package. If the files are small, we can do this manually using Java, javac, jar commands. But when the project is more and more big, the file is more and more, this thing, it is not so nice. Because these commands tend to be very mechanical. But we can leave mechanical things to machines.

There are three major build tools in the Java world: Ant, Maven, and Gradle.

Java build tools

1. Ant, a common build tool for the Java platform.

Ant is a build tool written in Java. Its core code is written in Java, so it is platform-independent. The build script is in XML format (the default is bulid.xml). For example, here is a list of build.xml used by an Ant tool:


        
<project name="HelloWorld" default="run" basedir=".">  
<property name="src" value="src"/>  
<property name="dest" value="classes"/>  
<property name="jarfile" value="hello.jar"/>  
<target name="init">  
   <mkdir dir="${dest}"/>  
</target>  
<target name="compile" depends="init">  
   <javac srcdir="${src}" destdir="${dest}"/>  
</target>  
<target name="build" depends="compile">  
   <jar jarfile="${jarfile}" basedir="${dest}"/>  
</target>  
<target name="test" depends="build">  
   <java classname="test.ant.HelloWorld" classpath="${hello_jar}"/>  
</target>  
<target name="clean">  
   <delete dir="${dest}" />  
   <delete file="${hello_jar}" />  
</target>  
</project> 
Copy the code

The Ant build script is fairly clear. Ant defines five tasks: init, compile, Build, test, and clean. What each task does is clearly defined. You need to compile before packaging, so specify the dependent path through Depends. If you run Ant build on the command line, compile will be executed first, and compile depends on init, so init will be executed first. With this, all we need is one command:

ant test
Copy the code

You can program, package, test. It has brought great convenience to developers. But ant has a fatal flaw, which is its inability to manage dependencies. We use a lot of third party tools, different tools, different versions. Having to manually copy the correct version to lib every time you pack is tedious and error-prone. To solve this problem, Maven comes into the picture.

2. Maven, a common build tool for Java platform.

Maven, a latecomer, inherits Ant’s project build functionality and also uses XML as a build script format. Maven has dependency management and project management capabilities and provides a central repository that helps you automatically download library files. At the heart of Maven’s improvement is the concept of a repository. I can put all the packages that I rely on in the warehouse, in my project management files, and mark what packages I need, what versions I need. At build time, Maven automatically typed these packages into my package for me. We no longer have to worry about managing dozens or hundreds of JAR files ourselves. To achieve this goal, Maven proposes to label each package with coordinates so that it can be easily looked up in the repository. So, packages built and published using Maven define their own coordinates according to this convention, for example:

<? The XML version = "1.0" encoding = "utf-8"? > <project ... xmlns... > <groupId>cn.hinus.recruit</groupId> <artifactId>Example</artifactId> <version>0.1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId> <artifactId>junit</artifactId> <version>4.10</version> </dependency> </dependencies> </project>Copy the code

So, defines the packages are the coordinates of the cn. Hinus. Recruit: Example: 0.1.0 from – the SNAPSHOT, and my project relies on junit: junit: 4.10. Maven will automatically pack junit in for me. If I don’t have junit locally, Maven will help me download it online. The place to download it is the remote repository, which can be specified by the Repository tag. Instead of defining tasks by target in Ant, Maven introduces the concept of a life cycle.

Maven’s disadvantages include:
  • Maven is configured using XML, and the syntax is not concise.
  • Maven takes convention over configuration too far. That said, Maven does not encourage you to define your own tasks. It requires users to work with plug-ins throughout maven’s life cycle. This is a bit like the template method pattern in design pattern.

To put it more simply, when I use Maven, I don’t have the flexibility to define my own tasks

3. Gradle, a common build tool for Java platform.

Gradle makes full use of maven’s existing resources. Maven inherits the core concepts of repository, coordinates, and dependency. The file layout is also the same as maven. But at the same time, it inherits the concept of Target from Ant, and we can redefine our tasks again. Instead of XML, Gradle uses a specialized Groovy-based DSL or Kotlin DSL, which makes Gradle build scripts much cleaner and cleaner than those written in Ant and Maven. Gradle boilerplate has very little code because its DSLS are designed to solve specific problems throughout the software lifecycle, from compilation, to static checking, to testing, to packaging and deployment. Google uses Gradle as the default build tool for Android OS.

// Apply the java plugin to add support for Java
apply plugin: 'java'

// In this section you declare where to find the dependencies of your project
repositories {
    // Use 'jcenter' for resolving your dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
}

// In this section you declare the dependencies for your production and test code
dependencies {
    // The production code uses the SLF4J logging API at compile time
    compile 'org. Slf4j: slf4j - API: 1.7.21'

    // Declare the dependency for your favourite test framework you want to use in your tests.
    // TestNG is also supported by the Gradle Test task. Just change the 
    // testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add 
    // 'test.useTestNG()' to your build script.
    testCompile 'junit: junit: 4.12'
}
Copy the code

Gradle,Gradle Wrapper and Android Plugin for Gradle.

1. Gradle

Gradle is a build system that makes compiling, packaging, and testing easier. Gradle download address

2. Gradle Wrapper

Gradle Wrapper is called Gradle Wrapper and is a layer of Wrapper around Gradle. Gradle Wrapper can be used without Gradle installed. Why do WE need Gradle Wrapper? For example, if every member of a development team needs to install Gradle on a computer, the environment and version running Gradle can create uncertainty about the build result. Gradle Wrapper is a script that can run Gradle builds without Gradle installed on your computer. It can specify Gradle versions. Developers can quickly start and run Gradle projects. Instead of having to install it manually, this standardizes the project and improves development efficiency. Gradle Wrapper generates the following files:

| ____gradle | | ____wrapper | | | ____gradle - wrapper. Specific business logic jar / / | | | ____gradle - wrapper. The properties / / configuration file | ____gradlew / / under Linux executable scripts | ____gradlew. Bat / / under Windows executable scriptsCopy the code

To change the configuration, open gradle/wrapper/gradle-wrapper.properties. Gradle Wrapper configuration will automatically download the appropriate Gradle version. By default, the download location is in the $USER_HOME /. Gradle/wrapper/dists.

DistributionBase =GRADLE_USER_HOME distributionPath=wrapper/dists? DistributionBase =GRADLE_USER_HOME distributionPath=wrapper/dists ZipStoreBase =GRADLE_USER_HOME zipStorePath=wrapper/dists # specifies which version of the Gradle build project to use. This address can be configured into the server address or local address distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zipCopy the code

3. Android Plugin for Gradle

The Android Studio build system is based on Gradle. The Android Plugin for Gradle adds some features specific to building Android applications. While Android plug-ins are usually updated synchronously with Android Studio, plug-ins (and the rest of the Gradle system) can run independently of Android Studio and be updated separately. In Android Studio, the following code is configured in your project’s root directory, build.gradle:

Jcenter buildscript {repositories {Google () ()} dependencies {classpath 'com. Android. View the build: gradle: 3.2.0' / / NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } }Copy the code

Gradle :3.2.0 in this dependencies reference refers to the gradle plugin version 3.2.0. During compilation, if the Gradle plug-in version does not match the Gradle version, the compilation will fail. Gradle in use corresponds to the gradle plugin version.

Gradle plug-ins correspond to gradle versions

4. Configure Gradle in Android project

1. Top Gradle files.

The top-level build.gradle file is located at the project root and is used to define build configurations that apply to all modules in the project. By default, this top-level build file uses buildScript blocks to define Gradle stores and dependencies shared by all modules in the project.

/** * Building blocks are the locations where you configure repositories and * Gradle's own dependencies - meaning, you should not include dependencies * in your modules. For example, this block contains the Android plugin * Gradle as a dependency because it provides additional Gradle instructions * needed to build Android application modules. */ buildScript {/** * repository block configures the repository that Gradle uses * to search for or download dependencies. Gradle pre-configured support for remoting * relies on repositories such as JCenter, Maven Central, and Ivy. You can also use a local * repository or define your own remote repository. The following code defines * JCenter as the repository Gradle should use to look up its dependencies. * * New projects created using Android Studio 3.0 and higher also include * Google's Maven repository. */ repositories {Google () jCenter ()} /** * Dependencies block The following line adds Gradle's Android plugin * version 3.2.0 as a classpath dependency. * / dependencies {classpath 'com. Android. View the build: gradle: 3.2.0' / / NOTE: Do not place your application dependencies here; They belong // in the individual module build.gradle files}} /** * AllProjects block Such as third-party plug-ins * or lib. However, you should configure module-specific dependencies * a build.gradle file for each module level. For new projects, Android Studio * includes JCenter and Google's Maven repository by default, but that's not the case * configure any dependencies (unless you select a template that requires some). */ allprojects { repositories { google() jcenter() } }Copy the code
  • The buildScript block repositories is primarily designed to show that only compilation tools will use this repository to obtain script-dependent plug-ins.
  • The allprojects block is for multi-project construction, providing common dependencies for allprojects. A subproject can configure its own repositories to obtain its own dependencies.

2. Module-level Gradle files.

Apply plugin: 'com.android.application' android {//compilesdkversion Specifies the Android API-level Gradle application to //compile your application. This means that your application can use // apis at this level and lower. CompileSdkVersion 28 //buildToolsVersion Specifies the version of the SDK build tool, command line tool and compiler that Gradle uses to build applications. You need to download the build tools using the SDK Manager. // This property is optional because by default the plug-in uses the recommended version of the build tool. The buildToolsVersion "28.0.3" //defaultConfig block encapsulates the default Settings and entries for all build variants, // and can dynamically override certain attributes in main/Androidmanifest.xml from the build system. // You can configure product Flavor to override these values for different versions of the application. DefaultConfig {//applicationId uniquely identifies the package to be published. // However, your source code should still reference the package name // as defined by the package attribute in the main/androidmanifest.xml file. ApplicationId "com. Yousheng. Myapplication" / / define the minimum API level needed for running the application. MinSdkVersion 15 // Specifies the API level used to test the application. 28 versionCode targetSdkVersion 1 versionName testInstrumentationRunner "1.0" "Android. Support. The test. The runner. AndroidJUnitRunner"} / / you can configure multiple building type in buildTypes block. By default, the build system defines two build types: debug and release. This // debug build type is not explicitly shown in the default build configuration, // but it contains debugging tools and is signed with a debug key. Release // Build types apply Proguard Settings and are not signed by default. buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}} // dependencies block // specify only dependencies needed to build the module itself in the module-level build profile. dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation' com. Android. Support: appcompat - v7:28.0.0 'implementation 'com. Android. Support. The constraint, the constraint - layout: 1.1.3' testImplementation junit: junit: '4.12' androidTestImplementation 'com. Android. Support. Test: runner: 1.0.2' androidTestImplementation 'com. Android. Support. Test. Espresso: espresso - core: 3.0.2'}}Copy the code
  • The apply plugin indicates that a plug-in is applied, and the plug-in generally has two options:

One is ‘com.android.application’, indicating that the module is an application module and can be run directly. The other is ‘com.android.library’, which means that this module is a library module and can only be run as a code library attached to other application modules.

  • Android {}, where we can configure various properties of the project build.
  • BuildToolsVersion Version of build tools, including package tools aapt, dx, and so on. You can use a higher version of build-tool to build a lower version of the SDK project.
  • CompileSdkVersion specifies the compiled version of the project. 28 indicates that the project is compiled using the Android 8.0 SDK. Only compile-time behavior is affected, not run-time behavior. The API available in the code must correspond to the declared version. Apis later than the declared version cannot be found and used. The major version number of the Support library must be the same as that of compileSdkVersion

  • MinSdkVersion specifies the minimum API level required for the application to run. If the Android device’s system API level is lower than Android :minSdkVersion, Android will prevent users from installing the app. If this attribute is specified and an API level higher than this API level is used in the project, an error will be reported at compile time.
  • TargetSdkVersion indicates that you have done enough compatibility and testing on the target version to enable the latest functionality and features for your application. The version of the SDK used when the application is running

For example: Android6.0 (API 23) system dynamic permission check function; TargetSdkVersion <23: after the application is installed on android6.0 phones, it will not perform the dynamic permission checking logic unique to android6.0 system, but still continue to perform the previous permission checking logic. When targetSdkVersion changes to 23: the dynamic permission checking feature of android6.0 takes effect.

MinSdkVersion <targetSdkVersion<= compileSdkVersion; It is best not to arbitrarily change targetSdkVersion, change targetSdkVersion must be compatible.

  • BuildTypes encapsulates all build type configurations for this project.

5.Gradle adds build dependencies

To add dependencies to your project, specify the dependency configuration in the Dependencies program block of your build.gradle file. For example, the build.gradle file for the following application module contains three different types of dependencies:

apply plugin: 'com.android.application' android { ... Implementation project(" myLibrary ") implementation fileTree(jar, arR, etc.) Implementation 'com.example.android:app-magic:12.3'}Copy the code

1. Local Library module dependencies.

implementation project(":mylibrary")
Copy the code

This code declares a dependency for the Android library module named “myLibrary” (the name must match the library name defined using include: in the settings.gradle file). When you build your application, the build system compiles library modules and packages the compiled content into APK.

2. Local binary file dependencies.

implementation fileTree(dir: 'libs', include: ['*.jar'])
Copy the code

Gradle declares dependencies for JAR files in the project module_name/libs/ directory (because Gradle reads the relative path to the build. Gradle file). Alternatively, you can specify individual files as follows:

implementation files('libs/foo.jar', 'libs/bar.jar')
Copy the code

Gradle declares aar file dependencies in the project module_name/libs/ directory:

android {
    ...
    repositories {
        flatDir {
            dirs 'libs'
        }
    }
}
Copy the code
dependencies {
    implementation(name: 'mylibrary-release', ext: 'aar')
}
Copy the code

3. Remote binary dependencies.

Abbreviated form:

Implementation 'com. Example. Android: app - magic: 12.3'Copy the code

Written in full form:

Implementation Group: 'com.example. Android ', name: 'app-magic', version: '12.3'Copy the code

This code declares a dependency for version 12.3 of the app-Magic library in the “com.example.android” namespace group.

Note: 1. A similar remote dependency requires you to declare the corresponding remote code base, which Gradle should look for. If the library does not already exist locally, Gradle gets it from a remote site when the build needs it (for example, when you click Sync Project with Gradle Files or when you run the build). 2. Specify the dependencies, dynamic version number should not be used, such as’ com. Android. View the build: gradle: 3 + ‘. Using this feature can result in unexpected version updates and difficulty resolving version differences.

Mvnrepository warehouse

Gradle relies on repositories

1. Why use remote dependencies?

Manually managing dependencies can be troublesome. You must first locate the dependency file location, then download the JAR file, copy the file to the project, and then reference them. Often these JAR files don’t have a specific version number, so you have to remember their version number so that when it comes time to update, you know which version to replace. This dependency package must also be placed on SVN or Git so that other colleagues do not have to manually download the dependency Jars. To solve these problems, remote dependencies were introduced.

2. Classification of remote dependent warehouses

If we want to put our dependencies remotely, we first need a repository to store them, so that’s what maven repository does. In Maven terminology, a repository is a place, such as a directory, where all project JAR files, Library JAR files, plug-ins, or any other project-specific files can be stored.

Maven repositories come in three types:

  • Local Warehouse (Local)
  • Remote Warehouse (remote)
  • Central, a type of remote repository, is most commonly used in Android development
Local repository

Maven local repository is a folder on your computer that holds all jar files. The local repository is a buffer and subset of the remote repository. When you build a Maven project, you will first look for resources from the local repository. So you don’t have to download it remotely the next time you use it. Maven will report an error at build time if the version of the jar you need does not exist in the local repository and does not exist in the remote repository. This may happen if the new version of the jar is not updated in the Maven repository. It needs to be maintained by itself. Maven local repositories are created in the %USER_HOME% directory by default.

Remote warehouse

If we are library authors, we don’t want to put the Library on a central repository server. We can also put the Library on our own maven repository server. If someone wants to use our library, they need to import our repository address first.

The central warehouse

This repository is managed by the Maven community and serviced by Sonatype inc., and contains a large number of commonly used libraries. It is easy to use with Apache Ant, Gradle, and other build tools, and requires web access to search.maven.org/#browse, where developers can find their desired code base.

The difference between a remote warehouse and a central warehouse: A remote warehouse is usually a warehouse created by a company or a team and maintained by the company or team; A central repository is also a remote repository but it’s a repository on the Internet, maintained by the Maven team;

3. Rely on search order

When we execute the Maven build command, Maven starts looking for dependent libraries in the following order:

  • Step 1: Search in the local repository. If no, go to Step 2. If no, perform other operations.
  • Step 2: Search the central repository. If no remote repository is found and one or more remote repositories have been set up, perform Step 4. If the remote repository is found, download it to the local repository for future reference.
  • Step 3: If the remote repository is not set up, Maven simply stalls processing and throws an error (the dependent file cannot be found).
  • Step 4: Search one or more remote repositories for dependent files. If found, download it to the local repository for reference. Otherwise, Maven will stop processing and throw an error (the dependent file cannot be found).

2. Maven’s common remote repository

MavenCentral warehouse

Maven Central is a Maven repository maintained by Sonatype.org. You can see the whole warehouse here. To use Maven Central, define the repository in the build.gradle file of your Android project as follows:

buildscript {
    repositories {
        mavenCentral()
    }
}
allprojects {
    repositories {
        mavenCentral()
    }
}

Copy the code

Download local repository file address:

Windows: C:\Users\ username \.gradle\caches\modules-2\files-2.1 OSX: /Users/ username /.gradle/caches/modules-2/files-2.1Copy the code

Early versions of Android Studio used Mavan Central, which has since switched to JCenter.

Jcenter warehouse

Build. Gradle file for your Android project. You can use jCenter by defining the repository as follows:

buildscript {
    repositories {
        jcenter()
    }
}
allprojects {
    repositories {
        jcenter()
    }
}
Copy the code

Download local repository file address:

Windows: C:\Users\ username \.gradle\caches\modules-2\files-2.1 OSX: /Users/ username /.gradle/caches/modules-2/files-2.1Copy the code

Note that while both JCenter and Maven Central are standard Android Library repositories, they are maintained on completely different servers, with content provided by different people, and have nothing to do with each other. What is available on JCenter may not be available on Maven Central, and vice versa.

Why does Android have two standard repositories? Both repositories actually have the same mission: to provide Java or Android Library repository storage services. Which upload (or both) is up to the developer. Initially, Android Studio chose Maven Central as the default repository. If you create a new project using older versions of Android Studio, mavenCentral() is automatically defined in build.gradle. But the biggest problem with Maven Central is that it’s not developer friendly. Uploading the Library is extremely difficult. Also for other reasons, such as security, the Android Studio team decided to replace the default repository with JCenter. Now once a project is created using the latest version of Android Studio, jCenter () is automatically defined instead of mavenCentral(). There are a few main reasons why Google changed to JCenter:

  • Jcenter is the largest Java repository in the world, so what’s available on Maven Central is likely to be available on JCenter. In other words, JCenter is a superset of Maven Central.
  • Uploading the library to the repository is simple and does not require a lot of complicated things like doing Maven Central.
  • Jcenter sends library via CDN using HTTPS, which is more secure, while Android Studio version 0.8 mavenCentral uses HTTP
  • Jcenter performs better than Mavan Central
  • MavenCentral automatically downloads a lot of IDE related indexes, which are rarely used and are not required

Note: Both JCenter and Maven Central are Maven repositories.

Build your own remote warehouse

In addition to the two standard central repositories, we can also define our own Maven repository server if the author of the library we are using hosts the library on their own server. This is the case with Twitter’s fabric. IO, which maintains its own Maven repository on Maven.fabric. IO /public. If you want to use the library of fabric. IO, you must define the url for the repository as follows.

repositories {
    maven { url 'https://maven.fabric.io/public' }
}

Copy the code

Then get a library inside using the same method.

Dependencies {the compile 'com. Crashlytics. SDK. The android: crashlytics: 2.2.4 @ aar' / / the new as substitute implementation for the compile}Copy the code

Is it better for library to upload to a standard server or to build your own server repository? It depends on your own needs. If you make your library public and make it available to other developers, build your own server repository.

Google’s warehouse

After gradle4.1, a new syntax, Google (), was added to conveniently reference Google’s own repository. Since Google’s repository does not support browsing, only downloading, it is not convenient to explore the aar source files that are relied upon. Google defines the repository as follows in the build.gradle file of your Android project, and you can use Google:

buildscript {
    repositories {
        google()
    }
}	
allprojects {
    repositories {
        google()
    }
}

Copy the code

Download local repository file address:

Windows: C:\Users\ username \.gradle\caches\modules-2\files-2.1 OSX: /Users/ username /.gradle/caches/modules-2/files-2.1Copy the code

Of course, Android Studio comes with some libraries. The support libraries are in the SDK. The Gradle plugin is usually in the Android Studio installation directory.

Jitpack warehouse

The repository provided by JitPack. To use JitPack, define the repository as follows in the Build. gradle file of your Android project:

buildscript {
    repositories {
          maven { url "https://jitpack.io" }
    }
}	
allprojects {
    repositories {
         maven { url "https://jitpack.io" }
    }
}
Copy the code

4. Remote warehouse mirror (domestic mirror of Aliyun)

In China, jCenter, mavenCentral and Google remote warehouses are used, GradleSync is very slow, and Goole warehouse even requires scientific Internet access. To speed up Gradle Sync, you can use Ali Cloud image repository as the download source first. If downloading the dependency fails, try using the mirror repository address.

buildscript {
    repositories {
        maven{ url 'https://maven.aliyun.com/repository/public'}
        maven { url 'https://maven.aliyun.com/repositories/jcenter' }
        maven { url 'https://maven.aliyun.com/repositories/google' }
        maven { url 'https://maven.aliyun.com/repository/central' }
        //jcenter()
        //mavenCentral()
        //google()
    }
}
     
allprojects {
    repositories {
        maven{ url 'https://maven.aliyun.com/repository/public'}
        maven { url 'https://maven.aliyun.com/repositories/jcenter' }
        maven { url 'https://maven.aliyun.com/repositories/google' }
        maven { url 'https://maven.aliyun.com/repository/central' }
        maven { url "https://jitpack.io" }
        //jcenter()
        //mavenCentral()
        //google()
    }
}
Copy the code

Ali Cloud warehouse address

Gradle introduces dependencies

If you want to add a third party library to your Android project, add the following line of code to the module’s build.gradle file.

Dependencies {the compile 'com. Squareup. Okhttp: okhttp: new version 2.4.0 / / as for implementation, instead of the compile}Copy the code

With that simple line of code, you can use the library. To reference a library code, there are three sections: GROUP_ID, ARTIFACT_ID, and VERSION in this code

  • GROUP_ID is com.squareup.okhttp, and it is possible to have multiple libraries with different functions in the same context. If libraries have the same group, they will share a GROUP_ID. Usually we name the developer package name immediately after the library group name, such as com.squareup.okhttp.
  • ARTIFACT_ID is okhttp, which is the real name of library.
  • VERSION is 2.4.0, which is just the VERSION number. Although it can be any text, it is recommended to set it to X.Y.Z. If you like, you can add beta.

Here’s an example from Square Library. As you can see, it’s easy to distinguish the library and developer names from each other.

After adding the above dependencies, Gradle will ask the Maven repository server if the library exists. If so, Gradle will get the path to the requested library. This path is usually of the form GROUP_ID/ARTIFACT_ID/VERSION_ID. Such as in jcenter.bintray.com/com/squareu… And oss.sonatype.org/content/rep… Com. squareup: okhttp:2.4.0 library file Android Studio will then download these files to our computer and compile them with our project. The whole process is that simple.

A Library downloaded from the repository is simply a JAR or AAR file stored on the repository server. It’s kind of like downloading these files yourself, copying them and compiling them with the project. But the biggest advantage of using Gradle dependency management is that you don’t do anything except add a few lines of text.

Once the dependency is added, the library is downloaded and compiled with the project.

In Dependencies {}, you declare library dependencies using one of several different dependency configurations, such as the implementation shown above. Each dependency configuration gives Gradle a different explanation of how to use the dependencies. The following table describes each configuration you can use for dependencies in an Android project. The table also compares these configurations with those deprecated since Android Gradle Plugin 3.0.0.

The new configuration The configuration is deprecated behavior
implementation compile Gradle adds dependencies to the compile classpath and packages dependencies into the build output. However, when your module is configuredimplementationWhen a dependency is created, Gradle is told that you do not want your module to leak dependencies to other modules at compile time. Use this dependency configuration instead ofapi 或 compileDeprecated, yesSignificantly reduce build timesBecause it reduces the number of modules that need to be recompiled to build the system. For example, ifimplementationA dependency changes its API, and Gradle only recompiles the dependency and the module that directly depends on it. Most applications and test modules should use this configuration.
api compile Gradle adds dependencies to the compiled classpath and builds the output. When the module includesapiWhen a dependency, the Gradle module is told that it wants to export the dependency indirectly to other modules so that these modules can use the dependency at run time and compile time. This configuration behaves like thiscompile(now deprecated), but you should use it sparingly only for dependencies that need to be exported indirectly to other upstream consumers. That’s because ifapiA dependency changes its external API, and Gradle recompiles all modules that can access the dependency at compile time. So, there’s a lot ofapiDependencies can significantly increase build time. If you do not want to expose the dependency API to different modules, the library module should use it insteadimplementationDependencies.
compileOnly provided Gradle only adds dependencies to the compilation classpath (that is, it does not add them to the build output). This configuration is useful if you are creating an Android module that needs the dependency at compile time and optionally renders the dependency at run time. If you use this configuration, your library module must include runtime conditions to check if the dependency is provided, and then properly change its behavior so that the module can function without the dependency. Doing so does not add unimportant transient dependencies and helps reduce the size of the final APK. This configuration behaves like thisprovided(now deprecated).
runtimeOnly apk Gradle simply adds dependencies to the build output for use by the runtime. That is, it will not be added to the compiled classpath. This configuration behaves like thisapk(now deprecated).
annotationProcessor compile Must be used to add annotation processor dependencies to a libraryannotationProcessorConfigure to add it to the annotation processor classpath. This is because you can use this configuration to separate the compilation classpath from the annotation processor classpath, which improves build performance. If Gradle finds the annotation handler on the compilation classpath, it deactivatesAvoid compilationFeature, which increases build time (Gradle 5.0 and later ignore annotation processors on the compiled classpath). The Android Gradle Plugin assumes that the dependency is an annotation handler if the JAR file contains the following files:META-INF/services/javax.annotation.processing.Processor. A build error is generated if the plug-in detects an annotation handler on the compiled classpath.

To add implementation dependencies for your local and appliance tests, use code like this:

Dependencies {// Adds a remote binary dependency only for local tests. testImplementation 'junit:junit:4.12' // Adds a remote binary dependency only for the instrumented test APK. androidTestImplementation 'com. Android. Support. Test. Espresso: espresso - core: 3.0.2'}Copy the code
  • Implementation: Dependencies are added to the compile path and packaged into the output (AAR or APK), but the implementation of the dependency is not exposed to other Modules at compile time, meaning that the implementation in the dependency is only accessible to other Modules at run time. This module can only be used internally. For example, if I use implementation in a Libiary and rely on the Gson library, and then my main project relies on Libiary, then my main project cannot access the methods in the Gson library.
  • API: Dependencies are added to the compile path and packaged into the output (AAR or APK). Unlike implementation, this dependency can be passed and its implementation can be accessed by other Modules both at compile time and run time

The difference between Implementation and API: App ModuleDepends on theLibraryAAnd theLibraryA Rely on theLibraryB. ifLibraryA rightLibraryB Is used to rely onimplementation The keyword. thenLibraryB The interface can only be givenLibraryAUse,App ModuleIt is inaccessibleLibraryBOf the interface provided. ifLibraryA rightLibraryB Is used to rely onapi Keyword, thenLibraryB In the interface, not only can giveLibraryAUse, but also can giveApp ModuleUse.

  • CompileOnly Gradle adds dependencies to compile path, used at compile time, not packaged to output (AAR or APK). This can reduce the size of the output in cases where it is only needed at compile time and optional at run time.

ifApp ModuleDepends on theA Lib、B Lib、C Lib.A LibandB LibAlso depends onC Lib. At this point we can let goA LibandB LibthroughcompileOnly To rely onC Lib. Reduce the likelihood of dependency conflicts.

  • RuntimeOnly corresponds to compileOnly. Gradle adds dependencies only to APK. They are used at runtime but not in compileOnly.

  • AnnotationProcessor is used to annotate the processor’s dependency configuration.

Android Plugin for Gradle 3.0.0+ no longer supports Android-apt.

Dependency management

1. Run the Gradle command to view the dependency hierarchy tree.

Run the terminal console command.

Gradlew :app :dependencies >log. TXT file gradlew :app :dependencies >log. TXT file Dependencies app:dependencies -- Configuration releaseRuntimeClasspath lists dependencies in an environment (releaseRuntimeClasspath)Copy the code

Other configuration type parameters can be obtained by running the following command:

gradlew dependencies --info
Copy the code

When there are multiple versions of the same inventory, Gradle automatically applies the highest version of the library to all applications. A library followed by a “(*)” indicates that the library has been overwritten.

2. Use Gradle View to View the dependency hierarchy tree.

  1. Install Gradle View in plugins and restart As.
  2. Go to View->Tool Windows -> Find Gradle View and click.

3. Pass dependencies.

In the Maven repository, artifacts describe related information and transitive dependencies through POM, an XML file. Gradle can analyze this file to get all the dependencies and their dependencies and their dependencies. Such as:As you can see, our project relies on Hibernate, whereas Hibernate relies on a number of other dependencies. With Gradle’s transitive dependencies, you don’t have to declare them all in your script. Gradle downloads them with a single line.

Implementation 'org. Hibernate: hibernate - core: 3.6.3. The Final'Copy the code

Transitive dependencies can be easily turned on or off with the transitive parameter. Transitive dependencies can be turned off by specifying transitive = false, or all transitive dependencies can be ignored by adding @JAR (@aar).

Implementation ('org.hibernate:hibernate-core: 3.6.3.final '){transitive = false}Copy the code

Or:

Implementation 'org. Hibernate: hibernate - core: 3.6.3. The Final @ jar'Copy the code

Check the dependencies as follows:

Global close dependency transfer feature:

dependencies {
configurations.all {
   transitive = false
}
...
}
Copy the code

4. Eliminate dependencies.

There may be times when you need to exclude a module from transitive dependencies that cannot be resolved by simply turning off dependency transitive features. That’s where exclude comes in. Exclude solves part of the dependency pass problem.

  • If there are two dependencies that refer to different versions of the same JAR package, gradle uses the latest jar package by default. You can use the exclude option to exclude these dependencies.
  • This module is not required at runtime.
  • Could not get this delivery dependency properly, remote repository does not exist.
  • Copyright reasons need to be excluded.

Annotations for myLibrary support AppCompat-v7 and Support support- Annotations for MyLibrary

Implementation (' cn. Qqtheme. Framework: WheelPicker: 1.5.1 ') {exclude group: 'com. Android. The support, the module: 'support-v4' exclude group:'com.android.support' ,module: 'support-annotations' }Copy the code

Before eliminating:After the rule out:

AAR package.

1. Aar’s advantages over JAR.

  • Jar files: contains only class files and manifest files, excluding resource files, such as images, all resource files under res.
  • Aar file: contains the class file and all resource files under res.

2. Create aar packages.

4. Aar package is prone to problems.

An AAR must not contain original assets

The tool does not support the use of raw resource files (stored in assets/) in library modules. Any raw resources used by an application must be stored in assets/ of the application module itself.

MinSdkVersion problem

The AAR is compiled as part of the relevant application module, so the API used in the library module must be compatible with the platform version supported by the application module, so the minSdkVersion in the AAR should be set as low as possible.

Problems with AAR internal tripartite library dependencies
An AAR packaged with Android Studio does not include the third-party libraries it relies on.Copy the code

For example, library Test relies on OKHTTP and is packaged as test. aar. The app references Test.aar locally but cannot use OKHTTP. The reason why Android Studio can’t package multiple Lib dependencies into an AAR file is that packaging different libraries together involves intelligent merging of resources and configuration files, so it’s a complex problem, and it’s easy to create the same dependency conflicts. This problem can be solved by using Maven dependencies. After the Library Module uplows Maven, a.pom file is generated to record library Module dependencies. When Gradle relies on the library on Maven, the dependency is downloaded via the POM file. If you do not want the corresponding dependencies, you can turn off Gradle dependency passing by using the following method.

/ / normal depend on implementation 'com. Chemao. Android: chemao - SDK: 1.2.3' / / close all depend on the transfer - 1 implementation method 'com. Chemao. Android: chemao - SDK: 1.2.3 @ aar' / / close all depend on the transfer - 2 implementation method (' com. Chemao. Android: chemao - SDK: 1.2.3 ') { transitive = false }Copy the code

There are two reasons not to use a local AAR:

  1. The third library in the AAR cannot use remote dependencies
  2. If you use local Maven, it would be impractical for everyone involved in development to configure a local Maven repository
Problems caused by multilevel AAR references

Problem descriptionModuleLib B refers to an AAR and ModuleApp A refers to ModuleLib B, in which case Lib will not be found. Problem analysisFor ModuleApp A, they rely on ModuleLib B. Whether it is used or not, they will go through the dependency of ModuleLib B. When they go to the added AAR local dependency, the path to find AAR should also be given, and if the path is given like libs, It will go to find their own package under the libs, naturally there is no.The solution

ModuleLib B lib directory is given in ModuleApp A:

android { ... repositories { flatDir { dirs 'libs','.. /ModuleLib B Name/libs' } } }Copy the code
The resource files in the AAR package are duplicated

The resource file of the main project will directly overwrite the aar file without any error or prompt. Finally, the aar file will also be directly used by the main project, so pay attention to the naming method. There is no better solution at the moment.

X. support The support library.

1. The reason for adding the support library

Why does Google need so many support libraries, why not just put them in the SDK? There are three reasons to refer to official documentation:

  • Backward compatibility

For example, the App we developed needs to support minSdkVersion=9, targetSdkVersion=11, and uses the ActionBar class provided by Android 3.0 (API Level 11) in the program. Apk was successfully compiled with compileSdkVersion=11. It runs perfectly on An Android 3.0 device, but the app crashes on an Android 2.3 device, reporting that the error of the ActionBar cannot be found. This makes sense because there are no new classes on the old version. In order to avoid the embarrassment that apps developed with the latest functions can only run on devices with the latest operating system, the authorities have put the newly added interfaces of the new system framework into the Android Support Library (Support package). When developers encounter the above situation, You can use the same ActionBar class from the support pack, which is packaged into the App, so that apps that use the functionality on the new version of the OS can also be backward compatible with older versions of the device. In addition to saving us from judging the system version on which the App is running, using the classes in the support pack allows us to maintain the same user experience across versions of the App. For systems below 5.0, material Design is used. The Android SDK (Android.jar) with which the App was compiled will not be packaged into our App. Because the App code uses the interface in Android. jar, which is the interface provided by the system framework layer (framework) on android devices.

  • Provide functionality that doesn’t fit into the framework

Android officially provides recommended design for App development, hoping that Android applications have relatively consistent interaction design to reduce user cost, and hope that third-party apps similar to system applications can be perfectly integrated into the Android ecosystem. However, these are only recommendations, and developers are not required to do so. If they do, they can use the features provided by the official support pack to avoid reinventing the wheel. This is the case with DrawerLayout, Snackbar, and other classes in support packages.

  • To support different forms of equipment

Use support packs to provide functionality on different forms of devices, such as mobile phones, TVS, wearables, etc.

2. support

android-support-v4
compile "com.android.support:support-v4
Copy the code

In April 2011, Google released a package compatible with the 1.6 OS at the lowest level. ViewPager, PageTabAtrip, Loader, and NotificationCompat FileProvider and other controls. V4 includes the basic functionality of V7 and V13.

android-support-v7
compile "com.android.support:appcompat-v7:xx.xx"
Copy the code

Launched at the 2014 I/O conference, compatible with Android2.1 at least. The latest V7 package adds a number of Material Design compatible classes and materials, including Theme, value, layout, new controls, and new animation implementations, all of which are support-V4. However, v7 relies on the V4 package, which means that both packages must be referenced at the same time. Android Studio imports the V7 project by default when creating the project, and uses a compatible style for the style.

android-support-v13

Version compatibility pack for tablet development, minimum compatible with Android3.2 system. Android 3.x is a tablet-only system. But the 3.x system failed, so there was no value in using the v13 package.

3. androidX

Starting with android9.0 (API28), the support library will be changed, and v7:28.0.0 will be the final version of the support library. Future new features and improvements will make their way into the Androidx package. The main reason is that the support library’s naming has become increasingly confusing and packages have become bloated. Dependent packages vary from:

API 'com. Android. Support: appcompat - v7:28.0.0'Copy the code

Becomes:

API 'androidx. Appcompat: appcompat: 1.0.0'Copy the code

It is important to note that the build. Gradle plugin version must be at least 3.2.0.

AndroidManifest merge conflict.

1. Problem description

Android Studio projects can have an Androidmanifest.xml file in each dependent module, but the final APK file can only contain one Androidmanifest.xml file. When building an application, a Gradle build consolidates all the manifest files into a single manifest file encapsulated in APK. For example, in the manifest of app, the application node has a label attribute under the application node.You can see an error in the merge message when you open manifest:

2. Solve problems

List file attribute conflict: Use tools:replace=” attribute name “to resolve. For example, if there is a conflict with the label attribute, you can use:

 tools:replace="label"
Copy the code

Android Studio Gradle is a series of two build tools that have evolved over time: Ant, Maven, and Gradle. ant, maven, gradle