Gradle必备知识点

之前对于Gradle的理解就是内嵌在AS里帮项目编译编译,打打包,仅次而已。。。不得其精髓,就无法享受Gradle带给开发的快感。

什么是Gradle?
是一款很优秀的构建工具。
gradle官方文档
gradle中文文档

配置Gradle环境
不要以为它只是AS的附属品,只要配置了Gradle的环境,哪里又可以用,当然也包括服务端,干的事就很多了。

首先需要Java环境,JDK1.6以上,略。。。java -version
下载Gradle 官网下载https://gradle.org/,直接下载地址

distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-all.zip

这里其实我拷贝的Gradle wrapper下gradle-wrapper.properties中的(等会会细说),下载all的是因为包含源代码,文档,实例等。
配置环境变量vim ~/.bash_profile 配置完成source ~/.bash_profile

GRADLE_HOME=/**/**/gradle
PATH=${PATH}:${GRADLE_HOME}/bin
export GRADLE_HOME PATH
gradle -v
//显示版本号即为成功

当然,可以try了
直接新建一个build.gradle文件,写入

task helloWorld<<{
  println "hello wa wa !"
}

cd到同级目录下

//hw是驼峰简写,  q  是日志级别quit ,println是quit级别
gradle -q hw/helloWorld

此处,应该可以简单想到AS gradle的工作原理了,实际里面是好多Gradle的任务。

其实我们在AS送使用的是Gradle Wrapper,wrapper 顾名思义就是在gradle上包括了一层,便于团队开发,比如我用的mac,你用的windows 他用的linux,wrapper可以帮我处理这些,我们可以使用相同的指令就可以了。gradle内置的gradle wrapper可以帮我们生成gradle wrapper

$ gradle wrapper

可以发现生成如下目录

gradle 
  --wrapper
    --gradle-wrapper.jar
    --gradle-wrapper.properties
gradlew
gradlew.bat

gradlew 和gradlew.bat就是linux和windows下的执行脚本
这个层级结构在我们的AS安卓项目里我们也可以看到,豁然开朗
gradle-wrapper.properties就是gradle的配置文件了,我们可以做一些设置,可以对比项目里

android.useDeprecatedNdk=true
org.gradle.daemon=true
zipStorePath=wrapper/dists
org.gradle.parallel=true
zipStoreBase=GRADLE_USER_HOME
org.gradle.caching=true
org.gradle.jvmargs=-Xmx5120M -XX\:MaxPermSize\=512m -XX\:+HeapDumpOnOutOfMemoryError -Dfile.encoding\=UTF-8
//下载gradle的路径
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-all.zip
org.gradle.configureondemand=true
distributionPath=wrapper/dists
//下载gradle解压后的主存储目录
distributionBase=GRADLE_USER_HOME
android.enableAapt2=false
android.useAndroidX=false
android.enableJetifier=false

看名字含义就差不多了;
当然我们也可以通过任务的形式配置wrapper

task wrapper(type:Wrapper){
  gradleVersion = '4.8'
distributionUrl='https\://services.gradle.org/distributions/gradle-4.8-all.zip'
}

日志类似于项目中 e ERROR q QUIT w WARNING i INFO d DEBUG

logger.quit('这是一条重要的日志信息')

查看所有可执行的tasks

./gradlew tasks
./gradlew -help
./gradlew --refresh-dependencies assemble //强制刷新依赖库,防止缓存很有用我们项目中就发生过小伙伴电脑编译没问题,其他人从GitHub拉下来的就编译不通过

首先大家应该明白Project和Task的区别,Gradle可以包含多个Project,多模块项目,每个project包含多task,最后构成了整个gradle的构建,详情参照groovy官网、groovy教程

结合项目看看gradle的生命周期,根project下都有一个setting.gradle,build.gradle:

include ':application', ':modules:xcf_annotation_processor', ':modules:xcf_annotation', ':modules:xcf_annotation_complier', ':modules:xcf_annotation_api'

