Android Gradle这一篇就够

了解Gradle配置对于我们日常开发太重要了,我们要知道为什么要这样配置,知道配置的优缺点,不能盲目。

一,Gradle基础

1,Gradle环境配置

下载gradle解压后找到目录,例如:D:\android\gradle\gradle-4.4\bin。把地址配置到环境变量path中。

2, Android Gradle构建生命周期

  • Initialization:初始化阶段,会执行项目根目录下的settings.gradle文件,来分析哪些项目参与构建
  • Configuration:配置阶段,配置阶段会去加载所有参与构建的项目的build.gradle文件,
    会将每build.gradle文件实例化为一个Gradle的project对象
  • Execution:执行阶段,开始打包

3,Gradle Wrapper

  • Gradle的一层包装,便于开发中统一构建版本。当启动Gradle时,Wrapper会检查Gradle有没有被下载关联。 在 Wrapper中包含gradle-wrapper.jar和gradle-wrapper.properties。
  • gradlew和gradlew.bat分别是Linux和Windows下的可执行脚本。Gradle-wrapper.jar是具体业务逻辑包,gradlwe最终还是使用Java执行的这个jar。
  • gradle-wrapper.properties中的配置:distributionBase 下载的gradle压缩包解压后存储主路径;distributionPath 相对于distributionBase的解压后的gradle压缩包的路径; zipStoreBase 同distributionBase ,只不过是存放zip压缩包的; zipStorePath 同distributionPath ,只不过是存放zip压缩包的; distributionUrl Gradle发行版本压缩包的下载地址。

4,Groovy基础

Groovy是基于JVM虚拟机的一种动态语言, 它的语法和Java非常相似,是一种灵活的动态脚本语言。每个Gradle的build脚本都是一个Groovy脚本。Groovy是弱类型语言,语法更加灵活,不用像Java中的类以及数据类型需要强制转换,如下示例:

①,字符和字符串

task hello << {
      def name='张三‘
      println '单引号:${name}‘
      println "双引号:${name}"
	  
}
//输出结果
单引号:${name}
双引号:张三
  • 不管是单引号还是双引号都是String类型。
  • 单引号没有运算能力。

②,集合,List集合,Map集合

//List集合
task printList << {
      def numList=[1,2,3,4,5,6]
      println numList[1]//访问第二个元
      println numList[-1]//访问最后一个元素
      println numList[-2]//访问倒数第二个元素
      println numList[1..3]//访问第二个到第四个元素
}
//Map集合
task printMap << {
      def map=["width":1080,"height":800]
      println map["width"]//访问第二个元素
      map.each{
             println "Key:${it.key},Value:${it.value}"
      }
}

③,闭包
闭包在很多语言中都有这种概念,在Java8中Lambda表达式也是闭包的形式。另外在javascript,kotlin中都有闭包的概念。如下示例:

定义方法customEach,它只有一个参数用于接收闭包代码块。如果只有一个参数,就用it代替。

task printClosure << {
      //使用我们自定义的闭包
      customEach{
             println it
      }
}
 //自定义方法
def customEach(closure){
     for(int i in 1..10){
             closure(i)
     }
}

定义方法eachMap,为闭包传递两个参数,key ,value,多个参数就不能用it了,必须要显示声明出来。

task printClosure << {
      //使用我们自定义的闭包
      eachMap{k , v ->
                 println "${k} is ${v}"
      }
}
//自定义方法
def eachMap(closure){
      def map=["name":"张三","age":18]
      map.each{
             closure(it.key,it.value)
      }
}

④,自定义属性

//自定义 工具版本号
ext.build_tools_version = "25.0.2"

//引用版本号 
buildToolsVersion build_tools_version

⑤,脚本即代码,代码也是脚本

这段代码是生成时间,可以用于打包文件名

def buildTime(){
    def date=new Date()
    def formattedDate=date.format('yyyyMMdd')
    return formattedDate
}

4,Gradle插件

①,二进制插件

apply plugin:'java’

二进制插件就是实现了org.gradle.api.Plugin接口,需要有plugin id。

②,应用脚本插件

apply from:'version.gradle’ 

应用脚本插件就是执行了version.gradle一段脚本代码。

③,第三方插件

apply plugin:'com.android.application’ 

classpath ‘com.android.tools.build:gradle:1.5.0’

第三方发布的作为jar的二进制插件。

二,Android Gradle配置

1,默认配置

 compileSdkVersion 28
 buildToolsVersion '25.0.1'
 defaultConfig {
        applicationId "com.sort.demo"
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
 }
  • applicationld:用于指定生成app的包名。
  • minSdkVersion:最低支持的sdk版本。
  • targetSdkVersion:用于配置基于哪个Android Sdk开发。
  • compileSdkVersion:当前app编译版本。
  • versionCode:用于配置App内部版本号,通常用于版本升级。
  • versionName:用于配置App的版本名称,是让用户知道当前的版本。
  • resConfigs:过滤语言

