Android工程gradle详解

版本的统一管理

当咱们的工程中有许多module的时候,分开管理编译版本,minsdk将会是一件很麻烦的事,由于一个library的改动,可能会影响到其余module。这时咱们就须要对全部的版本进行统一的管理,管理的方式有两种:javascript

rootProject

咱们能够把一些须要用的字段都放在project的build.gradle(注意是project的不是module的)中:java

ext {   
 compileSdk = 21    
minSdk = 11    
targetSdk = 23    
support = "23.1.1"   
buildTools = "21.0.1"   
 buildstyle ="debug"
}复制代码

这样,在module的build.gradle中能够进行读取:android

defaultConfig {   
 applicationId "android.com.testgradle"    
minSdkVersion rootProject.ext.minSdk    
targetSdkVersion rootProject.ext.targetSdk    
versionCode 1   
 versionName "1.0"
}复制代码

gradle.properties

找到工程目录下的gradle.properties文件,若是没有也能够本身建立:git

ANDROID_COMPLILE_SDK_VERSION=21
ANDROID_BUILD_SDK_VERSION=21.0.1
ANDROID_TEXT=test复制代码

而后在各个module的build.gradle中能够引用:github

compileSdkVersion ANDROID_COMPLILE_SDK_VERSION as int
buildToolsVersion ANDROID_BUILD_SDK_VERSION复制代码

须要注意的是在gradle.properties中声明的格式都是string类型,若是如要转化成int类型,能够用as int 进行强制转化。app

#程序中对buildTypes的区分gradle

##buildTypes是对不一样build类型的处理
当你点击运行按钮的时候会根据build Variant进行对应的方式编译。
build Variant能够在这里进行选择:ui

Paste_Image.png

library的buildTypes

默认状况下被依赖工程会使用release模式,与上层依赖的app工程选择的模式无关
须要在build.gradle中进行设置:spa

defaultPublishConfig "debug"复制代码

新增buildTypes

你能够在buildTypes,根据须要新增一个类型,以下代码所示:插件

buildTypes {    
debug {       
 buildConfigField("String","TEXT","\"这个字符串来自debug模式\"")        
minifyEnabled false        
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'    
}    
stag {        
buildConfigField("String","TEXT","\"这个字符串来自test模式\"")        
minifyEnabled false        
signingConfig signingConfigs.debug       
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'   
 }   
 release {       
buildConfigField("String","TEXT","\"这个字符串来自release模式\"")        
minifyEnabled false        
signingConfig signingConfigs.debug        
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'    
}
}复制代码

不一样模式字符不一样

根据上面的代码,在不一样的buildTypes中,TEXT这个变量是不同的,并且根据代码能够看出,这个变量是一个String类型,那么咱们在程序文件中能够这样经过BuildConfig引用:

t2.setText(BuildConfig.TEXT);复制代码

Variant

gradle插件容许最终生成的包以多个维度进行组合
例如咱们能够设定一下几个维度:

productFlavors {    
red {       
 applicationId 'android.com.red'    
 versionCode 1
minSdkVersion 21
targetSdkVersion 22
}    
blue {        
applicationId 'android.com.blue'    
}    
yellow {       
 applicationId 'android.com.yellow'   
 }    
}复制代码

每一个维度中能够设置这个版本的最小sdk限制,以及targetsdkversion

假设咱们设置的维度有red blue yellow可是结合以前讲过的buildType(假设只有debug和release)
那么将会出现如下构建:
blueDebug和blueRelease
yellowDebug和yellowRelease
redDebug和redRelease
Gradle会为每个Variant建立一个任务
对应以下:
gradle assembleBlue 会生成debug和release两个版本
gradle assembleDebug 会生成blue yellow red三个版本
gradle assembleBlueDebug 会生成bluedebug一个版本
这里还有一个用处须要提一下:
Gradle在打包android应用以前会将全部的代码,资源文件,包括manifest进行结合,固然library也会提供额外的资源,这些也会进行合并。

字符串读取

若是咱们在建立一些设置时,须要动态的去更改内容,能够设置一个变量,而后从本地文件或者打包的命令行读取,咱们这里就拿上面提到过的BuildConfig作例子,但愿程序中引用的字符串是从本地读取的或从命令行读取的

从本地文件中读取

首先须要在最开始的地方设置一个变量aaa:

Paste_Image.png

而后在buildTypes的debug模式中修改对应的代码:

debug {    
buildConfigField("String","TEXT","\""+aaa+"\"")    
minifyEnabled false    
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}复制代码

而后在工程目录下建立一个本地文件test.properties,并添加内容:

Paste_Image.png

而后回到你刚才定义字符串的build.gradle中,添加:

if(rootProject.file('test.properties').exists()){ 
 java.util.Properties properties = new Properties()   
properties.load(rootProject.file('test.properties').newDataInputStream())   
aaa = properties.getProperty('debug.text')    
println("!!!!!"+aaa)
}else {
    aaa = "文件没找到"
}复制代码

便可读取到你本地文件的字符串

从命令行读取

若是是从命令行读入就更加简单,只须要将上面读取文件的代码改成:

aaa = new String(System.console().readLine("请输入字符串:"))复制代码

编译命令

首先想看一个工程包含了多少task,须要切到这个工程目录下:

