[Android] Android plugin for Gradle

1. What is the Android plugin for Gradle

In the Gradle core idea (5) Easy-to-understand Gradle plug-in explanation in this article, we know that Gradle has many plug-ins. In order to support the construction of Android projects, Google has written Android plug-ins for Gradle. The new Android build system is created by Gradle. Composed of Android plugins, Gradle is an advanced build toolkit that manages dependencies and allows developers to customize the build logic. Android Studio uses Gradle wrapper to integrate Gradle's Android plugin. It should be noted that the Android plugin for Gradle can also run independently of AndroidStudio.
In Android's official website referred to the new Android build system mainly has the following characteristics:

  • Code and resources are easy to reuse
  • Whether it is for multiple apk releases or different styles of applications, it is easy to create multiple different versions of the application.
  • Easy to configure, extend and customize the build process
  • Good IDE integration

Gradle's Android plug-in combined with Android Studio has become the most popular Android build system.

2. Module type and project view of Android Studio

Each project in Android Studio contains one or more modules containing source code files and resource files. These modules can be independently built, tested or debugged. The types of modules of an Android Studio can be as follows:

Android application modules
Android application modules may depend on library modules, although many Android applications contain only one application module, and the build system will generate an APK from it.

Android library module The
Android library module contains reusable Android-specific code and resources, which the build system will generate an AAR.

The App Engine module
contains the code and resources for application engine integration.

The Java library module
contains reusable code, and the build system will generate a JAR package.

The Android project view in Android Studio 3.3.2 is shown below. All build files are displayed under the Gradle Scripts level, and the usefulness of these files is roughly introduced.
Vep71s.png

  • Project build.gradle: Configure the overall properties of the project, such as the specified code repository, the dependent Gradle plug-in version, and so on.
  • Module build.gradle: Configure the compilation parameters of the current Module.
  • gradle-wrapper.properites: Configure Gradle Wrapper, you can view the core idea of ​​Gradle (4) This article on Gradle Wrapper, which seems useless but is actually important .
  • gradle.properties: Configure the compilation parameters of Gradle. See the official Gradle documentation for specific configuration
  • settings.gradle: Configure Gradle's multi-project management.
  • local.properties: Generally used to store the private property configuration of the Android project, such as the SDK path of the Android project.

This article mainly introduces the project build.gradle and the module build.gradle.

3. Project build.gradle

We create a new Android project, the content of its project build.gradle is as follows:

gradle

buildscript {
    repositories {
        google()
        jcenter ()   
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.3.2' //1
    }
}

