Overview
Started a new series, this series of learning Gradle, the goal is to thoroughly understand Gradle, mainly to make notes that you understand, to prevent forgetting
Gradle series (1): Groovy learning
Gradle learning series (two): Gradle core decryption
Gradle learning series (3): Gradle plugin
Gradle learning series (four): Gradle dependency
Introduction
In normal use, dependency is a hurdle that cannot be escaped. Compilation errors are always reported due to various reasons. Today we will understand the dependency and the solution of common problems.
Dependent type
Gradle dependencies are respectively 直接依赖,项目依赖,本地jar arr依赖,传递依赖
, and the meaning of these dependencies are distinguished below
- Direct dependency: The dependency imported directly in the project is the direct dependency
dependencies {
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
}
- Transitive dependency: Project A depends on okhttp, and okhttp depends on okio. Finally, project A also depends on okio, which means that the dependency is transitive.
Local Libary module dependency
implementation project(':mylibary')
This way of dependency is directly dependent on this project libary module
, this libary module
needs to setting.gradle
be configured in
Local binary dependency
This mainly includes local jar file dependency, aar file dependency and local so file dependency
Local jar file dependency, generally including these two forms
//直接依赖某文件
implementation files('libs/foo.jar', 'libs/bar.jar')
//配置某文件夹作为依赖项
implementation fileTree(dir: 'libs', include: ['*.jar'])
Local arr file dependency, generally including these two forms
//依赖单个arr文件
implementation(name: 'facesdk', ext: 'aar')
//依赖某个文件夹中的所有aar文件
implementation fileTree(include: ['*.aar'], dir: 'libs')
The location of the arr folder needs to be set before use
// 声明本地 aar 文件地址
repositories {
flatDir {
dirs 'libs'
}
}
so file dependency
The .so file is the same as the .java file. It will be viewed by Gradle as a local code resource. You only need to set the resource path of the so to get the so file.
Depends on the so file, generally there are two forms
- Use the default file path of the android plugin to store the so file, so that android will find the so according to the default path, the default path is /src/main/jniLibs
- The other is that we manually set the so file path location
// 设置 .so 资源路径
android{
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
Remote dependency
Gradle
There is no remote warehouse of its own, so to add remote dependencies, you must declare which remote warehouse to use. Each Gradle
script needs to declare the warehouse. The first version is in the build.gradle
configuration sub project
-repository of the root directory.
allprojects {
repositories {
jcenter()
maven {
url 'https://maven.google.com/'
name 'Google'
}
}
}
Then you can remotely depend on the configuration
implementation 'com.example.android:app-magic:12.3'
The above is a shorthand, the full version is written as follows:
implementation group: 'com.example.android', name: 'app-magic', version: '12.3'
group
name
version
Co-locating a remote warehouse, version is best to write a fixed version number to prevent problems in the build
The core of dependency management
Gradle dependency management relies on two classpaths:compileClasspath、runtimeClasspath
-
compileClasspath: Code and classes that can be used during compilation. When a component participates in compilation, Gradle will put it in compileClasspath
-
runtimeClasspath: Code and classes used at runtime, when a component participates in packaging, Gradle will put it in the runtimeClasspath
-
Compile time: the code is still in the writing stage, as long as it has not been compiled into a class, it is compile time
-
Runtime: When compiled into a class file, it is called runtime when it runs on the machine
-
The code and class library contained in compileClasspath are the class libraries we need to use when writing code. If there is no class library in it, we will not find the class when we write it.
-
RuntimeClasspath mainly includes the class libraries needed during app runtime. If there is no library included in this, then the class cannot be found during runtime and crash
-
Operators such as implementation and api are just different operation methods for dependent libraries. The core logic is
compileClasspath
whether the library pulled from the remote is placed in the middle orruntimeClasspath
is it still俩个都放
Dependent management configuration
The dependency configuration supported by the current Gradle version implementation、api、compileOnly、runtimeOnly 和 annotationProcessor
has been deprecated.compile、provided、apk、providedCompile
Below we use implementation and api to understand compileClasspath
andruntimeClasspath
implementation
Dependencies will be added to the compilation path, and the dependencies will be packaged and output to aar/apk, but the dependencies will not be exposed to other moudles during compilation
比如:A implementation B B implementation C
- In B, you can use the class library in C
- In A, you cannot use the class library only given by C, but you can use the class library in B
- This is because the
implementation
introduced dependency will add C to thecompileClasspath
sum of B, andruntimeClasspath
add C to A'sruntimeClasspath
- Because C does not join A's compileClasspath, A has no way to access the class library in C at compile time, and because C joins A's runtimeClasspath, A can access the C class library at runtime
api
The dependency will be added to the compilation path, and the dependency will be packaged and output to aar/apk. Unlike implementation, this dependency can be passed
For example: A implementation BB api C
- In B, you can use the class library in C
- In A, you can use the class library in B, or you can use the class library in C
- This is because the dependency introduced by api will add C to the
compileClasspath
sum of B, andruntimeClasspath
at the same time add C to thecompileClasspath
sumruntimeClasspath
of A, so A can also access the class library in C at compile time
compileOnly
Dependencies are only used at compile time, not packaged into aar/apk and cannot be used at runtime
For example: A implementation BB compileOnly C
- A cannot access the code of C, B can access C, and C will not be packaged in the apk
runtimeOnly
Cannot be used when relying on compilation, it will only be packaged into aar/apk runtime for use
For example: A implementation BB runtimeOnly C
- AB can not call the code in C, but C will be packaged into APK
Depend on remote library
In fact, every component has its own compileClasspath 和 runtimeClasspath
each component involved when compiled, Gradle will put it compileClasspath in
every component involved in packaging, Gradle will put him in runtimeClasspath
So if you rely on a remote warehouse, how to decide to put compileClasspath 还是 runtimeClasspath
it in?
In fact, when maven uploads a component, it will not only upload a binary file (such as aar), but also upload a pom.xml file, and the dependency information is in this file.
The following is a pom file, the picture comes from Gradle and Android build entry
, thank you for your output
pom file will have both a dependency, will be divided runtime
and compile
, runtime compiler will not participate, will be involved in packaging, compile will be involved in compiling and packaging
Suppose A depends on B, and B depends on C
BC is a remote warehouse. The dependency on C in B's POM is runtime.
In Gradle 4.4, A can still call C code. This problem was fixed after Gradle 5.0
Dependency conflict
ABC is the original Module, D is a remote dependency, A depends on B, A depends on C, B depends on D 1.0 version, and C depends on D 1.1 version. At this time, D has a dependency conflict. It is necessary to determine whether to use D version 1.0 or 1.1 version
How to resolve dependency conflicts
- When compiling, B relies on version 1.0 of D when compiling, and C depends on version 1.1 of D when compiling, but the 1.1 version of D is finally packaged into the APK. This is because the version number conflicts and the latest version is selected.
- Gradle also provides us with a series of methods for resolving dependency conflicts as follows: dependency transfer is not allowed, exclude removes a dependency, and force the use of a certain version
Mandatory use of a dependent version
isForce
isForce means to force the use of this version of the dependency
dependencies {
implementation('org.hibernate:hibernate:3.1') {
//在版本冲突的情况下优先使用3.1版本
isForce = true
}
}
strictly
Strictly is a kind of strong version constraint, you can use (!!) shorthand
dependencies {
implementation("io.reactivex.rxjava2:rxjava:2.2.0!!")
implementation("io.reactivex.rxjava2:rxjava") {
version {
strictly("2.2.0")
}
}
}
exclude removes a dependency
Gradle provides an exclude setting in the implementation{...}, which can be set to ignore the specified dependencies, and the ignored dependencies are regarded as never dependent. According to the current situation of my testing, this only applies to remote dependencies.
dependencies {
implementation('org.hibernate:hibernate:3.1') {
//排除特定的依赖
exclude module: 'cglib' //by artifact name
exclude group: 'org.jmock' //by group
exclude group: 'org.unwanted', module: 'iAmBuggy' //by both name and group
}
}
We can use group or module, or group and module together as the basis for exclusion
The following is the meaning of group and module
implementation("org.hibernate:hibernate:3.1")
group = org.hibernate
module = hibernate
version = 3.1
Dependency passing is not allowed
dependencies {
implementation('org.hibernate:hibernate:3.1') {
//禁用依赖传递
// 传递依赖:A => B => C ,B 中使用到了 C 中的依赖,
// 且 A 依赖于 B,如果打开传递依赖,则 A 能使用到 B
// 中所使用的 C 中的依赖,
//默认都是打开,即 true
transitive = false
}
}
jar conflict
If the app references an aar, there is a xx.jar in arr, and the app references this xx.jar again, at this time, you need to use compileOnly instead of implementation for the operator that references the jar in aar