2,signingConfigs 签名信息配置

signingConfigs{
        debug{
            storeFile file("test.keystore")
            storePassword "password"
            keyAlias "test"
            keyPassword "password"
        }
        release{
            storeFile file(“test.keystore”) //签名证书文件
            storePassword “password” //签名证书文件密码
            keyAlias “test” //签名证书秘钥别名
            keyPassword “password” //签名证书秘钥密码
        }
 }

signingConfigs 需要写在defaultConfig 和buildTypes 前面,不然会编译报错。默认情况下debug模式签名已经配置好了,在$HOME/.android/debug.keystore。

3,buildTypes 构建类型

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

buildTypes的属性:

  • applicationIdSuffix:应用id后缀
  • versionNameSuffix:版本名称后缀
  • minifyEnabled:是否混淆
  • proguardFiles:混淆文件
  • singningConfig:签名配置
  • manifestPlaceholders:清单占位符
  • shrinkResources:是否去除未使用的资源
  • zipAlignEnable:是否使用zipalign工具压缩
  • multiDexEnabled:是否拆分成多个Dex
  • multiDexKeepFile:指定文本文件编译进主Dex文件中
  • multiDexKeepProguard:指定混淆文件编译进主Dex文件中

4, sourceSets

每一个BuildType都会生成一个SourceSet,默认位置为src//。一个SourceSet包含源代码,资源文件等信息。针对不同的BuildType,可以单独为其指定Java源代码,res资源等。jniLibs目录下放置的是so库文件,直接放在目录下即可。如果so是在libs中,那就需要单独配置了,如下jniLibs.srcDirs=[‘libs’]。

 sourceSets {
            main {
                res.srcDirs =
                        [
                                'src/main/res/google',
                                'src/main/res/baidu'
                        ]
                jniLibs.srcDirs = ['libs']
            }
 }

开发中我们会引用到aar这样的架包, 如果aar中的libs存在架包,也需要单独配置,配置如下:so适配问题

repositories {
    flatDir {
        dirs 'libs'
    }
 }

使用abiFilters 过滤,其实这个可以不设置,这样编译时,就会将项目里所有依赖资源包里的so库都打到最终的apk里。但是有些平台,我们是不需要支持的,如果不删除的话,apk就臃肿了。如果那些so库是我们自己编译出来的,那可以直接在工程中删除对应so文件,但是如果是第三方提供的,就不好删除了,所以就需要使用abiFilters来过滤了。

defaultConfig {
        ndk {
            abiFilters 'armeabi','armeabi-v7a','arm64-v8a'
        }
}

5,packagingOptions
用于解决文件冲突问题

 packagingOptions {
        exclude ’META-INF/LICENSE.txt‘
        exclude ’META-INF/LICENSE‘
 }

用于解决项目中的jar冲突

compile ('com.wdullaer:materialdatetimepicker:3.2.2') {
    exclude group: 'com.android.support', module: 'support-v4'
    exclude group: 'com.android.support', module: 'design'
}

简写方式

compile() { dep ->
    ['support-v4', 'support-v13', 'design'].each{ module -> dep.exclude module: module }
}

6,批量修改生成的apk文件名

applicationVariants.all { variant ->
        variant.outputs.all { output -> // AS 3.0之后版本 each 改为 all
            def fileName = "${buildTime()}_release.apk"
            def outFile = output.outputFile
            if (outFile != null && outFile.name.endsWith('.apk')) {
                outputFileName = fileName// AS 3.0之后版本 output.outputFile 改为 outputFileName
            }
        }
}

7,动态生成版本信息
在开发中,一个工程中有多个module,每个module都有build.gradle配置,这些配置中的版本信息可以统一管理和维护。如下示例:

创建config.gradle内容如下

ext {
    android = [
            compileSdkVersion: 22,
            minSdkVersion    : 19,
            targetSdkVersion : 22,

            buildToolsVersion: '25.0.0',
            versionCode      : 1,
            versionName      : "1.0.1",

    ]
    dependencies = [
            supportV4: 'com.android.support:support-v4:22.2.1',
            design   : 'com.android.support:design:22.2.0'
    ]
}

在主工程中引入config.gradle

apply from: "config.gradle"

module中引用配置的内容

 android {
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    buildToolsVersion rootProject.ext.android.buildToolsVersion
    defaultConfig {
        applicationId "com.example.demo"
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
        versionCode rootProject.ext.android.versionCode
        versionName rootProject.ext.android.versionName
    }
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile rootProject.ext.dependencies.supportV4
    compile rootProject.ext.dependencies.design
}