allprojects {
    repositories {
        google()
        jcenter ()  
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

Note 1: Configure the dependent Gradle plug-in version. The Gradle plug-in belongs to a third-party plug-in. Therefore, configure Google's Maven library and JCenter library in the buildscrip block, so that the Gradle system can find the corresponding Gradle plug-in.
If you use the error google()report not found: 'google()', you can replace it with the following code:

Code

maven {url 'https://maven.google.com'}

If you don't understand the Gradle plug-in, you can check the Gradle core idea (5) The easy-to-understand Gradle plug-in explanation of this article.

4. Module build.gradle

Create a new Android project, the content of its module build.gradle is as follows:

gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.example.myapplication"
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint: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'
}

4.1 Android plugin types for Gradle

The plug-in id introduced by apply is com.android.application, indicating that the current module is an application module. There are multiple types of Gradle Android plug-ins:

  • Application plug-in, the plug-in id is com.android.application, an APK will be generated.
  • The library plug-in, the plug-in id is com.android.library, it will generate an AAR and provide it to other application modules.
  • 测试插件,插件id为com.android.test,用于测试其他的模块。
  • feature插件,插件id为com.android.feature,创建Android Instant App时需要用到的插件。
  • Instant App插件,插件id为com.android.instantapp,是Android Instant App的入口。

4.2 Android块

Android块用于描述该Module构建过程中所用到的所有参数。

  • compileSdkVersion:配置编译该模块的SDK版本
  • buildToolsVersion:Android构建工具的版本

4.2.1 defaultConfig块

Android块中的defaultConfig块用于默认配置,常用的配置如下所示。

属性 描述
applicationId 指定App的包名
minSdkVersion App最低支持的SDK版本
targetSdkVersion 基于哪个SDK版本开发
versionCode App内部的版本号,用于控制App升级
versionName App版本名称,也就是发布的版本号
testApplicationId 配置测试App的包名
testInstrumentationRunner 配置单元测试使用的Runner,默认为android.test.InstrumentationTestRunner
proguardFile ProGuard混淆所使用的ProGuard配置文件
proguardFiles 同时配置多个ProGuard配置文件
signingConfig 配置默认的签名信息

4.2.2 buildTypes块

buildTypes块用于配置构建不同类型的APK。
当我们新建一个项目时,在Android块已经默认配置了 buildTypes块:

gradle

buildTypes {
       release {
           minifyEnabled false
           proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
       }
   }

在AS的Terminal中执行gradlew.bat build命令,会在该模块的build/outputs/apk目录中生成release和debug的APK,虽然只配置了release ,但release和debug是默认配置,即使我们不配置也会生成。也可以修改默认的release和debug,甚至可以自定义构建类型,比如:

gradle

buildTypes {
       release {
           minifyEnabled false
           proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
       }
       debug {
           debuggable true
       }
       privitedebug{
           applicationIdSuffix ""
       }
   }

这时会在build/outputs/apk目录中生成release、debug、privitedebug的APK。
buildTypes块还可以配置很多属性,常用的配置如下所示。

属性 描述
applicationIdSuffix 配置applicationId的后缀
debuggable 表示是否支持断点调试
jniDebuggable 表示是否可以调试NDK代码
buildConfigField 配置不同的开发环境,比如测试环境和正式环境
shrinkResources 是否自动清理未使用的资源,默认值为false
zipAlignEnabled 是否开启开启zipalign优化,提高apk运行效率
proguardFile ProGuard混淆所使用的ProGuard配置文件
proguardFiles 同事配置多个ProGuard配置文件
signingConfig 配置默认的签名信息
multiDexEnabled 是否启用自动拆分多个Dex的功能

4.2.3 signingConfigs块

用于配置签名设置,一般用来配置release模式。

属性 描述
storeFile 签名证书文件
storePassword 签名证书文件的密码
storeType 签名证书的类型
keyAlias 签名证书中密钥别名
keyPassword 签名证书中密钥的密码

gradle

signingConfigs {
        release {
            storeFile file('C:/Users/liuwangshu/.android/release.keystore')
            storePassword 'android'
            keyAlias 'androidreleasekey'
            keyPassword 'android'
           
        }

4.2.4 其他配置块

android块中除了前面讲的defaultConfig块、buildTypes块、signingConfigs块还有其他的配置块,这里列举一些。

描述
sourceSets 配置目录指向
productFlavors 多个渠道配置
lintOptions Lint配置
dexOptions DEX工具配置
adbOptions adb配置
packagingOptions 打包时的相关配置

更多的配置块请参考官方文档

4.2.5 全局配置

如果有多个module的配置是一样的,可以将这些配置提取出来,也就是使用全局配置。全局配置有多种方式,这里介绍其中的两种。
1. 使用ext块配置
在项目build.gradle中使用ext块,如下所示。

gradle

ext{
    compileSdkVersion =28
    buildToolsVersion ="28.0.3"
    minSdkVersion =15
    targetSdkVersion =28
}

在某个module的build.gradle中使用配置:

gradle

apply plugin: 'com.android.application'
android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
    defaultConfig {
        applicationId "com.example.liuwangshu.hookinstrumentation"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
  ...
}
...

2. 使用config.gradle配置
首先在根目录下创建config.gradle文件来进行配置。
config.gradle

gradle

ext{
    android=[
            applicationId:"com.example.liuwangshu.hookinstrumentation",
            compileSdkVersion :28,
            buildToolsVersion :"28.0.3",
            minSdkVersion : 15,
            targetSdkVersion : 28,
    ]

    dependencies =[
            "appcompat-v7" : "com.android.support:appcompat-v7:28.0.0",
            "constraint"  : "com.android.support.constraint:constraint-layout:1.1.3",
    ]
}

接着在项目build.gradle中添加apply from: "config.gradle",这样项目的所有module都能用config.gradle中定义的参数。
最后在module的build.gradle中使用配置:

gradle

apply plugin: 'com.android.application'
android {
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    buildToolsVersion rootProject.ext.android.buildToolsVersion
    defaultConfig {
        applicationId rootProject.ext.android.applicationId
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
   ...
dependencies {
    implementation rootProject.ext.dependencies["constraint"]
    implementation rootProject.ext.dependencies["appcompat-v7"]
    ...
}

4.3 dependencies 块

dependencies 块用于配置该module构建过程中所依赖的所有库。Gradle插件3.4版本新增了 api 和 implementation 来代替 compile 配置依赖,其中 api 和此前的 compile是一样的。dependencies和api主要以下的区别:

  • implementation可以让module在编译时隐藏自己使用的依赖,但是在运行时这个依赖对所有模块是可见的。而api与compile一样,无法隐藏自己使用的依赖。
  • 如果使用api,一个module发生变化,这条依赖链上所有的module都需要重新编译,而使用implemention,只有直接依赖这个module需要重新编译。

 

1.Android签名文件配置

在一般公司中,当团队比较小的时候,App的签名信息都是放到项目中的,甚至会上传到github上,这样做很是方便。但随着团队人数的增多,这样做的风险会越来越大,因为签名信息是重要的资源,这样就不能将签名上传到github上,也就不应该在build.gradle中直接配置签名。
主要有以下的几种解决方法:
1.自定义一个签名配置文件
2.本地~/.gradle/gradle.properties文件中配置签名信息

1.1 自定义签名信息文件

首先,在工程的目录下新建一个文件夹,内部存储签名文件和签名信息文件。签名文件为gradledemo.jks,签名信息文件为keystore.properties。keystore.properties中的配置如下所示。

Code

STORE_FILE=../signfiles/gradledemo.jks
KEY_ALIAS=gradle
STORE_PASSWORD=jinjiesanbuqu
KEY_PASSWORD=jinjiesanbuqu

当然不要忘了在.gitignore中将gradledemo.jks和keystore.properties忽略掉。接着在模块build.gradle中进行配置,如果还不清楚什么是模块build.gradle和项目build.gradle,看Android Gradle (一)Gradle的Android插件入门这篇文章。
在模块build.gradle中加入如下代码。

java

apply plugin: 'com.android.application'
android {
 ...
}
def setSigningProperties(){
    def propFile = file('../signfiles/keystore.properties')
    if (propFile.canRead()){
        def Properties props = new Properties()
        props.load(new FileInputStream(propFile))
        if (props!=null && props.containsKey('STORE_FILE') && props.containsKey('STORE_PASSWORD') &&props.containsKey('KEY_ALIAS') && props.containsKey('KEY_PASSWORD')) {
            android.signingConfigs.release.storeFile = file(props['STORE_FILE'])
            android.signingConfigs.release.storePassword = props['STORE_PASSWORD']
            android.signingConfigs.release.keyAlias = props['KEY_ALIAS']
            android.signingConfigs.release.keyPassword = props['KEY_PASSWORD']
        } else {
            throw new Exception("some key missing")
        }
    }else {
        throw new Exception("keystore.properties not found:" + propFile.absolutePath)

    }
}

setSigningProperties方法用于读取keystore.properties文件中的签名文件的信息。
最后在模块build.gradle中的signingconfigs块中调用setSigningProperties方法就可以了。

java

apply plugin: 'com.android.application'

android {
 ...
    signingConfigs {
        release {
            setSigningProperties()
        }
    }
}

1.2 本地添加签名信息文件

还可以将签名文件和签名信息文件放到本地中。比如签名文件放到/.gradle/gradledemo.jks,签名信息文件放到/.gradle/keystore.properties。这样签名文件和签名信息文件都不会提交到github上。
keystore.properties的内容如下。

Code

GRADLEDOME_RELEASE_STORE_FILE=~/.gradle/release-key.keystore
GRADLEDOM_RELEASE_KEY_ALIAS=key-alias
GRADLEDOM_RELEASE_STORE_PASSWORD=pass
GRADLEDOM_RELEASE_KEY_PASSWORD=pass

在模块build.gradle中的signingconfigs块中配置签名,如下所示。

java

signingConfigs {
        release {
            storeFile file(GRADLEDOME_RELEASE_STORE_FILE)
            storePassword GRADLEDOME_RELEASE_STORE_PASSWORD
            keyAlias GRADLEDOME_RELEASE_KEY_ALIAS
            keyPassword GRADLEDOME_RELEASE_KEY_PASSWORD
        }
    }

除了这两点,还可以将签名文件和签名信息文件放在专门打包的服务器上,在打包的时候读取即可。这个涉及的内容就多了,就不在本文进行说明了。

2.Gradle的库依赖

现在一个Android项目都是需要去引入其他的库,比如jar、aar、Module等等,现在我们分别来介绍下。下面例子的代码如果不特意说明均是写在模块build.gradle中的。
Gradle的本地库依赖
关于jar依赖可以按照如下这么写,可以指定一个也可以指定多个jar。

java

//依赖引入libs下所有的jar
implementation fileTree(dir:'libs',include:['*.jar'])

//指定依赖某一个或几个jar
implementation files('libs/XXX.jar','libs/XXX.jar')

aar依赖需要额外增加一些语句,如下所示。

java

android {
    ...
    repositories { 
        flatDir {
            dirs "libs"
        }
    }
}    
dependencies {
implementation fileTree(dir:'libs',include:['*.aar'])
implementation(name:'XXX',ext:'aar')
}

Gradle的本地Module依赖
当项目中有多个Module时,我们需要在settings.gradle中引入,如下所示。

java

include ':app'
include ':library1', ':library2'

接着在模块build.gradle引入。

java

implementation project(':library1')

Gradle的远程库依赖
当在Android Studio中新建一个项目时,会在项目build.gradle有如下代码:

java

buildscript {
    repositories {
        google()
        jcenter()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.4.0'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        
    }
}

这些代码都是默认的,在buildscript和allprojects块中,通过repositories来引入谷歌的Maven库和JCenter库。首先会从谷歌的Maven库去寻找相应的库,如果找不到会从JCenter库中去寻找。
然后在模块build.gradle加入如下的代码,就可以引入远程库。

java

implementation group:'com.android.support',name:'appcompat-v7',version:'28.0.0'
//简写
implementation 'com.android.support:appcompat-v7:28.0.0'

3.Gradle的库依赖管理

随着Gradle依赖的库越来越多,那么必然会产生一些问题,比如依赖冲突的问题,为了解决依赖冲突,我们需要先了解Gradle的库依赖管理的几个技术点。

3.1 Gradle的依赖传递

Gradle默认是支持依赖传递的,所以当用到Gradle依赖时一定会涉及到它,是必须要知道的一个知识点。
那什么是依赖传递呢?举一个最简单的例子。
projectC依赖projectB,projectB依赖projectA,那么projectC就依赖了projectA。
依赖传递会产生一些问题,比如重复依赖、依赖错误等问题,那么我们可以通过transitive来禁止依赖传递。

java

implementation('com.xxx.xxx:xxx:3.6.3') {
     transitive false
 }

上面禁止了com.xxx.xxx:xxx:3.6.3库的依赖传递,还可以使用如下语句来关闭当前模块的所有库的依赖传递:

java

configurations.all {
   transitive = false
}

只不过这样就需要手动添加当前模块的每个库的依赖项,一般不会这么做。

3.2 Gradle的依赖检查

有了依赖检查,我们可以解决依赖产生的问题。依赖检查有很多种方式,分别来介绍下。

使用Gradle的命令行
可以直接使用Gradle的命令行来进行依赖检查,拿Windows平台来说,使用cmd进入项目的根目录,执行gradle :app:dependencies即可,其中app是我们新建工程时默认的模块的名称。日志输出很多,下面截取一部分:

Code

+--- com.android.support:appcompat-v7:28.0.0
|    +--- com.android.support:support-annotations:28.0.0 //1
|    +--- com.android.support:support-compat:28.0.0 //2
|    |    +--- com.android.support:support-annotations:28.0.0
|    |    +--- com.android.support:collections:28.0.0
|    |    |    \--- com.android.support:support-annotations:28.0.0
|    |    +--- android.arch.lifecycle:runtime:1.1.1
|    |    |    +--- android.arch.lifecycle:common:1.1.1
|    |    |    |    \--- com.android.support:support-annotations:26.1.0 -> 28.0.0 //3
|    |    |    +--- android.arch.core:common:1.1.1
|    |    |    |    \--- com.android.support:support-annotations:26.1.0 -> 28.0.0
|    |    |    \--- com.android.support:support-annotations:26.1.0 -> 28.0.0
|    |    \--- com.android.support:versionedparcelable:28.0.0
|    |         +--- com.android.support:support-annotations:28.0.0
|    |         \--- com.android.support:collections:28.0.0 (*)

上面是appcompat-v7:28.0.0库的依赖树的一小部分,appcompat-v7:28.0.0依赖了注释1处和注释2的库,注释2处的库又依赖了com.android.support:support-annotations:28.0.0和com.android.support:collections:28.0.0,因此当我们引入appcompat-v7:28.0.0时,会自动下载所有它依赖传递的库。注释3处说明,Gradle在依赖传递时,会自动提升依赖传递的库的版本,默认使用最高版本的库。

使用Gradle面板
除了命令行,还可以使用Android Studio中的右侧的Gradle面板,找到app模块,展开后找到help目录中的dependencies,如下图所示。
eujYaq.png
双击或者右键选择第一个选项即可执行命令,日志就会在AS中Run窗口中打印出来。
现在再举个例子,拿我们熟悉的retrofit举例,在模块build.gradle中引入retrofit:

java

implementation 'com.squareup.retrofit2:retrofit:2.6.0'

执行依赖检查命令,打印的关于retrofit的日志如下:

Code

+--- com.squareup.retrofit2:retrofit:2.6.0
|    \--- com.squareup.okhttp3:okhttp:3.12.0
|         \--- com.squareup.okio:okio:1.15.0

可以很清楚看到retrofit:2.6.0依赖okhttp:3.12.0,而okhttp:3.12.0依赖okio:1.15.0。
这时我们使用3.1小节的transitive试试,修改build.gradle:

java

implementation ('com.squareup.retrofit2:retrofit:2.6.0') {
      transitive false
}

执行依赖检查命令,打印的关于retrofit的日志如下:

Code

+--- com.squareup.retrofit2:retrofit:2.6.0

使用Gradle View插件
如果你觉得前两种方式查看不方便、不直观,还可以使用Android Studio的Gradle View插件。
在AS中选择File–>Settings–>Plugins中搜索gradle view,找到Gradle View插件安装并重启AS,如下图所示。
eujUiV.png

接下来选择View-–>Tools Windows–Gradle View,这时就可以在AS的底部发现Gradle View窗口,里面会显示当前项目的所有依赖树,如下图所示。
eujtI0.png

3.3 Gradle的依赖冲突

依赖冲突产生的原因多是库的版本问题,举个例子,如果在build.gradle中这么写:

java

implementation 'com.squareup.retrofit2:retrofit:2.6.0'
implementation 'com.squareup.okio:okio:1.14.0'

在3.2小节中,我们知道retrofit:2.6.0依赖的okio的版本是1.15.0,而这里引入的okio的版本为1.14.0,引入的版本不同就会产生依赖冲突。依赖冲突的解决的关键有两点,一个是Gradle的依赖检查,这个在3.2小节已经讲过了,另一个是利用Gradle的关键字,合理利用它们是解决依赖冲突的关键,在3.1小节已经介绍了 transitive,现在介绍其余的。

3.3.1 force

有时候我们不是想要排除某个库,而是需要强制使用统一的库的版本,force可以强制设置模块的库的版本,在模块build.gradle中加入如下代码。

java

configurations.all {
    resolutionStrategy {
        force 'com.squareup.okio:okio:2.1.0'
    }
}
dependencies {
...
}

强制当前模块的okio的版本为2.1.0,使用依赖检查来查看下retrofit的依赖:

Code

+--- com.squareup.retrofit2:retrofit:2.6.0
|    \--- com.squareup.okhttp3:okhttp:3.12.0
|         \--- com.squareup.okio:okio:1.15.0 -> 2.1.0
\--- com.squareup.okio:okio:1.14.0 -> 2.1.0

可以看到okio的版本都被强制升级到了2.1.0,这样就可以解决一些依赖冲突的问题。

3.3.2 exclude

有些时候需要排除库依赖传递中涉及的库,此时不能靠关闭依赖传递来解决问题,这时可以使用exclude。
我们知道com.android.support:appcompat-v7:28.0.0依赖于com.android.support:support-annotations:28.0.0、com.android.support:support-compat:28.0.0、com.android.support:cursoradapter:28.0.0等库,这时我们不想再依赖support-annotations库,可以这么写。

java

configurations {
    all*.exclude group: 'com.android.support', module: 'support-annotations'
}
dependencies {
...
}

使用依赖检查来查看com.android.support:appcompat-v7:28.0.0的依赖:

Code

+--- com.android.support:appcompat-v7:28.0.0
|    +--- com.android.support:support-compat:28.0.0
|    |    +--- com.android.support:collections:28.0.0
|    |    +--- android.arch.lifecycle:runtime:1.1.1
|    |    |    +--- android.arch.lifecycle:common:1.1.1
|    |    |    \--- android.arch.core:common:1.1.1
|    |    \--- com.android.support:versionedparcelable:28.0.0
|    |         \--- com.android.support:collections:28.0.0
|    +--- com.android.support:collections:28.0.0
|    +--- com.android.support:cursoradapter:28.0.0

Compared with the log in section 3.2, it can be found that com.android.support:appcompat-v7:28.0.0 no longer depends on com.android.support:support-annotations:28.0, and the goal is achieved.

Guess you like

Origin blog.csdn.net/xfb1989/article/details/110085131