·

  • 初始化创建Settings实例
  • 解析settings.gradle 构造各个Project实例
  • 解析每个Project对应的build.gradle,配置相应Project

差不多了,先来看几种任务的写法

//方式1
task name<<{//<<其实是doLast的意思,最后执行,{}闭包
  description '添加描述'
}
//方式2
tasks.create(name){
 description '添加描述'
  doLast{
  }  
}
//也可以
name.doFrist{//开始执行
}

//自定义任务
class CustomTask extends DefaultTask{
  @TaskAction
  def doSelf(){
    pritln 'do self'
  }
}
def Task mTask =task execTask(type:CustomTask){
  doFrist{ pritln 'doFrist' }
}
mTask.doLast{
pritln 'doLast' 
}

./gradlew -q execTask
execTask.enable=false//可以禁用这个task

execTask.onlyIf{//满足某些条件才能执行
  boolean
}

//多个任务排序
./gradlew task1 task2
指定顺序可以使用 task2.mustRunAfter(task1)

//任务依赖
task mTask1<<{
    println 'hello'
}
task mTask2(dependsOn:mTask1){
    doLast{
      println 'world'
    }
}
./gradlew -q mTask2
hello world
//还有另外一种写法,依赖多个task
task mTask3<<{
dependsOn:mTask1,mTask2
    println 'end'
}
//每个task其实也是project的一个属性
project.hasdProperty('mTask3')

gradle是基于groovy的,groovy也是基于jvm虚拟机的一种动态语言,语法跟Java相似,懂Java,学习groovy没有任何障碍,完全兼容Java,可以直接写Java代码。接下来是一些基本用法

def str1='hello'
def str2="hello"
//行尾不必写分号
//方法
def method1(int a,int b){
  a+b//此处注意可以省略return 默认最后一行就是返回
}
//两种调用方式
task mTask(){
  method1(2,3)
  method 1,2
}

//集合
task listTask{
  def numList=[1,2,3]
   numList[1]
   numList[-1]
  //it类似Java集合的迭代器
  numList.each{ println it }
}
//map
task mapTask{
  def map=['key':'value']
  map.each{
    println map['key']/map[key]
    println 'key'${it.key}
  }
}
//javabean
class Person {
  private String name;
}

task beanTask{
  Person p=new Person
  p.name="hello"
println "name:${p.name}"
}

//自定义属性,可以是project可以是task

ext.age=18
ext{
    sex='famle'
    phone='1234445'
}

task task1<<{
    println("phone:${phone}")
}

Gradle插件

为什么我们几乎什么也不用关心就能在Android studio中构建我们安卓项目,这是因为Gradle默认实现了很多有用的插件;这些是我们常见的插件,他们帮我们做了哪些平常我们不需要深入关心的工作,比如编译,测试,打包,资源文件目录等等。
buildscript{}块是在有个项目构建前,为项目准备初始化相关配置的地方,配置好依赖我们就可以使用插件了

apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
apply plugin: 'com.growingio.android'

比如:com.android.application就是一个Android相关的插件,引入这个我们就可以使用Android{}代码块的先关配置,来帮助我们生成编译打包我们的Android工程;

简单自定义插件,通过下面简单的自定义插件,可以帮助大家了解Android插件的工作方式,内部帮助我们实现了很多的task。

在build文件中

class CustomPlugin extends Plugin{
    void app(){
        project.task("task1")<<{
          println("hello task1")
        }
    }
}
apply plugin :CustomPlugin

接下来可以分别简单介绍一下Java和Android插件:

Java插件

apply plugin : 'java'

上述眼熟的代码起始就是大家新建Java模块时候依赖的Java插件,他来帮助我们自动构建我们的Java项目