8,从git的tag中获取版本号

git describe --abbrev=0 --tags //获取当前tag名

①,动态获取版本名称

 defaultConfig {
        versionCode appVersionCode
        versionName getAppVersionName()
 }

从git tag中获取应用的版本名称

def getAppVersionName() {
    def stdout = new ByteArrayOutputStream()
    exec {
        commandLine 'cmd','git', 'describe', '--abbrev=0', '--tags'
        standardOutput = stdout
    }
    return stdout.toString()
}

②,动态获取版本号

defaultConfig {
        versionCode getAppVersionCode()
        versionName getAppVersionName()
}

以git tag数量作为版本号

def getAppVersionCode(){
    def stdout=new ByteArrayOutputStream()
    exec {
        commandLine 'cmd','git','tag','--list'
        standardOutput = stdout
    }
    return split("\n").size()
}

9,隐藏签名文件信息

在团队开发中如果想隐藏签名信息,把正式包的时候不受影响。把信息存到主机环境变量中,既做到隐藏也能正常打包。

signingConfigs {
        def appStoreFile=System.getenv("STORE_FILE")
        def appStorePassword=System.getenv("STORE_PASSWORD")
        def appKeyAlias=System.getenv("KEY_ALIAS")
        def appKeyPassword=System.getenv("KEY_PASSWORD")
        //当不能从环境变量里获取到签名信息的时候,就使用项目中带debug签名
        if(!appStoreFile||!appStorePassword||!appKeyAlias||!appKeyPassword){
            appStoreFile="debug.keystore"
            appStorePassword="android"
            appKeyAlias="androiddebugkey"
            appKeyPassword="android"
        }
        release{
            storeFile (appStoreFile)
            storePassword appStorePassword
            keyAlias appKeyAlias
            keyPassword appKeyPassword
        }
}

10,动态配置AndroidManifest文件

在我们做渠道统计的时候一般会这样做,在打包的时候会把${UMENG_CHANNEL}替换成渠道名。


根据渠道名来表示渠道号,这样在渠道统计的时候就能统计不同的渠道。

 productFlavors{
        google{
            manifestPlaceholders.put("UMENG_CHANNEL","google")
        }
        baidu{
            manifestPlaceholders.put("UMENG_CHANNEL","baidu")
        }
 }

简写方式

productFlavors {
        google {
        }
        baidu {
        }
        productFlavors.all { flavor ->
             manifestPlaceholders.put("UMENG_CHANNEL", name)
        }    
}

11,自定义BuildConfig

比如:不同的渠道想实现打开不同的网页,可以这么做。

productFlavors {
        google {
            buildConfigField 'String','WEB_URL', ' http://www.google.com '
        }
        baidu {
            buildConfigField 'String','WEB_URL', ' http://www.baidu.com '
        }
}

比如:测试包用测试环境,正式包用正式环境。

buildTypes {
        debug{
            buildConfigField ' String ', ' WEB_URL ', ' http://www.ceshi.com '
        }
        release {
            buildConfigField 'String','WEB_URL', ' http://www.zhengshi.com '
        }
}

比如:测试包开启日志,正式包关闭日志

 buildTypes {
        debug{
            buildConfigField 'bool', 'isDebug', 'true'
        }
        release {
            buildConfigField 'bool', 'isDebug', 'false'
        }
 }

12,动态添加自定义的资源

res/values中的资源,不光可以在res/values中使用xml的方式定义,还可以在gradle中定义。这样不同的渠道引用的string是不同的。还可以使用id,bool,dimen,integer,color等类型定义value资源。在BuildType中也可使用。

productFlavors {
        google {
           resValue 'string','channel_tips','google渠道欢迎你'
        }
        baidu {
            resValue 'string','channel_tips','baidu渠道欢迎你'
        }
}

13,Java编译选项

compileOptions {
        encoding=‘utf-8’//指定文件编码格式
        sourceCompatibility=JavaVersion.VERSION_1_7 //源代码编译级别
        targetCompatibility=JavaVersion.VERSION_1_7//生成Java字节码的版本
}

14,DEX选项配置

在apk打包的时候被dx命令优化成Android虚拟机可执行的DEX文件。默认情况下被dx分配内存是一个G8,也就是1024MB。

dexOptions {
        javaMaxHeapSize ‘4g‘ //执行dx最大堆内存
        jumboMode true  //忽略方法数限制的检查,apk无法再低版本的设备上面安装
        threadCount 2    //dx使用的线程数
}

15, lintOptions
程序在编译的时候会检查lint,有任何错误提示会停止build,我们可以关闭这个开关。

lintOptions {
        abortOnError false //即使报错也不会停止打包
        checkReleaseBuilds false  //打包release版本的时候进行检测
}

你可能感兴趣的:(android知识点)