ADeveloperH Blog

Meet a better self !

Android 周边技术分享


Gradle 知识点相关

1、gradle简介

Gradle 是以 Groovy 语言为基础, 基于DSL (领域特定语言) 语法的自动化构建工具,但是它增加了一些额外的特性,这使得Gradle更加的容易去阐释构建. 一个构建脚本能够包含任何Groovy语言的元素 ( Any language element except for statement labels ), 每个构建脚本都使用UTF-8编码.

2、环境配置

3、名词解释

3.1 什么是BuildTypes、Flavors、BuildVariants

BuildTypes:构建类型,AndroidStudio的Gradle组件默认提供给了“debug”“release”两个默认配置,此处用于配置是否需要混淆、是否可调试等

Flavors:产品渠道,默认不提供任何默认配置,在实际发布中,根据不同渠道,我们可能需要用不同的包名,服务器地址等

BuildVariants:每个buildtype和flavor组成一个buildvariant

4、命令相关

4.1 区别gradle命令和gradlew命令

  • 定义区别:Gradlew是包装器,自动下载包装器里定义好的gradle 版本,保证编译环境统一,gradle 是用本地的gradle版本。对于gradlew,其在Android studio的指定是在gradle/wrapper/gradle-wrapper.properties 中完成的;
  • 存放路径不同:对于系统gradle其存放在 ~/.gradle 下面(如果配置了环境变量就在环境变量的目录下)。对于gradlew其存放在 ~/.gradlew/wrapper/dists 下面。这个下面有着许多版本的gradle。这些就是gradlew下载下来的gradle版本;
  • 使用gradle版本不同:若你在命令行使用gradlew那么你使用的是存放在~/.gradlew/wrapper/dists路径下面的,通过在gradle/wrapper/gradle-wrapper.properties文件指定的gradle脚本。而你若使用gradle命令行,那么你使用的是~/.gradle下面的gradle脚本。通过命令行gradle -v 和gradlew -v 可以查看两者的版本具体信息差异。