//添加依赖的时候告诉gradle去哪里找到这些依赖,设置依赖仓库
repositories{
    repositories {
        mavenCentral()
        maven { url "https://jitpack.io" }
        maven { url "https://dl.bintray.com/thelasterstar/maven/" }
        jcenter()
    }
}
//告诉gradle我们依赖什么,group,name,version以冒号隔开
dependencies {
    implementation 'com.alibaba:fastjson:1.1.46'
}

注:
api 跟2.x的compile类似
implementation 这个最常用,在当前模块内起作用,比如B模块依赖了Gson,A模块依赖B模

有了上述构建的基本的以来就有了,对于项目本身的文件资源默认是在sourceSet中配置

//这个就是Java工程的默认目录了,当然如果是需要自定义目录结构,就可以更高这里
sourceSets{
    main{
        java{
              srcDir 'src/java'
        }
        resources{
              srcDir 'src/resources'
        }
    }
}
//大家可以打印一下,都有哪些属性
task printSourceSet{
    sourceSets.all{
        println name
    }
}

当然这些都是常用的,要想编译运行我们的Java项目,还依赖于我们的tasks,默认的Java插件都帮我们实现完了,如果大家想看有哪些任务:./gradlew tasks查看所有的任务;
还有几个常见属性和常用的任务:

//编译Java源文件使用的Java版本
sourceCompatibility Javaversion
//编译生成类的Java版本
targetCompatibility Javaversion
//生成类库的目录
libsDir File

//打jar包
task publishJar(type:Jar)

artifacts{
    archives publishJar
}

//最有用的是上传任务,可以打完包配置直接上传maven,jcenter,详情可自行百度
uploadArchIves{

}

Android插件

这应该是我们平时使用和见的最多的

apply plugin: "com.android.application"

里面android{}就是我们配置Android gradle的总入口

android {
    compileSdkVersion xx
    buildToolsVersion  'xxx'

    defaultConfig{
          applicationId 'xxx'
          xxxx
          xxxx
          //defaultConfig中可以显示的配置singningConfigs,否则会根据buildTypes自动release或者debug
          signingConfig singningConfigs.debug
     }
     //签名配置信息
     singningConfigs{
        release{
          storeFile file('xxxxxx.keystore')
          storePassword 'xxx'
          keyAlias 'xxx'
          keyPassword  'xxx'
        }
        debug{
          storeFile file('xxxxxx.keystore')
          storePassword 'xxx'
          keyAlias 'xxx'
          keyPassword  'xxx'
        }
    }
    
    buildTypes{
      release{
          // 此处可以配置签名配置
          signingConfig singningConfigs.debug
         // 是否需要混淆
          minifyEnable   boolean
          //设置为.debug那么打出的包名就是applicationId.debug
          applicationIdSuffix  'xxx'
          
          mutiDexEnable  boolean

          proguardFiles file
          //清除未使用的资源
          shrinkResources boolean
          //Android为我们提供的整理apk的工具,更快的读写apk中的资源,降低内训的使用,建议开启
          zipaligin boolean
      }    
      debug{}
    }
  //配置渠道包,常用
  productFlavors{
      google{}
      huawei{}
   }

}

Android项目gradle的加载流程setting.gradle--->root.gradle--->setting中配置的project
所以我们在root.gradle做一些通用的配置

buildscript {//gradle脚本执行的依赖,root中添加作用其他subject/moudles
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.2.3'
    }
}

allprojects {//项目本身需要的依赖,对所有projects都起作用
    repositories {
        mavenCentral()
    }
}

设置一个gradle基类,这个在多module中非常实用,创建common.gradle在根目录下:

android {
    compileSdkVersion rootProject.ext.compileSdk
    buildToolsVersion rootProject.ext.buildTools

    defaultConfig {
        minSdkVersion rootProject.ext.minSdk
        targetSdkVersion rootProject.ext.targetSdk
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
    dexOptions {
        javaMaxHeapSize "4g"
    }
    packagingOptions {
        exclude '.readme'
        exclude 'LICENSE.txt'
        exclude 'META-INF/dependencies.txt'
    }
}

//在其他模块中直接apply from 就可以继承上面所有的属性,不需要重复写
apply from: rootProject.file('common.gradle')//其他目录就可以xxxx/xxx/common.gradle

android {
    defaultConfig {
        applicationId rootProject.ext.packageName
        versionCode 1
        versionName "1.0"
    }
}

//还有一种写在公共模块的方式在root.gradle

configure(project(':app').subprojects) {//相当于放在app下的build.gradle
  apply plugin :'xxx'
  android {
      defaultConfig{}
  }
  dependencies{}
}

抽取公共常量constant.gradle

ext {
    android_const = [
            compileSdk : 28,
            minSdk     : 16,
            targetSdk  : 26,
            support    : "27.1.1",
            buildTools : "28.0.3",
            packageName: "com.xiachufang"
    ]

    dep_const = [
            glide      : "3.6.0",
            okio       : "1.4.0",
            okhttp     : "2.4.0",
            fabric     : "2.4.0",
            logansquare: "1.1.0",
            dagger     : "2.0.1",
            butterknife: "7.0.1",
            retrofit   : "2.0.0-beta2",
            greendao   : "2.0.0"
    ]

    app_dep = [
            "appcompat-v7"      : 'com.android.support:appcompat-v7:' + android_const.support,
            "design"            : 'com.android.support:design:' + android_const.support,
            "okio"              : 'com.squareup.okio:okio:' + dep_const.okio,
            "okhttp"            : 'com.squareup.okhttp:okhttp:' + dep_const.okhttp,
            "glide"             : 'com.github.bumptech.glide:glide:' + dep_const.glide,
            "butterknife"       : 'com.jakewharton:butterknife:' + dep_const.butterknife,
            "rxjava"            : 'io.reactivex:rxjava:1.0.10',
            "rxandroid"         : 'io.reactivex:rxandroid:0.24.0',
            "retrofit"          : 'com.squareup.retrofit:retrofit:' + dep_const.retrofit,
            "converter-gson"    : 'com.squareup.retrofit:converter-gson:' + dep_const.retrofit,
            "adapter-rxjava"    : 'com.squareup.retrofit:adapter-rxjava:' + dep_const.retrofit
    ]
}

//可以在每个模块中apply from:
//也可以在root.gradle中apply,然后在其他模块中

apply plugin: 'java'

dependencies {
    compile rootProject.ext.app_dep.rxjava
}

说一个常用的属性BuildConfig,大家可能都使用过,来判断是都是Debug
,这个文件是不能更改的,是gradle帮我们生成的,那么我们想扩展先关字段怎么办?如果能扩展还是很方便的

//我们使用这个扩展过线上和debug的地址
productFlavors{
      google{
          buildConfigField 'String' 'WebUrl' 'www.google.com'
          //添加自定义资源
          resValue   'String' 'chanel_tips' 'google 欢迎你'
       }
      huawei{
           buildConfigField 'String' 'WebUrl' 'www.huawei.com'
       }
   }

其他扩展

 //Java编译版本编码格式等等
compileOptions{}
adbOptions{}
testOptions {}
dexOptions{
    //配置dex命令时分配最大堆内存
    javaMaxHeapSize '4g'
    // Sets the maximum number of DEX processes
    maxProcessCount 8
  }
packagingOptions {
        exclude '.readme'
        exclude 'LICENSE.txt
    }

defaultConfig{
    //国际化只打中文资源
    resConfigs 'zh'
}

多渠道构建

在gradle Android plugin中定义了一个Build variant的感念,翻译为构建变体。实际就是构建apk,实际也就是Build type+Product flavor=Build variant,Build type是构建类型,Product flavor就是多渠道配置,Build type也就是release,debug,自定义类型等,之前说过

productFlavors{
    google{
        applicationId ''//指定渠道包名
        manifestPlaceholders.put("UMENG_CHANNEL","google")//修改
AndroidManifest的值
        // app名称替换
        manifestPlaceholders = [app_name:"@string/app_name"]
        multiDexEnable//设置是否支持多dex
        proguardFiles//单独混淆
        signingConfig//单独的签名配置
        versionCode
        verisonName
    }
    huawei{}
}

//设置维度
flavorDimensions 'abi' ,'version'
productFlavors{
    free{
        dimensions 'version'
    }
    pay{
        dimensions 'version'
    }
    x86{
        dimensions 'abi'
    }
    arm{
        dimensions 'abi'
    }
}

//将会构建出x86free x86pay armfree armpay的包

常用的结合的例子
获取keystorePropertiesFile

// Creates a variable called keystorePropertiesFile, and initializes it to the
// keystore.properties file.
def keystorePropertiesFile = rootProject.file("keystore.properties")

// Initializes a new Properties() object called keystoreProperties.
def keystoreProperties = new Properties()

// Loads the keystore.properties file into the keystoreProperties object.
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))

