目录
1)Gradle简介
2)Android中的gradle
- 2.1)gradle文件
- 2.2)gradle wrapper
- 2.3)配置输出文件格式
- 2.3.1)基本用法
- 2.3.2)使用签名文件进行签名
- 2.3.3)多渠道包配置
- 2.4)配置Gradle 编译速度
3)Groovy基本语法
4)Gradle插件开发
1)Gradle简介
- mac在~/.bash_profile中添加如下代码
//此处是gradle路径,我本机在AS时已下载过4.1版本,此处我使用已下载好的地址
export GRADLE_HOME=~/.gradle/wrapper/dists/gradle-4.1-all/bzyivzo6n839fup2jbap0tjew/gradle-4.1
export PATH=${PATH}:${GRADLE_HOME}/bin
- 保存配置
source ~/.bash_profile - gradle -v
------------------------------------------------------------
Gradle 4.1
------------------------------------------------------------
Build time: 2017-08-07 14:38:48 UTC
Revision: 941559e020f6c357ebb08d5c67acdb858a3defc2
Groovy: 2.4.11
Ant: Apache Ant(TM) version 1.9.6 compiled on June 29 2015
JVM: 1.8.0_91 (Oracle Corporation 25.91-b14)
OS: Mac OS X 10.12.4 x86_64
2)Android中的gradle
2.1)gradle文件
文件 | 说明 |
---|---|
setting.gradle | 文件定义了哪些module 应该被加入到编译过程 |
Project-build.gradle | 此处的配置会被应用到所有项目中 |
Module-build.gradle | 每个module的配置,会覆盖Project-build.gradle的相同部分 |
// ~/Project-build.gradle
buildscript {
repositories {
google()
//一般用共有的jCenter仓库
jcenter()
}
dependencies {
//gradle-wrapper.properties中配置的是的Gradle的版本.
//build.gradle中的依赖指定的是Gradle插件的版本.
//对应关系请自查询
classpath 'com.android.tools.build:gradle:3.0.0'
}
}
//此处配置会应用至所有module中
allprojects {
repositories {
google()
jcenter()
//有一些项目,可能在公司的私有的仓库中
maven {
url "http://maven.aliyun.com/nexus/content/repositories/releases"
//若有密码 则加上credentials
credentials {
username "xxx"
password "xxx"
}
}
//也可以使用相对路径配置本地仓库
flatDir {
dirs "xxx"
}
}
}
// ~/Module-build.gradle
//android应用程序的gradle插件
apply plugin: 'com.android.application'
//apply plugin: 'com.android.library' //library项目
//apply plugin: 'java-library'
//关于android的配置项
android {
compileSdkVersion 26
//默认配置
defaultConfig {
applicationId "com.tgf.studygradle"
minSdkVersion 21
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
//编译类型配置 默认的有debug、release 的类型
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
//依赖配置,定义了项目需要依赖的其他库
dependencies {
//可以使用文件依赖
implementation fileTree(dir: 'libs', include: ['*.jar'])
//最新版的gradle已经使用api替代了compile。 api和implementation的依赖层次是有区别的,请查资料
//一定要指名依赖版本,尽量不要使用通配符+,可以避免检查是否有新版本,也能做到工程化开发都统一依赖库版本
//对于特定的buildTypes不同依赖的,可以使用debugImplementation或debugApi
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
2.2)gradle wrapper
gradle wrapper的诞生是为了兼容不断发展的Gradle版本。
//通过此命令可以手动创建wrapper脚本,AS创建工程时会自动创建
gradle wrapper
gradle wrapper --gradle-version 4.1 //也可以加入版本号
下载目录
cd ~/.gradle/wrapper/dists
- gradle-wrapper.properties中配置的是的Gradle的版本.
...
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
- 通过wrapper可以使用命令行编译:
命令 | 说明 |
---|---|
./gradlew ... | mac系统 |
gradle.bat | win系统 |
-
使用 ./gradlew tasks可列出所有所有task
常用task | 说明 |
---|---|
./gradlew assemble | 对所有的 buildType 生成 apk 包,会编译debug和release |
./gradlew assembleDebug | 只编译debug版本 |
./gradlew assembleRelease | 只编译release版本 |
./gradlew clean | 删除所有编译输出文件 |
./gradlew check | lint检测, Wrote HTML report to file:///Users/tugaofeng/tgf/study/Learn-Android/studyGradle/app/build/reports/lint-results.html |
./gradlew build | 相当于执行assemble -> check |
2.3)配置输出文件格式
2.3.1)基本用法
- gradle build后会根据配置文件生成BuildConfig.java。因为gradle基于grovvy语言,其是一种JVM语言,可以生成Java字节码文件。
//我们在代码中可以使用BuildConfig 判断当前环境是否debug
if (BuildConfig.DEBUG){
...
}
- 可以在buildTypes中对不同的编译环境定义不同的键值对,这些值在不同的编译包apk中对应的值不一样,来实现一些需求,比如API服务器环境配置
// ~/build.gradle
buildTypes {
debug {
buildConfigField "String", "API_URL", "\"http://www.baidu.com/api\""
buildConfigField "boolean", "LOG_SHOW", "true"
// 相当于在res/strings.xml 下增加一个名为 app_name,debugAPP 的资源
// 注意,这里是添加,在 string.xml 不能有这个字段,会重名!!!
resValue "string", "app_name", "debugAPP"
}
release {
buildConfigField "String", "API_URL", "\"http://www.google.com/api\""
buildConfigField "boolean", "LOG_SHOW", "false"
resValue "string", "app_name", "releaseAPP"
...
}
}
//debug环境编译后的 BuildConfig.java
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.tgf.studygradle";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
// Fields from build type: debug
public static final String API_URL = "http://www.baidu.com/api";
public static final boolean LOG_SHOW = true;
}
Log.d("MainActivity","API环境: "+ BuildConfig.API_URL);
Log.d("MainActivity","是否打印日志: "+ BuildConfig.LOG_SHOW);
Log.d("MainActivity","app_name="+getString(R.string.app_name));
2.3.2)使用签名文件进行签名
// ~/build.gradle
apply plugin: 'com.android.application'
...
//获取local.properties的内容
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
android {
...
//第一种:使用gradle直接签名打包
// signingConfigs {
// release {
// storeFile file("/Users/tugaofeng/tgf/study/Learn-Android/studyGradle/studyGradle_keystore")
// storePassword "123456"
// keyAlias "studyGradle_keystore"
// keyPassword "123456"
// }
// }
//第二种:为了保护签名文件,把它放在local.properties中并在版本库中排除
//不把这些信息写入到版本库中(注意,此种方式签名文件中不能有中文)
signingConfigs {
release {
storeFile file(properties.getProperty("keystroe_storeFile"))
storePassword properties.getProperty("keystroe_storePassword")
keyAlias properties.getProperty("keystroe_keyAlias")
keyPassword properties.getProperty("keystroe_keyPassword")
}
}
buildTypes {
...
release {
...
signingConfig signingConfigs.release
}
}
}
// ~/local.properties
#对应自己实际的证书路径和名字
keystroe_storeFile=/Users/tugaofeng/tgf/study/Learn-Android/studyGradle/studyGradle_keystore
keystroe_storePassword=123456
keystroe_keyAlias=studyGradle_keystore
keystroe_keyPassword=123456
2.3.3)多渠道包配置
apply plugin: 'com.android.application'
//打包时间
def releaseTime() {
return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}
//获取local.properties的内容
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
android {
compileSdkVersion 26
// 默认配置
defaultConfig {
applicationId "com.tgf.studygradle"
minSdkVersion 21
targetSdkVersion 26
versionCode 1
versionName "1.0.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
//为了保护签名文件,把它放在local.properties中并在版本库中排除
//不把这些信息写入到版本库中(注意,此种方式签名文件中不能有中文)
signingConfigs {
release {
storeFile file(properties.getProperty("keystroe_storeFile"))
storePassword properties.getProperty("keystroe_storePassword")
keyAlias properties.getProperty("keystroe_keyAlias")
keyPassword properties.getProperty("keystroe_keyPassword")
}
}
//维度,gradle3.0后此处需要有个默认,名字随便取。此属性可用于不同维度的组合
flavorDimensions "default"
// 多渠道配置
productFlavors {
baidu{
// 每个环境包名不同
applicationId "com.tgf.studygradle.multichannel.baidu"
// 动态添加 string.xml 字段;
// 注意,这里是添加,在 string.xml 不能有这个字段,会重名!!!
resValue "string", "app_name", "studyGradle百度版本"
// 动态修改 常量 字段
buildConfigField "String", "ENVIRONMENT", '"我是百度首页"'
// 修改 AndroidManifest.xml 里渠道变量
//manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
}
qq{
applicationId "com.tgf.studygradle.multichannel.qq"
resValue "string", "app_name", "studyGradle腾讯版本"
buildConfigField "String", "ENVIRONMENT", '"我是腾讯首页"'
//manifestPlaceholders = [UMENG_CHANNEL_VALUE: "qq"]
}
}
buildTypes {
debug {
// debug模式,API服务地址
buildConfigField "String", "API_URL", "\"http://www.baidu.com/api\""
// debug模式,显示log
buildConfigField("boolean", "LOG_SHOW", "true")
//为已经存在的applicationId添加后缀
applicationIdSuffix ".debug"
// 为版本名添加后缀
versionNameSuffix "-debug"
// 不开启混淆
minifyEnabled false
// 不开启ZipAlign优化
zipAlignEnabled false
// 不移除无用的resource文件
shrinkResources false
}
release {
// release模式,API服务地址
buildConfigField "String", "API_URL", "\"http://www.google.com/api\""
// release模式,不显示log
buildConfigField "boolean", "LOG_SHOW", "false"
// 为版本名添加后缀
versionNameSuffix "-release"
// 开启ZipAlign优化
zipAlignEnabled true
// 移除无用的resource文件
shrinkResources true
// 使用config签名
signingConfig signingConfigs.release
// 开启混淆
minifyEnabled true
// 混淆文件位置
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// 批量打包
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "${variant.productFlavors[0].name}_v${defaultConfig.versionName}_${variant.buildType.name}_${releaseTime()}.apk"
}
}
}
}
//忽略lint检测的error [lint配置说明](http://blog.csdn.net/berber78/article/details/60766091)
lintOptions {
// true--错误发生后停止gradle构建
abortOnError false
}
}
dependencies {
//可以使用文件依赖
implementation fileTree(dir: 'libs', include: ['*.jar'])
//最新版的gradle已经使用api替代了compile。 api和implementation的依赖层次是有区别的,请查资料
//一定要指名依赖版本,尽量不要使用通配符+,可以避免检查是否有新版本,也能做到工程化开发都统一依赖库版本
//对于特定的buildTypes不同依赖的,可以使用debugImplementation或debugApi
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
productFlavors和buildTypes一样,可以拥有自己的sourceset文件夹,且productFlavors和buildTypes可以结合拥有一个优先级高于productFlavors和buildTypes单独设置的sourceset,比如想要qq的release版本拥有一个单独的图标,可以建立qqRelease的sourceset。注意:顺序必须是flavor+buildType形式。
2.4)配置Gradle 编译速度
- ./gradle.properties
#加大可用编译内存
org.gradle.jvmargs=-Xmx1536m
# 开启并行编译
org.gradle.parallel=true
#开启守护进程,该进程在第一次启动后一直存在,后续的编译可以重用该进程
org.gradle.daemon=true
-
执行所有task的时候我们都可以通过添加--profile生成一份执行报告
3)Groovy基本语法
- 新建一个文件夹,并在文件夹内创建build.gradle
//变量:使用def关键字 单引号只是一串字符串
def name1 = 'tgf'
//变量:插值占位符 需要使用双引号
def greeting = "hello,$name1"
//方法:如果不指定返回值 则返回最后一行代码
def add(def num) {
num + num
}
//类:类中声明的属性会自动生成get和set方法
//myClass.name = 'xxx'相当于 myClass.setName('xxx')
//myClass.name 相当于 myClass.getName()
class MyClass{
String name
}
//列表
List list = [1,2,3]
//Map
Map map = [id:1,name:'玄策']
//闭包写法,如果只有一个参数 可以忽略此参数,使用it代替
//闭包写法在Android build.gradle中大量使用
/*def multi = { num ->
num * num
}*/
def multi = {
it * it
}
//任务-打印
task myTask << {
println '========打印变量&方法==========='
//测试打印
println "hello world"
//测试打印变量
println greeting
//测试打印方法
println add(2)
println '========打印类属性值==========='
//创建类的实例对象
def myClass = new MyClass()
myClass.name = '大家好'
//测试打印类属性值
println myClass.name
println '=========打印列表数据=========='
//测试打印列表
list.each(){element ->
println element
}
println '=========打印Map数据=========='
println map['id']
println map.get('name')
println '=========打印闭包写法=========='
println multi(4)
}
//任务-从文件夹拷贝至另一个
task copyFile(type: Copy){
from 'src'
into 'dst'
}
//task的依赖关系 dependsOn
//如果是Muliti-Project的模式,依赖关系要带着所属的Project,如taskA.dependsOn ':other-project:taskC' 其中taskC位于和taskA不同的Project中,相对于AndroidStudio来说,就是位于不同的Module下的build.gradle中,而other-project为Module名字。
task taskA <<{
println "这是taskA"
}
task taskB <<{
println "这是taskB"
}
taskA.dependsOn taskB
4)Gradle插件开发简介
-
新建Android-library的Module,删除非必要的文件,保留如下格式
com.tgf.mygradleplugin.properties代表了我们这个插件的id就是com.tgf.mygradleplugin
#指向我们新建的类
implementation-class=com.tgf.mygradleplugin.MyGradlePlugin
- build.gradle
apply plugin: 'java'//导入java插件用于,编译打包我们的插件, 也可以使用apply plugin: 'groovy'
apply plugin: 'maven'//maven插件,用于上传插件到仓库
//uploadArchives 类型是upload,这个task不是'maven'创建的
// 使用命令./gradlew uploadArchives 可以在指定路径生成jar
uploadArchives{
//本地仓库的一种
repositories{
flatDir{
name "localRepository"
dir "localRepository/libs"
}
}
//提交到远程服务器:
// repository(url: "http://xxx") {
// authentication(userName: "xxx", password: "xxx")
// }
//本地的Maven地址设置为D:/repos
// repository(url: uri('/Users/tugaofeng/xxx'))
}
group = "com.tgf.mygradleplugin"//project属性
version = "1.0"//project属性
dependencies {
//导入Gradle的api,要写插件,肯定要使用Gradle的api
compile gradleApi()
}
- 举例:我们使用这个插件来在指定路径里生成一个文件,并写入作者信息
- AuthorBean.java
public class AuthorBean {
//姓名
private String name;
//年龄
private int age;
public AuthorBean(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "作者:"+name+" ,年龄:"+age;
}
@Input
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Input
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
- WriteAuthorTask.java
public class WriteAuthorTask extends DefaultTask{
private AuthorBean authorBean; //作者实体
private String fileName;
private File targetDict;
@Nested
public AuthorBean getAuthorBean() {
return authorBean;
}
@Input
public String getFileName() {
return fileName;
}
@InputDirectory
public File getTargetDict() {
return targetDict;
}
@TaskAction
public void writeObject(){
File targetFile = new File(targetDict,fileName);
try {
FileOutputStream fos = new FileOutputStream(targetFile);
fos.write(authorBean.toString().getBytes());
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void setAuthorBean(AuthorBean authorBean) {
this.authorBean = authorBean;
}
public void setTargetDict(File targetDict) {
this.targetDict = targetDict;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
}
- MyGradlePlugin.java
public class MyGradlePlugin implements Plugin {
@Override
public void apply(Project project) {
WriteAuthorTask task = project.getTasks().create("writeAuthor", WriteAuthorTask.class);
task.setTargetDict(new File("" + "/Users/tugaofeng/Desktop"));
task.setFileName("author.txt");
task.setAuthorBean(new AuthorBean("涂高峰",30));
task.writeObject();
}
}
- Project-build.gradle
buildscript {
repositories {
google()
jcenter()
flatDir name:'localRepository',dir:'mygradleplugin/localRepository/libs'
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
//命名是我们的groupId:moduleName:version
classpath 'com.tgf.mygradleplugin:mygradleplugin:1.0'
}
}
- app的Module-build.gradle
//新增apply插件id
apply plugin: 'com.tgf.mygradleplugin'
-
运行命令或点击AS的命令,会生成对应路径的jar,并执行插件代码,写入txt文件
./gradlew uploadArchives
简单介绍了如何自定义gradle插件,详细的学习在插件化的时候进行学习
参考资料
Gradle 完整指南(Android)
《Gradle for Android》
Groovy 基本语法