4.2 gradle 文件介绍

  • 工程Project 中的 build.gradle : 工程控制Gradle编译配置
  • 模块module中的 build.gradle : 控制每个Module的编译过程
  • gradle.properties : gradle动态参数的配置文件
  • local.properties : 本地的配置,如:SDK位置
  • gradle-wrapper.properties :gradle本地代理,声明了指向目录和版本
  • distributionUrl : 指定gradle版本不存在时,就从Value的地址中去下载。很多时候,我们只要版本换成我们本地存在的gradle版本就可以了
  • settings.gradle : 配置Gradle中的Module管理

  • Project:build.gradle是用来配置项目的构建任务.默认的build.gradle内容如下:
    //项目构建文件,你可以到各子项目/模块添加常用的配置选项.
    buildscript	{
      //Android插件从这个仓库中下载
      repositories{
          jcenter()	//	依赖仓库源的别名,兼容maven的远程中央仓库
      }
      //依赖
      dependencies{
          //	android	gradle插件
          classpath	'com.android.tools.build:gradle:2.2.0-alpha1'
          //	提示:请不要在此处添加应用程序依赖;它们应该在单个Module(模块)build.gradle文件
          //这里添加的应该只是Project的依赖
      }
    }
    //此处配置Project中默认的仓库源,包括每个module的依赖
    //这样每个module就不用单独配置仓库了
    allprojects	{
      repositories{
      jcenter()
      }
    }
    //buildscript中的repositories是指定Android插件的仓库源.
    //allprojects中的repositories是指定整个Project中默认的仓库源.
    //任务类型是Delete,clean任务就是删除项目根目录下的build目录(build为输出目录)
    task clean(type:	Delete)	{
      delete	rootProject.buildDir
    }
    //	打包前执行clean任务
    
  • 模块构建配置文件:build.gradle.Module:build.gradle是用来配置模块的构建任务.默认的build.gradle文件内容如下:
    //插件:
    //这个module是一个android程序,使用com.android.application. 编译的结果是一个 apk
    //如果是android库,应该使用com.android.library. 编译结果为 aar
    //如果是java库,应该使用java. 编译结果为 jar
    apply plugin:	'com.android.application'
    android{
          //android程序构建需要配置的参数
          //编译使用的SDK版本
          compileSdkVersion	23
          //buildtool版本
          buildToolsVersion	"23.0.2"
          defaultConfig{//默认配置
          applicationId	"com.wirelessqa.basebuildsample" //apk包名
          //最小SDK版本
          minSdkVersion	16
          //目标SDK版本
          targetSdkVersion	23
          //version	code
          versionCode	1
          //应用程序的版本
          versionName	"1.0"
          //android单元测试test runner
          testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    //构建类型,	此处配置debug和release版本的一些参数,像混淆、签名配置.
    buildTypes{
      //release版本的配置
      release{
              //是否开启混淆
              minifyEnabled false
              //指定混淆文件及混淆规则配置文件的位置
              proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
          }
      }
    }
    //模块依赖
    dependencies{
      //编译依赖libs目录下所有jar包
      compile	fileTree(dir:'libs',include:['*.jar'])
      //编译依赖appcompat库
      compile	'com.android.support:appcompat-v7:23.4.0'
    }
    

    模块构建配置文件:build.gradle也可以在项目结构中配置

4.3 常用命令

  • 查看当前项目支持哪些Gradle任务:gradlew task
------------------------------------------------------------
All	tasks	runnable	from	root	project(所有从项目根目录可运行的任务)
------------------------------------------------------------
Android	tasks(Android	任务)
-------------
androidDependencies	-	显示项目的Android依赖
signingReport	-	显示每个变种版本的签名信息.
sourceSets	-	打印出所有在这个项目中定义的source集合.
Build	tasks(构建任务)
-----------
assemble	-	编译并打出应用程序所有变种版本的包.
assembleAndroidTest	-编译并打出所有测试应用的包.
assembleDebug	-	编译并打出Debug版本的包.
assembleRelease	-	编译并打出Release版本的包.
build	-	执行所有检查并编译打包
buildDependents	-	检查所有的依赖并编译打包.
buildNeeded	-	检查所有的依赖并编译打包.
clean	-	删除构建目录
查看和执行Gradle任务
259compileDebugAndroidTestSources
compileDebugSources
compileDebugUnitTestSources
compileReleaseSources
compileReleaseUnitTestSources
mockableAndroidJar	-	创建一个适用于单元测试的android.jar版本.
Build	Setup	tasks	(构建设置任务)
-----------------
init	-	被始化一个新的Gradle构建.
wrapper	-	生成	Gradle	wrapper文件
Help	tasks(帮助任务)
----------
buildEnvironment	-	显示项目根目录中声明的所有构建脚本的依赖
components	-	显示项目根目录产生的组件
dependencies	-	显示项目根目录中所有依赖的声明.
dependencyInsight	-	显示并洞察项目根目录中一个特殊的依赖关系.
help	-	显示帮助信息
model	-	显示项目根目录的配置模型.
projects	-	显示项目根目录中的子项目.
properties	-	显示项目根目录的属性.
tasks	-	显示从项目根目录可以运行的任务(
有些显示的任务可能属于子项目)
Install	tasks(安装任务)
-------------
installDebug	-	编译打包并安装Debug版本的包.
installDebugAndroidTest	-	编译打包并安装Debug版本的测试包到设备上
查看和执行Gradle任务
260uninstallAll	-	卸载所有版本的包.
uninstallDebug	-	卸载Debug版本的包.
uninstallDebugAndroidTest	-	从设备上卸载Debug版本的Android测试包
uninstallRelease	-	卸载Release版本的包.
Verification	tasks(验证任务)
------------------
check	-	运行所有检查.
connectedAndroidTest	-	在已连接的设备上安装所有flavors(渠道包)并运行instrumentation测试
connectedCheck	-	在当前已连接的设备上运行设备检测.
connectedDebugAndroidTest	-	在已连接的设备上安装并运行Debug版本的测试.
deviceAndroidTest	-	在所有提供的设备上安装并运行instrumentation测试.
deviceCheck	-	在所有提供的设备和测试服务器上运行设备检测.
lint	-	在所有变种版本上运行lint检测.
lintDebug	-	Debug版本上运行lint检测.
lintRelease	-	Release版本上运行lint检测.
test	-	在所有变种版本上运行单元测试.
testDebugUnitTest	-	Debug版本上运行单元测试.
testReleaseUnitTest	-	Release版本上运行单元测试.
Other	tasks	(其它任务)
-----------
jarDebugClasses
jarReleaseClasses
transformResourcesWithMergeJavaResForDebugUnitTest
transformResourcesWithMergeJavaResForReleaseUnitTest
想查看所有任务和更多详情,	请运行gradlew	tasks	--all
想查看一个任务的更多详情,	请运行gradlew	help	--task	<task>
BUILD	SUCCESSFUL
查看和执行Gradle任务
261Total	time:	6.717	secs
这个构建可以更快,	请考虑使用Gradle守护:	https://docs.gradle.org/2.10/userguide/gradle_daemo
n.html
  • 执行Gradle任务
      执行命令:
      gradle	+	任务名称或.\/gradlew	+	任务名称
      注意:GradleAndroid插件提供了四个顶级任务:打包(assemble)、检测(check)、构建
      (build)、清理(clean),当我们执行一个顶级任务的时候会同时执行与其依赖的任务.
      比如你执行:	.\/gradlew	assemble
      它会把你配置的所有构建类型(Build	Types)全部打出来,默认的构建类型是DebugRelea
      因此最起码它会执行两个任务:
      gradlew	assembleDebug
      gradlew	assembleRelease
      如果有其它的构建类型,任务名应该是:
      gradlew	assemble+构建类型名
      另外,执行构建(build)任务会执行检测(check)和打包(assemble)任务.
    
  • 常用命令
	
	查看任务帮助信息 gradlew h(elp)
	
	查看命令中带--的帮助信息 (注意命令中带-- gradlew -? -h --help  
	
	查看gradle版本 gradlew -v
	
	检查任务 gradlew check
	
	编译并打出Debug版本的包   gradlew assembleDebug
	
	编译并打出Release版本的包 gradlew assembleRelease
	
	导出指定渠道测试包,同时携带异常信息 gradlew assembleBaiduDebug --stacktrace
	
	执行检查并编译打包(执行了 checkassemble,打出所有ReleaseDebug的包. gradlew build
	
	清除所有中间编译结果,删除build目录(会把app下面的build目录删掉) gradlew clean
	
	编译打包并安装Debug版本的包 gradlew installDebug
	
	卸载Debug版本的包 gradlew uninstallDebug
	
	立即停止编译 gradlew --stop
	
	使用-info查看任务详情 gradlew uninstallDebug	-info
	
	排除任务使用- x命令行选项来排除一个任务 gradlew dist -x test
	
	查看项目依赖关系	gradlew :app:dependencies

4.4 Build Variant 相关

5 动态参数配置

在Gradle中动态配置资源参数我们可以根据各自的需求在不同的领域(如:buildType 的debug, defaultConfig …)下去动态替换或配置我们项目中所使用到的资源,如 log 开关, 针对不同渠道的对应内容字段,不同版本定义引入的不同值等等。对于动态资源在build.gradle中多个领域中的使用,会遵循一下顺序来进行覆盖:buildType > productFlavor > defaultConfig > Manifest中的配置 > 依赖的第三方库的配置 > 任意领域中的默认值(也就是没有设置值)

5.1 Manifest占位符

动态配置Manifest的参数:

ManifestApplication节点下
        //这里以友盟为例
        <!-- 友盟统计相关meta-data -->
        <meta-data
            android:name="UMENG_APPKEY"
            android:value="balabalabala" />
        <meta-data
            android:name="UMENG_CHANNEL"
            android:value="${UMENG_CHANNEL_VALUE}" />

		build.gradle中对参数进行动态配置
 		productFlavors {
        	baidu {
            	manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
        	}
		}

5.2 gradle.properties 定义参数

  • 系统属性:
      配置:systemProp.proName=123   
      使用:System.properties['proName']
    
  • 自定义属性:
      配置:有key ray.proName=123 ,无key :  proName = 123 
      使用:有key project.property('ray.proName') , key  proName
    

示例如下:


gradle.properties中的配置

#使用系统参数配置
systemProp.keyStore=ray.jks
#使用key/value键值对配置
ray.keyPassword=123456
#属性配置
mKeyAlias=ray

build.gradle中的使用

//签名打包
release {
    //签名文件所在路径
    storeFile file(System.properties['keyStore'])
    //签名密码
    storePassword "111111"
    //别名
    keyAlias mKeyAlias
    keyPassword project.property('ray.keyPassword')
}

5.3 BuildConfig文件中变量配置

在app/build/generated/source/buildConfig文件夹下面, 使用: buildConfigField “String” , “key” , “"value"”
配置buildConfig中的属性参数
String : 参数类型(int,boolean…), key : 属性的名字, value : 属性的值, \ 为转义字符


buildTypes {
        debug {
            debuggable true
            jniDebuggable true
            signingConfig signingConfigs.debug
            applicationIdSuffix ".beta"
            buildConfigField("int","BUILD_MODE","1")
        }
}

//编译后会在app/build/generated/source/buildConfig/debug/BuildConfig类中生成一个int类型变量BUILD_MODE值为1
//在写代码时可以直接使用:BuildConfig.BUILD_MODE来获取不同版本的BUILD_MODE值

5.4 resValue 动态修改工程资源

和buildConfig 类似,resValue(“string”,”key”,”value”),其中string 表示会在 app/build/generated/res/resValue/…/generated.xml中生成对应的String的key和Value,代码中可以直接getResources().getString(R.string.key);获取到value

注意: 因为Gradle编译的时候会将脚本配置和string.xml文件中配置进行merge,所以string.xml中已经存在的key在编译前要删除,否则会报错


//下面模拟在不同渠道下修改资源参数

productFlavors{
	baidu{
	  buildConfigField "String" , "productCode" , "\"baidu 1.0\""
	  resValue("string","productName","baidu")
	}
}

5.5 全局属性配置

在project : build.gradle 的buildscript中声明ext和自定义属性,然后在其他module中就可以直接使用这个属性了。如下:


//Project : build.gradle

buildscript {
    //自定义工程使用的属性
    ext {
        kotlin_version = '1.1.0'
        compile_version = 25
    }

    //声明依赖Android Gradle 插件版本
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

//Module : build.gradle 中使用

android {
	compileSdkVersion compile_version
	dependencies {
	 	compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
	}
}

5.6 建立自定义的gradle属性文件

新建自己的Gradle属性文件 text.gradle , 然后在Project中引入,最后就可以在其他的module中直接使用了,具体应用可以参考 印象笔记Github


//text.gradle 编写如下:
ext {
    kotlinVersion = "1.1.0"
    rxjavaLibVersion = "1.2.0"

    dependencies = [
            // Rx
            rxJava: "io.reactivex:rxjava:$rxjavaLibVersion"
    ]
}

//Project : build.gradle 中引入
//就是引入他的相对根目录路径
apply from: 'config/text.gradle'


//Module : build.gradle 中使用 rootProject.ext.XXXXX
dependencies {

 def dependencies=rootProject.ext.dependencies

    compile dependencies.rxJava

    compile "org.jetbrains.kotlin:kotlin-stdlib:$rootProject.ext.kotlinVersion"

}


5.7 Module : build.gradle中配置

控制每个Module的编译过程以及具体的参数配置

defaultConfig:基本配置信息


//默认配置
defaultConfig {
    //包名
    applicationId "com.rayhahah.gradledemo"
    //最低版本
    minSdkVersion 19
    //目标版本
    targetSdkVersion 25
    //版本代码
    versionCode getVersinCode()
    //版本
    versionName "1.0"
    //自动化测试
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    resValue "int","test","1"
}

signConfigs:签名配置信息


signingConfigs {

    //debug模式签名文件
    debug {}
    //签名打包
    release {
        //签名文件所在路径
        storeFile file("ray.jks")
        //签名密码
        storePassword "111111"
        //别名
        keyAlias "rayhahah"
        keyPassword "111111"
    }

    //自定义签名配置
    ray{
        //和上面的属性一致,根据个人需求实现不同配置
    }
}

buildTypes 编译类型:指定编译不同类型情况下的不同配置信息。这里是列举了部分属性和方法,全部的方法和属性请看官网文档

//构建配置
buildTypes {

	release {
		//是否启用资源优化
	    minifyEnabled
	    //启用舍弃无用资源,只有当开启混淆才能够启用
	    shrinkResources false
	    //指定混淆文件
	    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
	    //指定我们release包的输出文件名就是我们的渠道名字
	    applicationVariants.all { variant ->
	        variant.outputs.each { output ->
	            def outputFile = output.outputFile
	            if (outputFile != null && outputFile.name.endsWith(".apk")) {
	
	                def fileName = "${variant.productFlavors[0].name}" + ".apk"
	                output.outputFile = new File(outputFile.parent, fileName);
	            }
	        }
	    }
	}
	
	debug {
	
	}
	
	//继承
	yhahah.initWith(debug)
	
	//自定义buildType
	rayhahah {
	    //指定签名配置文件
	    signingConfig signingConfigs.debug
	    //包名增加后缀
	    applicationIdSuffix ".ray"

	}
}

sourceSets 配置资源逻辑组。通过指定资源,文件夹路径构建自己的工程目录

指定Android所需要文件夹所在具体路径

sourceSets {

   //这样的配置适用于将Eclipse中的项目结构迁移到AndroidStudio中
   main {
       //指定src资源目标目录
       java.srcDirs = ['src']
       //指定asset的目标目录
       assets.srcDirs = ['assets']
       //指定res的目标目录
       res.srcDirs = ['res']
       //指定依赖C文件的目标目录
       jni.srcDirs = ['jni']
        //指定依赖so文件的目标目录
       jniLibs.srcDirs = ['libs']
       //指定Manifest的目标文件路径
       manifest.srcFile 'AndroidManifest.xml'
   }
 }

layout分包管理,参考博文

1. res下新建文件夹 layouts(其实叫什么都无所谓)
2. 然后在 layouts 新建你要分的包 如: activityfragment 或按照业务模块来分
3. 在分包内新建Android resource directory -> layout 不要改名字
4. modulebuild gradle 如下配置
5. 然后将以前的layout文件拷贝到对应分包的layout下就可以使用了

PS:只有在Project目录才能看到,Android目录结构是看不到的

 sourceSets {
        main {
            res.srcDirs = [
                    'src/main/res/layouts/activity',
                    'src/main/res/layouts/fragment',
                    'src/main/res/layouts',
                    'src/main/res'

            ]
        }
    }

productFlavors:打包渠道配置信息


//多渠道打包配置,利用Manifest占位符动态参数配置

productFlavors {
    baidu {
        manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
    }

    wandoujia {
        manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
    }

    googleplayer {
        manifestPlaceholders = [UMENG_CHANNEL_VALUE: "googleplayer"]
    }

    //不想每一个都去配置渠道名称也可以用下面这个函数
    //这个函数可以将 Manifest中的占位符替换成 每个渠道的名字
    productFlavors.all {
        flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
    }
}

splits 官方说: 使用 splits 可以比使用 flavor 更加有效创建多 apk.目前而言, 仅支持 Density 和 ABIs 这两个分类.


splits {
        // 按屏幕尺寸
        density {
            enable true

            // 默认包含全部分辨率, 这里是剔除一些我们不要的
            exclude "ldpi", "mdpi", "xxxhdpi", "400dpi", "560dpi", "tvdpi"
        }

        // 按架构
        abi {
            enable true

            // 使用 `reset()` 后, 我们就相当于不包含任何架构,
            // 这种情况下我们就可以通过 `include` 指定想要使用的架构
            reset()

            include 'x86', 'armeabi-v7a'
            universalApk true       // 是否同时生成一个包含全部 Architecture 的包
        }
    }
}

compileOptions:编译配置项

//主要配置Java编译版本
compileOptions {
    sourceCompatibility org.gradle.api.JavaVersion.VERSION_1_8
    targetCompatibility org.gradle.api.JavaVersion.VERSION_1_8
}

lintOptions:lint配置项

//lint配置项
lintOptions {

  //启用出错停止gradle构建
  abortOnError false

  // true--检查所有问题点,包含其他默认关闭项
  checkAllWarnings true

  // 关闭指定问题检查
  disable 'TypographyFractions','TypographyQuotes'

  // 打开指定问题检查
  enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
  
  // 仅检查指定问题
  check 'NewApi', 'InlinedApi'

  // true--生成HTML报告(带问题解释,源码位置,等)
  htmlReport true
  
  // html报告可选路径(构建器默认是lint-results.html )
  htmlOutput file("lint-report.html")

  // 忽略指定问题的规则(同关闭检查)
  ignore 'TypographyQuotes'
}

dependencies 项目依赖

build.gradle中dependencies中的配置解析

//dependencies : 当前Android Module构建过程中所依赖的所有库
dependencies {

   //依赖指定目录下所有指定后缀的文件
   compile fileTree(dir: 'libs', include: ['*.jar'])

   //测试工具依赖
   testCompile 'junit:junit:4.12'

   //远程库的依赖 (当从远程库中下载一次过后,就会缓存到本地了)
   //默认远程库配置为 jcenter() 
   compile 'com.android.support:appcompat-v7:25.2.0'

   //依赖指定文件(这里依赖的是jar包)
   compile file('libs/test-1.0.0.jar')

   //依赖本地项目库
   compile project(':testLibrary')

   //格式:  groupId: com.squareup.retrofit2
   //       artifactId : retrofit
   //       version:  2.1.0
   // SNAPSHOT : 表示依赖 retrofit 及其依赖的所有项目,如果他所依赖的项目在本项目中重复出现依赖,则只依赖retrofit项目中的。
   // @aar :  表示只依赖retrofit,不依赖他所依赖的项目
   compile ('com.squareup.retrofit2:retrofit:2.1.0-SNAPSHOT@aar') {
        //强制刷新远程库,避免远程库刷新,本地未更新
        transitive = true        
        //exclude : 单独去除okhttp3的依赖
        exclude module  'com.squareup.okhttp3:okhttp:3.3.0' 
   }
}

so库依赖


src/main目录下创建jniLibs,然后将so文件拷贝进去就可以了
当然也可以通过sourceSet指定jniLib的目标目录来自定义管理依赖的so文件存放

本地Module依赖

build.gradledependencies 领域中添加 compile project(':testLibrary')
setting.gradle 中添加moduleinclude 如:include ':app',':testLibrary'

5.8 方法def定义

在gradle中你可以写方法供 配置信息动态调用


//自定义函数
def getVersinCode() {
	//......
}

android{
    defaultConfig{
        versionCode getVersinCode()
    }
}

7 Task 和 Plugin 相关

Task基本使用:


task testTask << {

    println 'testTask << print'

    // 表示在task最前面来执行的过程
    doFirst {
        println 'testTask do first'
	}
	
    // << 和 doLast表示对当前task追加执行过程,效果是一样的
    doLast{
        println 'testTask do last!'
	}
}

//task之间的依赖 dependsOn。当执行存在依赖的task时,会先执行他的父类也就是依赖目标
task testDependsOn(dependsOn:testTask){
    println 'testDependsOn default print'
}

//或者
testDependsOn.dependsOn testTask

//当执行testDependsOn时 打印顺序: 
testDependsOn  default print -> testTask do first -> testTask << print -> testTask do last!

//顺序总结为:
		//1.不加doLast和doFirst的最先执行
		//2.依赖task优先级高于自己的doFirst和doLast
		//3.同一个task中的doLast按从上向下顺序执行
		//4.同一个task中的doFirst按从下到上倒序执行
		//5.同一个task的doFirst优先级高于doLast

//显示声明类型为Copy,不声明默认为defaultTask
task testCopy(type : Copy){
    //将当前gradle文件从src目录拷贝到dst目录
    from "src"
    into "dst"
}

//每一个特定的Task类型还可以含有特定的Property,比如Copy的from和to等。自定义property
ext.testProperty = ""

task testExtProperty << {
    //直接使用自定义的property
    println testProperty
}

自定义Task:


//局部自定义Task:直接在build.gradle中自定义Task,但是也只能在当前module中引用
class TestCustomTask extends DefaultTask {
    //@Optional,表示在配置该Task时,message是可选的。
    @Optional
    String message = 'I am jjx'

    //@TaskAction表示该Task要执行的动作,即在调用该Task时,hello()方法将被执行
    @TaskAction
    def hello() {
        println "hello world $message"
    }
}

//hello使用了默认的message值
task hello(type: TestCustomTask)

//重新设置了message的值
task helloOne(type: TestCustomTask) {
    message = "I am a android developer"
}


全局自定义Task
如果需要自定义大量的Task,就要新建一个Gradle文件来统一管理
通过apply来引入使用
//这是插件
apply plugin: 'com.android.application'
//这里gradle-quality.gradle就是另外单独定义了task的gradle
apply from: '../build-config/gradle-quality.gradle'

自定义Plugin:自定义Plugin可以让我们在工程编译根据需求中自动去完成一些操作,下面就是一个编译后自动打印当前时间的Plugin,在build.gradle中直接定义


//与自定义Task十分类似,可以在build.gradle中自定义plugin
apply plugin: DateAndTimePlugin

dateAndTime {
    timeFormat = 'HH:mm:ss.SSS'
    dateFormat = 'MM/dd/yyyy'
}

class DateAndTimePlugin implements Plugin<Project> {
    //该接口定义了一个apply()方法,在该方法中,我们可以操作Project,
    //比如向其中加入Task,定义额外的Property等。
    void apply(Project project) {
        //加载Extension
        project.extensions.create("dateAndTime", DateAndTimePluginExtension)

        //使用Extension配置信息
        project.task('showTime') << {
            println "Current time is " + new Date().format(project.dateAndTime.timeFormat)
        }

        project.tasks.create('showDate') << {
            println "Current date is " + new Date().format(project.dateAndTime.dateFormat)
        }
    }
}
//每个Gradle的Project都维护了一个ExtenionContainer,
//我们可以通过project.extentions进行访问
//比如读取额外的Property和定义额外的Property等。
//向Project中定义了一个名为dateAndTime的extension
//并向其中加入了2个Property,分别为timeFormat和dateFormat
class DateAndTimePluginExtension {
    String timeFormat = "MM/dd/yyyyHH:mm:ss.SSS"
    String dateFormat = "yyyy-MM-dd"
}

工程中定义Plugin:其实本质上就是对上面的自定义Plugin结构化拆解

8 gradle 优化

  • 禁用Task达到提速

根据上面的耗时来对应禁用Task来达到提速的效果 在Project:build.gradle 中的 buildScript中动态配置编译时禁用即可, 代码:gradle.startParameter.excludedTaskNames.add(‘lint’) 就可以实现禁用了,具体需要继续禁用的可以根据项目输出的编译分析文件来作出添加和调整

  • AAPT aapt即Android Asset Packaging Tool,在SDK的build-tools目录下。该工具可以查看,创建, 更新ZIP格式的文档附件(zip, jar, apk)。也可将资源文件编译成二进制文件,尽管你可能没有直接使用过aapt工具,但是build scripts和IDE插件会使用这个工具打包apk文件构成一个Android 应用程序,在Debug模式下,我们需要优化AAPT来大量提速我们的编译(记得在release下改回来)

aaptOtions{
    cruncherEnabled = false
}

  • Gradle编译优化 提升Gradle本身编译速度
gradle.properties中配置

//开启守护线程支持
org.gradle.daemon=true

//开启并行编译
org.gradle.parallel=true
//按需编译
org.gradle.configureondemand=true

//手动配置Gradle编译时内存分配
# Default value: -Xmx10248m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

# 开启JNI编译支持过时API
android.useDeprecatedNdk=true


build.gradle中配置 

//增加编译内存到4g
dexOptions{
    incremental true    
    javaMaxHeapSize "4g"

	//dex增量编译
	jumboMode = true
}

9 常见问题

app:标识主 module
library:标识主 module 依赖的 module

9.1 ‘/gradlew’ 不是内部或外部命令,也不是可运行的程序或批处理文件。####

解决方案:查看项目目录下是否确实gradlew.bat文件,如果缺少把别的项目的copy一份过来运行命令gradlw -v下载对应版本后即可执行相关命令。

9.2 app包含某种构建类型或风味维度,但依赖的library中不包含

gradle报错:Unable to resolve dependency for ‘:TMessagesProj@fortest/compileClasspath’: Could not resolve project :commonlibrary.

解决方案:迁移到 Android Plugin for Gradle 3.0.0

9.3 library 中包含远程依赖,app 直接依赖 library 生成的 aar 导致的问题

使用 Android Studio 打包出来的 AAR ,不会将其依赖的三方库打包进去。 解决方案:
方案一:app 中也添加 library 依赖的第三方库。
方案二:需要上传到 maven 并使用 transitive = true,进行依赖传递

参考:
Android-少不了的 AAR 文件常识,最好知道的注意事项

解决AAR包引用第三方依赖无效

9.4 library 引入依赖报错:Invalid package reference in library

问题原因: library 中引入 implementation ‘org.bouncycastle:bcprov-jdk15on:1.57’,build 后 lint 报错 详细报错信息如下:

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':mylibrary:lint'.
> Lint found errors in the project; aborting build.
  
  Fix the issues identified by lint, or add the following to your build script to proceed with errors:
  ...
  android {
      lintOptions {
          abortOnError false
      }
  }
  ...
  
  Errors found:
  
  C:\Users\Administrator\.gradle\caches\modules-2\files-2.1\org.bouncycastle\bcprov-jdk15on\1.57\f66a135611d42c992e5745788c3f94eb06464537\bcprov-jdk15on-1.57.jar: Error: Invalid package reference in library; not included in Android: javax.naming.directory. Referenced from org.bouncycastle.jce.provider.X509LDAPCertStoreSpi. [InvalidPackage]
  C:\Users\Administrator\.gradle\caches\modules-2\files-2.1\org.bouncycastle\bcprov-jdk15on\1.57\f66a135611d42c992e5745788c3f94eb06464537\bcprov-jdk15on-1.57.jar: Error: Invalid package reference in library; not included in Android: javax.naming. Referenced from org.bouncycastle.jce.provider.X509LDAPCertStoreSpi. [InvalidPackage]
  


* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 6s

解决方案:
方案一: 直接忽略 lint 检查:
在 gradle 最上边添加如下 task:

tasks.whenTaskAdded { task ->
    if (task.name.equals("lint")) {
        task.enabled = false
    }
}

解决方案二:忽略 InvalidPackage 的 lint 检查:

 gradle  android{} 中添加
lintOptions {
        warning 'InvalidPackage'
    }

解决方案三:在 lint.xml 中配置具体要忽略的 lint 检查:

在要 build  library 目录下新建 lint.xml
<?xml version="1.0" encoding="utf-8" ?>
<lint>
    <issue id="InvalidPackage">
        <ignore path="**/bcprov-jdk15on*.jar"/>
    </issue>
</lint>

在要 build  library 目录下 gradle  android{} 中添加
lintOptions {
        lintConfig file("lint.xml")
    }

参考资料:

Firestore: Invalid package reference in library

Gradle dagger lint: ignore by package

Invalid package reference in library, not included in Android - abortOnError

Android Lint:基本使用与配置

gradle lint介绍以及通过禁用lint来解决lint相关的报错

Android自定义Lint实践

使用 Lint 改进您的代码(官网)

9.5 Could not find com.android.tools.build:gradle:2.3.3

参考资料:

Error:Could not find com.android.tools.build:gradle:2.3.3

10 项目应用

10.1 项目不同版本存在不同业务需求实现

参考资料: 官网Android Studio 创建源集 Android Studio Set of source 代码源集 Android Studio 中利用 Gradle 开发多渠道版本配置步骤

最近的文章

Gradle 中Groovy相关

1 Groovy简介2 Groovy 参考资料Groovy基本语法使用带例子:Gradle详解(一)——Groovy语法快速入门gradle中使用Groovy语法简介:Gradle详解(二)——GradleGroovy相关专栏(结合了gradle脚本比较全面):Groovy专栏列表页拥抱 Android Studio 之三:溯源,Groovy 与 Gradle 基础…

继续阅读
更早的文章

Gradle资料相关

1、参考资料gradle和groovyAndroid Gradle构建-理解DSL语言以及运行机制groovy语法(可使用在线编辑器,文档比较老)精通 GroovyAndroidStudio中Groovy Console:Tools—->Groovy Consolegradle-plugin中文使用文档:Gradle User Guide 中文版Gradle User Guide 中文版GitHub地址没有上个好:Gradle Android插件用户指南翻译Android Gradl...…

继续阅读