android {
  signingConfigs {
    config {
      keyAlias keystoreProperties['keyAlias']
      keyPassword keystoreProperties['keyPassword']
      storeFile file(keystoreProperties['storeFile'])
      storePassword keystoreProperties['storePassword']
    }
  }
  ...
}
...

FileProvider骚操作

android {
  defaultConfig {
    def filesAuthorityValue = applicationId + ".files"
    manifestPlaceholders = [filesAuthority: filesAuthorityValue]
      buildConfigField("String",
                       "FILES_AUTHORITY",
                       "\"${filesAuthorityValue}\"")
  }
  }
}

//在AndroidManifest中使用

  ...
  
    ...
    
      ...
    
  

//在Java代码中
Uri contentUri = FileProvider.getUriForFile(getContext(),
  BuildConfig.FILES_AUTHORITY,
  myFile);

根据git tag来生成版本号

def getVersionCode = { ->
    try {
        def code = new ByteArrayOutputStream()
        exec {
            commandLine 'git', 'tag', '--list'
            standardOutput = code
        }
        return code.toString().split("\n").size()
    }
    catch (ignored) {
        return -1;
    }
}

def getVersionName = { ->
    try {
        def stdout = new ByteArrayOutputStream()
        exec {
            commandLine 'git', 'describe', '--tags', '--dirty'
            standardOutput = stdout
        }
        return stdout.toString().trim()
    }
    catch (ignored) {
        return null;
    }
}
android {
    defaultConfig {
        versionCode getVersionCode()
        versionName getVersionName()
    }
}

通过uploadArchives上传maven

apply plugin: 'maven'
apply plugin: 'signing' //使用signing plugin做数字签名

//定义GroupID和Version,
//ArtefactID会自动使用Project名
group = 'com.github.xxx'
version = '1.0.0'

repositories {
    mavenCentral();
}

artifacts {
    archives file('xxx.aar')
}
signing {
    sign configurations.archives
}

uploadArchives {
    repositories {
        mavenDeployer {
            //为Pom文件做数字签名
            beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }

            //指定项目部署到的中央库地址,UserName和Password就是Part 1中注册的账号。
            repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
                authentication(userName: Username, password: Password)
            }
            snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") {
                authentication(userName: ossrhUsername, password: ossrhPassword)
            }

            //构造项目的Pom文件,不要遗漏必填项
            pom.project {
                name project.name
                packaging 'aar'
                description 'xxxxxx'
                url 'https://github.com/xxx'

                scm {
                    url 'scm:[email protected]:xxx.git'
                    connection 'scm:[email protected]:xxx.git'
                    developerConnection '[email protected]:xxx.git'
                }

                licenses {
                    license {
                        name 'The Apache Software License, Version 2.0'
                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                        distribution 'xxxx'
                    }
                }

                developers {
                    developer {
                        id 'xxx'
                        name 'xxx'
                        email '[email protected]'
                    }
                }
            }
        }
    }
}

你可能感兴趣的:(Gradle必备知识点)