赞
踩
Gradle是新一代的自动化构建工具,一个基于JVM的构建工具,它是一个独立的项目,跟AS,Android无关,类似Ant,Maven这类构建工具都是基于xml来进行描述的,很臃肿,而Gradle采用的是一种叫做Groovy的语言,语法跟Java语法很像,但是一种动态语言,而且在Java基础上做了不少改进,用起来更加简洁、灵活,而且Gradle完全兼容Maven、lvy,这点基本上宣布Maven、lvy可以被抛弃了,Gradle的推出主要以Java应用为主,当然目前还支持Android、C、C++;
构建工具就是对你的项目进行编译、运行、签名、打包、依赖管理等一系列功能集合,传统的构建工具有Make、Ant、Maven、lvy等,而Gradle是新一代的自动化构建工具;
gradle优势(gradle提供了什么)
1). 一种可切换的,像maven一样的基于约定的构建框架,却又从不锁住你(约定优于配置)
2). 强大的支持多工程的构建
3). 强大的依赖管理(基于Apache Ivy),提供最大的便利去构建你的工程
4). 全力支持已有的Maven或者Ivy仓库基础建设
5). 支持传递性依赖管理,在不需要远程仓库和pom.xml和ivy配置文件的前提下
6). 基于groovy脚本构建,其build脚本使用groovy语言编写
7). 具有广泛的领域模型支持你的构建
参考:gradle_百度百科
Gradle 跟 Android Studio 其实没有关系,但是 Gradle 官方还是很看重 Android 开发的,Google 在推出 AS 的时候选中了 Gradle 作为构建工具,为了支持 Gradle 能在 AS 上使用,Google 做了个 AS 的插件叫 Android Gradle Plugin ,所以我们能在 AS 上使用 Gradle 完全是因为这个插件的原因。在项目的根目录有个 build.gradle 文件,里面有这么一句代码:
buildscript { dependencies { classpath "com.android.tools.build:gradle:4.1.2" } }
这个就是依赖 gradle 插件的代码,后面的版本号代表的是 android gradle plugin 的版本,而不是 Gradle 的版本,这个是 Google 定的,跟 Gradle 官方没关系。
Android Gradle Plugin每个版本的具体变化和具体功能参考:Android Gradle 插件版本说明 | Android 开发者 | Android Developers
project
├─── setting.gradle
├─── build.grade
├─── app
│ └─── build.gradle
└─── libraries
├─── library1
│ └─── build.gradle
└─── library2
└─── build.gradle
以一个测试项目为例,来介绍下一个完整的Android项目包含的基本Gradle相关的配置:
红色标记部分从上到下一步步分析;
这个文件是app文件夹下这个Module的gradle配置文件,也可以算是整个项目最主要的gradle配置文件,具体里面的配置下面会介绍;
每个Module都需要有一个gradle配置文件,语法都是一样,唯一不同的是开头可能不一样;
可以独立为APP的Module使用开头:
plugins { id 'com.android.application' }
作为library的Module使用开头:
plugins { id 'com.android.library' }
这个目录下有个wrapper文件夹,里面可以看到两个文件,我们主要看下gradle-wrapper.properties这个文件的内容:
#Thu Nov 04 13:56:00 CST 2021 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
其中GRADLE_USER_HOME通常指~/.gradle
,从图示项目中能够知道我要使用gradle-6.5版本,从https://services.gradle.org/distributions/gradle-6.5-bin.zip
下载,下载到本地的~/.gradle/wrapper/dists
目录。那是否是各个项目的Gradle都要经过Gradle Wrapper下载,能不能全部的项目共用一个Gradle?这样理论上是能够的,可是因为Gradle自己不必定保持彻底的兼容性,因此新老项目共用一个Gradle有时可能会遇到意想不到的问题。指定对应版本的Gradle,而不经过Gradle Wrapper下载的设置方式是选择Specified location同时指定Gradle home:
这个文件是整个项目的gradle基础配置文件,默认的内容就是声明了android gradle plugin的版本;
- buildscript {
- dependencies {
- classpath "com.android.tools.build:gradle:4.1.2"
- }
- }
这个文件是全局项目配置文件,里面主要生命一些需要加入gradle的module,我们看看FloatWindow内容:
include ':app' include ':library' rootProject.name = "FloatWindowL"
- // Top-level build file where you can add configuration options common to all sub-projects/modules.
- buildscript {//这里是gradle脚本执行所需依赖,分别对应的maven库和插件
- repositories {
- google()//Android Studio3.0后新增了google()配置,可以引用google上的开源项目
- jcenter()//是一个类似于github的代码托管仓库,声明了jcenter()配置,可以轻松引用 jcenter上的开源项目
- }
- dependencies {
- //此处是android的插件gradle,gradle是一个强大的项目构建工具
- classpath "com.android.tools.build:gradle:4.1.2"
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
- }
- }
-
- allprojects { //这里是项目本身需要的依赖,比如项目所需的maven库
- repositories {
- google()
- jcenter()
- }
- }
- //运行gradle clean时,执行此处定义的task任务;
- //该任务继承自Delete,删除根目录中的build目录
- //相当于执行Delete.delete(rootProject.bulidDir)
- //gradle使用groovy语言,调用method时可以不用加()。
- task clean(type: Delete) {
- delete rootProject.buildDir
- }
repositories{}闭包:配置远程仓库
该闭包中声明了jcenter()和google()的配置,其中jcenter是一个代码托管仓库,上面托管了很多Android开源项目,在这里配置了jcenter后我们可以在项目中方便引用jcenter上的开源项目,从Android Studio3.0后新增了google()配置,可以引用google上的开源项目;
dependencies{}闭包:配置构造工具
该闭包使用classpath声明了一个gradle插件,由于gradle并不是用来构建Android项目,因此此处引入相关插件来构建Android项目,启动4.1.2为该插件的版本号,可以根据最新的版本号调整;
从文件内容可以看出,主要分为三部分,如下:
- plugins {
- id 'com.android.application'
- }
-
- android {
- compileSdkVersion 30
- buildToolsVersion "30.0.3"
-
- defaultConfig {
- applicationId "com.gome.floatwindowl"
- minSdkVersion 17
- targetSdkVersion 30
- versionCode 1
- versionName "1.0"
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
- }
-
- dependencies {
-
- implementation 'androidx.appcompat:appcompat:1.1.0'
- implementation 'com.google.android.material:material:1.1.0'
- implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
- implementation 'androidx.navigation:navigation-fragment:2.2.2'
- implementation 'androidx.navigation:navigation-ui:2.2.2'
- testImplementation 'junit:junit:4.+'
- androidTestImplementation 'androidx.test.ext:junit:1.1.1'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
- }
//声明Android程序 //com.android.application 表示这是一个应用程序模块 //com.android.library 标识这是一个库模块 //区别:前者可以直接运行,后者是依附别的应用程序运行 plugins { id 'com.android.application' }
文件中第一行使用plugin表示应用一个插件,该插件一般有两种值选择:
这个闭包主要为了配置项目构建的各种属性;
添加signingConfigs{}闭包:
signingConfigs {// 自动化打包配置 release {// 线上环境 keyAlias 'test' keyPassword '123456' storeFile file('test.keystore') storePassword '123456' } debug {// 开发环境 keyAlias 'test' keyPassword '123456' storeFile file('test.keystore') storePassword '123456' } }
可以手动添加签名配置,也可以通过Project Structure选中app,点击Signing添加,具体步骤如下图所示:
签名配置完成后可以方便带签名打包,在module的Build Variants中有两个Type,分别是debug和release,可以选择任意一个类型进行打包,并且他们会利用各自配置的Key进行打包,执行 Run app或者Build->Build apk就会自动在module name/app/build/outputs/apk路径下生成Apk文件。另一种打包方式是Build->Generate Signed APK填写签名信息生成Apk。
compileSdkVersion:设置编译时用的Android版本;
buildToolsVersion:设置编译时使用的构建工具版本,Android Studio3.0后去除此项配置;
defaultConfig{}闭包:
compileSdkVersion 30 //设置项目编译时用的Android版本 buildToolsVersion "30.0.3" defaultConfig { applicationId "com.gome.floatwindowl" //项目包名 minSdkVersion 17 //项目最低的兼容版本 targetSdkVersion 30 //项目的目标版本 versionCode 1 //版本号 versionName "1.0" //版本名称 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" //表明要使用AndroidJUnitRunner进行单元测试 }
buildTypes{}闭包:
这个闭包主要指定生成安装文件的主要配置,一般包含两个子闭包,一个是debug闭包,用于指定生成测试版安装文件的配置,可以忽略不写;另一个是release闭包,用于指定生成正式版安装文件的配置。两者能配置的参数相同,最大的区别默认属性配置不一样,两种模式支持的属性配置如下图:
- buildTypes {// 生产/测试环境配置
- release {// 生产环境
- buildConfigField("boolean", "LOG_DEBUG", "false")//配置Log日志
- buildConfigField("String", "URL_PERFIX", "\"https://release.cn/\"")// 配置URL前缀
- minifyEnabled false//是否对代码进行混淆
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'//指定混淆的规则文件
- signingConfig signingConfigs.release//设置签名信息
- pseudoLocalesEnabled false//是否在APK中生成伪语言环境,帮助国际化的东西,一般使用的不多
- zipAlignEnabled true//是否对APK包执行ZIP对齐优化,减小zip体积,增加运行效率
- applicationIdSuffix 'test'//在applicationId 中添加了一个后缀,一般使用的不多
- versionNameSuffix 'test'//在applicationId 中添加了一个后缀,一般使用的不多
- }
- debug {// 测试环境
- buildConfigField("boolean", "LOG_DEBUG", "true")//配置Log日志
- buildConfigField("String", "URL_PERFIX", "\"https://test.com/\"")// 配置URL前缀
- minifyEnabled false//是否对代码进行混淆
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'//指定混淆的规则文件
- signingConfig signingConfigs.debug//设置签名信息
- debuggable false//是否支持断点调试
- jniDebuggable false//是否可以调试NDK代码
- renderscriptDebuggable false//是否开启渲染脚本就是一些c写的渲染方法
- zipAlignEnabled true//是否对APK包执行ZIP对齐优化,减小zip体积,增加运行效率
- pseudoLocalesEnabled false//是否在APK中生成伪语言环境,帮助国际化的东西,一般使用的不多
- applicationIdSuffix 'test'//在applicationId 中添加了一个后缀,一般使用的不多
- versionNameSuffix 'test'//在applicationId 中添加了一个后缀,一般使用的不多
- }
- }
release{}闭包和debug{}闭包两者能配置的参数相同,最大的区别默认属性配置不一样:
sourceSets{}闭包:配置目录指向
- sourceSets {//目录指向配置
- main {
- jniLibs.srcDirs = ['libs']//指定lib库目录
- }
- }
配置 jniLibs.srcDirs = [‘libs’],可以在Android studio的Android视图下生成jniLibs文件夹,可以方便我们存放jar包和库文件,其中Android视图下的jniLibs和project视图下的libs指向同一文件夹(app→libs),如下图所示:
当需要调试版本和正式版本使用不同的manifest文件时,可以做如下配置:
sourceSets {
main {
if (isDebug.toBoolean()) {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/release/AndroidManifest.xml'
}
}
}
packagingOptions{}闭包:打包时的相关配置
当项目中依赖的第三方库越来越多时,有可能会出现两个依赖库中存在同一个(名称)文件。如果这样,Gradle在打包时就会提示错误(警告)。那么就可以根据提示,然后使用以下方法将重复的文件剔除,比较常用的是通过exclude去除重复的文件,例如:
- packagingOptions{
- //pickFirsts做用是 当有重复文件时 打包会报错 这样配置会使用第一个匹配的文件打包进入apk
- // 表示当apk中有重复的META-INF目录下有重复的LICENSE文件时 只用第一个 这样打包就不会报错
- pickFirsts = ['META-INF/LICENSE']
-
- //merges何必 当出现重复文件时 合并重复的文件 然后打包入apk
- //这个是有默认值得 merges = [] 这样会把默默认值去掉 所以我们用下面这种方式 在默认值后添加
- merge 'META-INF/LICENSE'
-
- //这个是在同时使用butterknife、dagger2做的一个处理。同理,遇到类似的问题,只要根据gradle的提示,做类似处理即可。
- exclude 'META-INF/services/javax.annotation.processing.Processor'
- }
productFlavors{}闭包:多个渠道配置
这个配置是经常会使用到的,通常在适配多个渠道的时候,需要为特定的渠道做部分特殊的处理,比如设置不同的包名、应用名等。场景:当我们使用友盟统计时,通常需要设置一个渠道ID,那么我们就可以利用productFlavors来生成对应渠道信息的包,如:
productFlavors { wandoujia { //豌豆荚渠道包配置 manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"] //manifestPlaceholders的使用在后续章节(AndroidManifest里的占位符)中介绍 } xiaomi { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi"] applicationId "com.wiky.gradle.xiaomi" //配置包名 } _360 { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "_360"] } //... }
当然也有更简洁的方式
- android {
- productFlavors {
- wandoujia {}
- xiaomi {}
- _360 {}
- //...
- }
-
- productFlavors.all {
- //批量修改,类似一个循序遍历
- flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
- }
- }
配置完之后,在命令行窗口中(Terminal)中输入gradlew assembleRelease(windows)即可开始打包,在Mac系统中对应指令应该是./gradlew assembleRelease。当然,如果想要debug版本的包,将指令中assembleRelease改为assembleDebug即可。最后生成的包还是在
app/build/outputs/apk中,默认命名格式如app-wandoujia-release-unsigned.apk,在module的Build Variants中可以选择相应的渠道。
注:Android Studio3.0需在主app的build.gradle里面的
defaultConfig {applicationId:"xxx.xxx.xxx"
targetSdkVersion:***
minSdkVersion :***
versionCode:***
versionName :***
//版本名后面添加一句话,意思就是flavor dimension 它的维度就是该版本号,这样维度就是都是统一的了
flavorDimensions “versionCode”
}
lintOptions{}闭包:代码扫描分析
Lint 是Android Studio 提供的 代码扫描分析工具,它可以帮助我们发现代码结构/质量问题,同时提供一些解决方案,而且这个过程不需要我们手写测试用例。
Lint 发现的每个问题都有描述信息和等级(和测试发现 bug 很相似),我们可以很方便地定位问题,同时按照严重程度进行解决。
- //程序在编译的时候会检查lint,有任何错误提示会停止build,我们可以关闭这个开关
- lintOptions {
- abortOnError false //即使报错也不会停止打包
- checkReleaseBuilds false //打包release版本的时候进行检测
- }
该闭包定义了项目的依赖关系,一般项目都有三种依赖方式:本地依赖、库依赖和远程依赖。本地依赖可以对本地的jar包或目录添加依赖关系,库依赖可以对项目中的库模块添加依赖关系,远程依赖可以对jcener库上的开源项目添加依赖关系。从Android Studio3.0后compile引入库不在使用,而是通过api和implementation,api完全等同于以前的compile,用api引入的库整个项目都可以使用,用implementation引入的库只有对应的Module能使用,其他Module不能使用,由于之前的项目统一用compile依赖,导致的情况就是模块耦合性太高,不利于项目拆解,使用implementation之后虽然使用起来复杂了但是做到降低偶合兴提高安全性。
- dependencies {//项目的依赖关系
- implementation fileTree(include: ['*.jar'], dir: 'libs')//本地jar包依赖
- implementation 'com.android.support:appcompat-v7:27.1.1'//远程依赖
- implementation 'com.android.support.constraint:constraint-layout:1.1.2'
- 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'
- }
- // 声明是Android程序,
- //com.android.application 表示这是一个应用程序模块
- //com.android.library 标识这是一个库模块
- //而这区别:前者可以直接运行,后着是依附别的应用程序运行
- apply plugin: 'com.android.application'
-
- android {
- signingConfigs {// 自动化打包配置
- release {// 线上环境
- keyAlias 'test'
- keyPassword '123456'
- storeFile file('test.jks')
- storePassword '123456'
- }
- debug {// 开发环境
- keyAlias 'test'
- keyPassword '123456'
- storeFile file('test.jks')
- storePassword '123456'
- }
- }
- compileSdkVersion 27//设置编译时用的Android版本
- defaultConfig {
- applicationId "com.billy.myapplication"//项目的包名
- minSdkVersion 16//项目最低兼容的版本
- targetSdkVersion 27//项目的目标版本
- versionCode 1//版本号
- versionName "1.0"//版本名称
- flavorDimensions "versionCode"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"//表明要使用AndroidJUnitRunner进行单元测试
- }
- buildTypes {// 生产/测试环境配置
- release {// 生产环境
- buildConfigField("boolean", "LOG_DEBUG", "false")//配置Log日志
- buildConfigField("String", "URL_PERFIX", "\"https://release.cn/\"")// 配置URL前缀
- minifyEnabled false//是否对代码进行混淆
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'//指定混淆的规则文件
- signingConfig signingConfigs.release//设置签名信息
- pseudoLocalesEnabled false//是否在APK中生成伪语言环境,帮助国际化的东西,一般使用的不多
- zipAlignEnabled true//是否对APK包执行ZIP对齐优化,减小zip体积,增加运行效率
- applicationIdSuffix 'test'//在applicationId 中添加了一个后缀,一般使用的不多
- versionNameSuffix 'test'//在applicationId 中添加了一个后缀,一般使用的不多
- }
- debug {// 测试环境
- buildConfigField("boolean", "LOG_DEBUG", "true")//配置Log日志
- buildConfigField("String", "URL_PERFIX", "\"https://test.com/\"")// 配置URL前缀
- minifyEnabled false//是否对代码进行混淆
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'//指定混淆的规则文件
- signingConfig signingConfigs.debug//设置签名信息
- debuggable false//是否支持断点调试
- jniDebuggable false//是否可以调试NDK代码
- renderscriptDebuggable false//是否开启渲染脚本就是一些c写的渲染方法
- zipAlignEnabled true//是否对APK包执行ZIP对齐优化,减小zip体积,增加运行效率
- pseudoLocalesEnabled false//是否在APK中生成伪语言环境,帮助国际化的东西,一般使用的不多
- applicationIdSuffix 'test'//在applicationId 中添加了一个后缀,一般使用的不多
- versionNameSuffix 'test'//在applicationId 中添加了一个后缀,一般使用的不多
- }
- }
-
- sourceSets {//目录指向配置
- main {
- jniLibs.srcDirs = ['libs']//指定lib库目录
- }
- }
-
- packagingOptions{//打包时的相关配置
- //pickFirsts做用是 当有重复文件时 打包会报错 这样配置会使用第一个匹配的文件打包进入apk
- // 表示当apk中有重复的META-INF目录下有重复的LICENSE文件时 只用第一个 这样打包就不会报错
- pickFirsts = ['META-INF/LICENSE']
-
- //merges何必 当出现重复文件时 合并重复的文件 然后打包入apk
- //这个是有默认值得 merges = [] 这样会把默默认值去掉 所以我们用下面这种方式 在默认值后添加
- merge 'META-INF/LICENSE'
-
- //这个是在同时使用butterknife、dagger2做的一个处理。同理,遇到类似的问题,只要根据gradle的提示,做类似处理即可。
- exclude 'META-INF/services/javax.annotation.processing.Processor'
- }
-
- productFlavors {
- wandoujia {}
- xiaomi {}
- _360 {}
- }
-
- productFlavors.all {
- //批量修改,类似一个循序遍历
- flavor -> flavor.manifestPlaceholders = [IFLYTEK_CHANNEL: name]
- }
-
- //程序在编译的时候会检查lint,有任何错误提示会停止build,我们可以关闭这个开关
- lintOptions {
- abortOnError false
- //即使报错也不会停止打包
- checkReleaseBuilds false
- //打包release版本的时候进行检测
- }
-
- }
-
- dependencies {
- //项目的依赖关系
- implementation fileTree(include: ['*.jar'], dir: 'libs')
- //本地jar包依赖
- implementation 'com.android.support:appcompat-v7:27.1.1'
- //远程依赖
- implementation 'com.android.support.constraint:constraint-layout:1.1.2'
- 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'
- }
APK的构建过程可以根据官方提供的流程图如下图所示
这个APK构建的过程主要分为以下几步:
1. 通过AAPT(Android Asset Packaging Tool)打包res资源文件,比如AndroidManifest.xml、xml布局文件等,并将这些xml文件编译为二进制,其中assets和raw文件夹的文件不会被编译为二进制,最终会生成R.java和resources.arsc文件。
2. AIDL工具会将所有的aidl接口转化为对应的Java接口。
3. 所有的Java代码,包括R.java和Java接口都会被Java编译器编译成.class文件。
4. Dex工具会将上一步生成的.class文件、第三库和其他.class文件编译成.dex文件。
5. 上一步编译生成的.dex文件、编译过的资源、无需编译的资源(如图片等)会被ApkBuilder工具打包成APK文件。
6. 使用Debug Keystore或者Release Keystore对上一步生成的APK文件进行签名。
7. 如果是对APK正式签名,还需要使用zipalign工具对APK进行对齐操作,这样应用运行时会减少内存的开销。
从以上步骤可以看出,APK的构建过程是比较繁琐的,而且这个构建过程又是时常重复的,如果没有构建工具,手动去完成构建工作,无疑对于开发人员是个折磨,也会产生诸多的问题,导致项目开发周期变长。
上面提到了,假设我们没有 IDE ,只有类似 Sublime、Atom、Vim这种轻量编辑器怎么办?那我们就没法开发 Android 了么?然而只要有构建工具,不需要 IDE 我们一样有办法开发,这个时候我们就需要用到几个有用的 Gradle 命令了:
./gradlew -v 版本号
./gradlew clean 清除9GAG/app目录下的build文件夹
./gradlew build 检查依赖并编译打包
这里注意的是 ./gradlew build 命令把 debug、release 环境的包都打出来,如果正式发布只需要打 Release 的包,该怎么办呢,下面介绍一个很有用的命令 assemble , 如
./gradlew assembleDebug 编译并打Debug包
./gradlew assembleRelease 编译并打Release的包
值得注意的是,以上所有命令都是在终端里执行,并且必须要切换到所在项目的根目录下执行,win系统直接执行 gradlew 。
参考:
史上最全Android build.gradle配置详解_林伟茂的博客-CSDN博客
Android SourceSet 使用 参考 示例_TDC的专栏-CSDN博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。