gradle tasks复制代码

会列出全部的tasks:

Paste_Image.png

固然篇幅有限,这里不贴出全部的任务了,咱们只须要知道编译命令便可,如图能够看到,若是想编译debug版本,使用:

assembleDebug复制代码

其它同理便可。
若是使用build则回编译出全部的版本

冲突问题

Paste_Image.png

在执行打包的时候颇有可能会出现如上问题,这是因为依赖的jar冲突问题,咱们能够分析一下工程结构。
我如今的结构是:

Paste_Image.png

如今调整一下结构:

Paste_Image.png

把jar以module的形式提供就不会出现冲突。

编译流程

Paste_Image.png

Paste_Image.png

Task任务

执行顺序

例如咱们新建一个任务:

task umengtest{    
println("aaaaaaaaa")    
println("bbbbbb")
}复制代码

他的执行以下:

Paste_Image.png

它并非在执行任务的时候执行的,而是在执行任务以前就打印了。
这是因为gradle构建有三个阶段:
初始化阶段,配置阶段,执行阶段。
上面的例子实际是打印在了配置阶段,换句话说,你不执行这个任务,执行这个工程别的任务也会打印。
例如我再写一个任务:

task umengtestaa<<{    
println("ccc")    
println("dddd")
}复制代码

此次执行这个任务:

Paste_Image.png

你会发现也打印aaaa这说明这不是在真正的执行阶段执行的,为了保证任务的可控性,能够像上面umengtestaa那样写加一个<<符号
这样就能够保证了任务的可控性:

Paste_Image.png

或者使用:

task umengtest{    
doLast{        
println("aaaaaaaaa")        
println("bbbbbb")    
}
}复制代码

打包任务

有了上面的基础,下面就能够说一下打包的任务了,根据咱们以前的工程,咱们有一个app的module和四个library module,我须要打印出一个apk和四个jar,明确了任务,如今能够开始实施了。
打开工程的build.gradle

def sdk = [        
root : 'build/sdk/',
]task dabao( type:Zip) {    
dependsOn('cp_main')    
def name = 'umeng_test'  ;    
destinationDir = file('build')   
 archiveName = name + '.zip'    
from('build/sdk') {
        into( name )   
 }
}
task cp_main(type: Copy, dependsOn: ['app:assembleRelease'] ) {    
destinationDir = file( sdk.root )    
from('app/build/outputs/apk') {        
include('app-release.apk')        
rename ('app-release.apk','test.apk' )        
into('.')    
}    
from('mylibrary1/build/intermediates/bundles/release') {        
include('classes.jar')        
rename ('classes.jar','library1.jar' )        
into('.')    
}    
from('mylibrary2/build/intermediates/bundles/release') {        
include('classes.jar')        
rename ('classes.jar','library2.jar' )        
into('.')   
 }    
from('mylibrary3/build/intermediates/bundles/release') {        
include('classes.jar')        
rename ('classes.jar','library3.jar' )        
into('.')    
}    
from('mylibrary4/build/intermediates/bundles/release') {        
include('classes.jar')       
 rename ('classes.jar','library4.jar' )       
 into('.')    }
}复制代码

这时,咱们再去找一下工程的build文件夹下能够发现:

Paste_Image.png

打包不一样内容的module

打包不一样内容的module能够利用以前讲过的variant或这个buildtypes来控制,这里就不说了,不明白的,能够回头再去看一下结合上面的打包脚本没有什么难度,然而还有一种需求,不是某个变量或者包名的更改,而是两个版本中两个文件的不一样,咱们能够试一下修改variant的方式来实现,在app的build.gradle中:

productFlavors {

        pay {



        }
        free {

        }

    }复制代码

修改结构目录:

Paste_Image.png

这样编译出来就会有两个不一样类型的apk了

防止打包错误

在这里已经说完了全部与打包相关的东西了,在最后仍然加这一个标题是交给你们如何防止项目开发者打包出错,或者上传包出错后定位问题。
方法就是在打包的时候,生成一个记录文件,在这个包中记录打包时间,和当前git的版本号:

def releaseTime() {    
return new Date().format("yyyyMMddHHmmss", TimeZone.getTimeZone("GMT+8"))
}
def getGitVersion() {    
return 'git rev-parse --short HEAD'.execute().text.trim()
}
task writefile( ){
    File configFile = new File('config.xml');    
if (!configFile.exists()){        
configFile.createNewFile()   
 }  
  FileOutputStream out =new FileOutputStream(configFile)   
 def result ="编译时间:"+"${releaseTime()}\n"+"commitid:"+"${getGitVersion()}\n"    out.write(result.getBytes())    
out.close();
}
task cp_config(type: Copy, dependsOn: ['writefile'] ) {   
 destinationDir = file( sdk.root )    
duplicatesStrategy = 'exclude'   
 includeEmptyDirs = false    
from('.') { 
       include('config.xml')     
   into('.')   
 }
}复制代码

以上工程中所用到的全部代码,已经上传github
地址以下:
github.com/mymdeep/And…
若有对groovy语法不清楚的看官,请看一下个人上一篇文章,groovy基础知识:
www.jianshu.com/p/b58b254d8…

*更多的开发知识,能够关注个人公众号:

Paste_Image.png
相关文章
相关标签/搜索