您的位置:首页 > 移动开发 > Android开发

Gradle系列第(三)篇---Android Studio与Gradle那些事儿

2016-12-14 16:42 519 查看
·


本来这篇要写Android性能优化的,个人时间比较少,每天加班到很晚,写博客的时间就很少了,但是Gradle系列的文章还没有写完,所以补一篇,在Gradle系列第(二)篇—Gradle编程主要对象主要写了Gradle中的几个对象(Project,Settings,Gradle,Task、Action),现在聊一聊Android Studio中的gradle常见的功能需求。如果你還沒有阅读过我的前两篇博客Gradle系列第(一)篇—Groovy语法初探Gradle系列2—Gradle编程主要对象,可以先看一下,有助于本文的理解,好啦,各位看官准备好瓜子花生,接下来一大篇文章哗啦啦的来了。不过不用担心,这篇博客仍然是面向基础。

读完这篇博客,你会了解到这些内容

- 1、Android的构建文件

- 2、全局参数配置

- 3、用脚本更改项目结构

- 4、多种apk的生成

- 5、签名的配置与使用

- 6、项目混淆(Proguard)

- 7、gradle多渠道打包

- 8、APK需求定制的案例

- 9、动态参数配置

- 10、gradle依赖管理

- 11、gradle.properties文件配置

- 12、jar文件输出

一、AS项目构建文件的简单解释

一个AS项目结构大概像下面这样子



如蓝色条所示,项目中总共包含了6个构建文件(不算Library中的gradle),我们先从宏观的方面了解一下,每个构建文件的作用是啥?

1、这个文件是app文件夹下这个Module的gradle配置文件,也可以算是整个项目最主要的gradle配置文件,比如自动打包debug,release,beta等环境,签名,多渠道打包,混淆等操作都可以在这里面写。每一个Module都需要有一个gradle配置文件。

2、我们主要看下gradle-wrapper.properties这个文件的内容

#Mon Dec 28 10:00:20 PST 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip


可以看到里面声明了gradle的目录与下载路径以及当前项目使用的gradle版本,这些默认的路径我们一般不会更改的,有時候导入一个新项目,gradle版本不对,可以在这里修改。

3、这个文件是整个项目的gradle基础(全局)配置文件,内容主要包含了两个方面:一个是声明仓库的源,这里可以看到是指明的jcenter(), 之前版本则是mavenCentral(), jcenter可以理解成是一个新的中央远程仓库,兼容maven中心仓库,而且性能更优。另一个是声明了android gradle plugin的版本。allprojects:中定义的属性会被应用到所有 moudle 中,但是为了保证每个项目的独立性,我们一般不会在这里面操作太多共有的东西。

buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.0'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
jcenter()
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}


所有通过gradle导入的jar包都是从http://bintray.com/bintray/jcenter这个中央仓库上扒下来的。如果你需要的jar包在这个网站上没有,那就无法通过gradle的方式来导入哦。

4、这个里面可以配置参数,然后在其他build.gradle中引用,后面会讲例子,如何动态配置参数。

5、这里主要指定了ndk和SDK的路径

ndk.dir=G\:\\Users\\wangjing\\AppData\\Local\\Android\\sdk\\ndk-bundle
sdk.dir=G\:\\Users\\wangjing\\AppData\\Local\\Android\\sdk


6、setting.gradle最关键的内容就是告诉Gradle这个multiprojects包含哪些子projects,当你的app只有一个模块的时候,你的setting.gradle将会是这样子的:

include ':app'


当你的app有多个模块的时候,你的setting.gradle将会是这样子的

include ':app', ':library',。。。。


setting.gradle文件将会在gradle初始化时期执行,关于初始化时期,可以查看上一篇博客,并且定义了哪一个模块将会被构建。举个例子,上述setting.gradle包含了app模块,setting.gradle是针对多模块操作的,所以单独的模块工程完全可以删除掉该文件。在这之后,Gradle会为我们创建一个Setting对象,每一个settings.gradle都会转换成一个Settings对象,并为其包含必要的方法,你不必知道Settings类的详细细节,但是你最好能够知道这个概念。另外可以在settings做一些初始化的工作,后面介绍。

读到这里做个总结

- build.gradle:控制每个Module的构建过程

- gradle.properties:设置gradle脚本中的参数

- local.properties:gradle的SDK和NDK环境变量配置

- gradle.properties:用于配置参数信息

- setting.gradle :配置gradle的多项目管理

二、实用技能精讲

1、全局参数配置

通常我们的项目都有很多的Module,像我现在公司的项目就有十几个,那么每个Module里面的gradle文件通常都有类似这样的配置。

android {
compileSdkVersion 24
buildToolsVersion "24.0.0"
defaultConfig {
applicationId "com.zhangwan.www.gradle"
minSdkVersion 15
targetSdkVersion 24
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}


这些配置对于每个Module来说,最好统一,我把它定义在项目根目录的gradle文件中,如下。

//全局配置
ext {
minSdkVersion =15
targetSdkVersion =24
compileSdkVersion =24
buildToolsVersion ="24.0.0"
versionCode =1
versionName="1.0"
}


定义好了,我们可以在各个Module的gradle文件文件中引用,如下:

android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion  rootProject.ext.buildToolsVersion

defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}


利用Gradle全局变量,对于多Module有很大的好处,方便统一,除了上面的列子,在举个例子。上面全部按照单个的属性配置的,对于相关的属性,可以将他们写到一个列表中,下面定义了一个dependencies_config的列表。

ext{
dependencies_config=[supportv7:"com.android.support:appcompat-v7:25.0.0"]
}


在Module中,这样引用

dependencies {
compile rootProject.ext.dependencies_config.supportv7
.....
}


2、项目结构更改

sourceSets 的作用是重新定义资源文件位置,比如

android {
compileSdkVersion 24
buildToolsVersion "24.0.0"

sourceSets{
main{
res.srcDirs=['src/main/res','src/main/res/layout/activity','src/main/res/layout/fragment']
}
}
}


在你Sync Now之后,会出现activity和fragment两个文件夹



最常见的是下面这块代码,当Eclipse项目转到Studio的时候,需要重新指定一些文件的位置。

sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
}


sourceSets的用法就是这样,可以重新指定文件目录,但是读到这,可能有些人心中有个问题,为什么sourceSets ,defaultConfig这样的东东要写在android的大括号中。换言之,android这个大括号里面还能写什么东西,我来列举一下。

android {
defaultConfig {
//默认配置项,defaultConfig就是程序的默认配置,注意,如果在   AndroidMainfest.xml里面定义了与这里相同的属性,会以这里的为主。
}

buildTypes {
// 编译配置,release或debug版本的内容
}

compileOptions {
// Java 的版本配置
}

sourceSets {
//源码设置(项目目录结构的设置)
}

packagingOptions {
//打包时的相关配置
}

lintOptions {
//编译的 lint 开关,程序在buid的时候,会执行lint检查,有任何的错误或者警告提示,都会终止构建,我们可以将其关掉。
//abortOnError false
}

productFlavors {
//产品发布的一些东西,比如渠道、包名等
flavor1 {
}

flavor2 {
}
}

signingConfigs {
//签名的配置
release {
}
}

testOptions{
//测试配置,TestOptions类型
}
aaptOptions{
//aapt配置,AaptOptions类型
}
lintOptions{
//lint配置,LintOptions类型
}
dexOptions{
//dex配置,DexOptions类型
}
compileOptions{
// 编译配置,CompileOptions类型
}
packagingOptions{
// PackagingOptions类型
}
jacoco{
//JacocoExtension类型。 用于设定 jacoco版本
}
splits{
//Splits类型。
}
}


在DSL文档中,以上每个类型都有它的详细配置选项,一般常见的设置就是上面啦,如果你觉得有的不太了解,看下面之后就了解了。

3、多种apk的生成

默认studio生成的buildTypes是像下面这样的,但是呢,我还想要其他的变种类型。

buildTypes {
release {
minifyEnabled false// 不混淆
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}


我们可以这样添加

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
r1{
applicationIdSuffix ".r1"
}
r2{
applicationIdSuffix ".r2"
}
r3{
applicationIdSuffix ".r3"
}
}


通过这样就可以得到多种变种app,执行assemble这个task,打出所有apk。



总共得到系统默认有的release和debug两个apk,额外还有r1,r2,r3三个不同的apk。

那么applicationIdSuffix是什么呢,逆向r1包看看。



系统通过包名来区分应用,这种方式无非就是在包名后面加上了一个后缀r1。

4、签名的配置与使用

上面打出的包都是没有指定签名的,我们要配置一个签名,首先需要生成签名文件。我生成的签名文件是1.jks

signingConfigs{
signR1{
storeFile file("build/1.jks");
storePassword "123456"
keyAlias "xxx"
keyPassword "123456"
}
signR2{
storeFile file("build/2.jks");
storePassword "123456"
keyAlias "xxx"
keyPassword "123456"
}
}


签名在signingConfigs中配置,signR1,signR2是签名的名字,在buildTypes中使用。

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
r1{
signingConfig signingConfigs.signR1
applicationIdSuffix ".r1"
}
r2{
signingConfig signingConfigs.signR2
applicationIdSuffix ".r2"
}

}


加上签名后打的包是这样,跟未加签名相比较,多了app-r1.apk,app-r2.apk。



5、项目混淆(Proguard)

面对众多的渠道,打包也有很多不同的需求。 比如 debug版,release版,dev版等等。 有时候不同的版本中使用到的不同的服务端api域名也不相同。 比如 debug_api.com,release_api.com,dev_api.com等等。不同的版本对应了不同的 api 域名,还可能对应不同的 icon 等。渠道首发包通常需要要求在欢迎页添加渠道的logo等。下面我们开始进行打包。首先进行混淆设置,混淆需要buildTypes中配置,在上面说过,默认生成的buildTypes是这样子的

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


其中,proguard-android.txt是在你的sdk\tools\proguard目录下。minifyEnabled:表示是否开启混淆,默认为false;proguardFiles:混淆配置文件,一般就采用项目中默认的proguard-rules.pro文件。在这个文件中写我们的混淆规则,比如:

-keepclasseswithmembernames class * {                                           # 保持 native 方法不被混淆
native <methods>;
}

-keepclassmembers enum * {                                                      # 保持枚举 enum 类不被混淆
public static **[] values();
public static ** valueOf(java.lang.String);
}

-keep class * implements android.os.Parcelable {                                # 保持 Parcelable 不被混淆
public static final android.os.Parcelable$Creator *;
}


有这些还不够,还需要在gradle中开启混淆

buildTypes {
release {
// 不显示 Log
buildConfigField "boolean", "LOG_DEBUG", "false"
shrinkResources true
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard.cfg'

}
debug {
// 显示 Log
buildConfigField "boolean", "LOG_DEBUG", "true"
signingConfig signingConfigs.debug
}
}


我们设置minifyEnabled true,就会在打包的时候进行代码混淆处理. 其中proguard-android.txt不用管,在sdk目录里面,我们主要是配置了proguard.cfg文件。可能大家直接在android studio创建项目不会有这个文件,而是proguard-rules.pro文件,其实一样的,我这里是因为项目是从eclipse迁移过来的,之前在eclipse上混淆是proguard.cfg文件.

6、gradle多渠道打包

1、第一步 在AndroidManifest.xml里配置PlaceHolder

<meta-data
android:name="MY_CHANNEL"
android:value="${MY_CHANNEL}" />


2、第二步 在build.gradle设置productFlavors

productFlavors{
xiaomi {
//用gradle修改AndroidManifest.xml中的meta-data元素值
manifestPlaceholders = [MY_CHANNEL: "xiaomi"]
}

_360 {
manifestPlaceholders = [MY_CHANNEL: "_360"]
}
baidu {
manifestPlaceholders = [MY_CHANNEL: "baidu"]
}

huawei{
manifestPlaceholders = [MY_CHANNEL: "huawei"]
}
}


或者批量修改

productFlavors{
xiaomi {}
_360 {}
baidu {}
huawei{}
}

productFlavors.all {
flavor -> flavor.manifestPlaceholders = [MY_CHANNEL: name]
}


最后,最好在defaultConfig中定义一个默认的渠道

defaultConfig{
manifestPlaceholders = [ MY_CHANNEL:"xiaomi" ]
}


到此配置完成,可以执行命令了。

- 3、去工程的根目录,也就是有gradlew文件的目录,打开命令行,输入命令:

./gradlew assemble

这时候你去app/build/outputs/apk中就能看到自动打好的渠道包了。

./gradlew assembleRelease

只打Release包

./gradlew assembleDebug

只打Debug包

./gradlew assemblebaidu

只打360的渠道包

./gradlew assemblebaiduRelease

不想敲命令行的,调起下面这个面板打包



7、APK需求定制

上面说了一下打包,在打包的時候,一些特殊化的操作,比如修改指定apk Logo,apk重命名,这些怎么搞?

渠道包重命名

android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
File outputDirectory = new File(outputFile.parent);
def fileName
if (variant.buildType.name == "release") {
fileName = "wangjing_${variant.productFlavors[0].name}.apk"
} else {
fileName = "wangjing_${variant.productFlavors[0].name}_beta.apk"
}
output.outputFile = new File(outputDirectory, fileName)
}
}
}


根据渠道修改APP名称

buildTypes {
debug {
// 显示Log
buildConfigField "boolean", "LOG_DEBUG", "true"
//重命名
resValue("string","app_name","DEBUG")
versionNameSuffix "-debug"
minifyEnabled false
zipAlignEnabled false
shrinkResources false
signingConfig signingConfigs.debug
}
release {
// 不显示Log
buildConfigField "boolean", "LOG_DEBUG", "false"
//重命名
resValue("string","app_name","DEBUG")
//混淆
minifyEnabled true
//加载默认混淆配置文件
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//签名
signingConfig signingConfigs.release

}
}


其中 resValue(“string”,”app_name”,”DEBUG”) 表示一个string 类型的变量app_name的值是DEBUG,做了上面的配置之后,需要将string.xml的app_name删掉,因为gradle编译的时候,会将脚本中的配置跟string.xml的合并。

8、动态参数配置

signingConfigs{
release{
storeFile file("build/mykey.jks")
storePassword   "123456"
keyAlias "123456"
keyPassword   "123456"
}
}


上面的这段配置,有个缺点,就是值直接写死了,我们可以动态配置参数。在哪里配置呢,一开始就说了,在gradle.properties中配置参数。如下:

systemPro.keyAliasPassword=123456
systemPro.keyAlias=123456
systemPro.keyStorePassword=123456
systemPro.keyStore=mykey.jks


配置好了,就可以“到处”使用了

signingConfigs{
release{
storeFile       System.properties["keyStore"]
storePassword   System.properties["keyStorePassword"]
keyAlias        System.properties["keyAlias"]
keyPassword     System.properties["keyAliasPassword"]
}
debug{
storeFile file("mykey.jks")
storePassword  "123456"
keyAlias"123456"
keyPassword  "123456"
}
}


9、gradle依赖管理

比如我们想依赖个support-v4包,直接一句话:

compile 'com.android.support:support-v4:23.1.1'


一个依赖需要定义三个元素:group,name和version。group意味着创建该library的组织名,通常这会是包名,name是该library的唯一标示。

上述的代码是基于groovy语法的,所以其完整的表述应该是这样的:

compile group: 'com.android.support:', name: 'support-v4', version:'23.1.1'


有些时候,你可能需要和sdk协调工作。为了能顺利编译你的代码,你需要添加SDK到你的编译环境。你不需要将sdk包含在你的APK中,因为它早已经存在于设备中,不需要在compile,我们总共有5个不同的配置:

compile是默认的那个,其含义是包含所有的依赖包,即在APK里,compile的依赖会存在。

apk的意思是apk中存在,但是不会加入编译中,这个貌似用的比较少。

provided的意思是提供编译支持,但是不会写入apk。

testCompileandroidTestCompile会添加额外的library支持针对测试。

通常项目的Module很多,依赖也非常多,为了方便管理,我们应该将这些依赖写到一个全局的地方,可以供其他module使用。这种思想也是第一小节所提的全局参数的配置。依赖管理可以参考:http://stormzhang.com/android/2016/03/13/gradle-config/

10、gradle.properties文件配置

gradle.properties常见配置比如有:

开启并行编译:加快gradle 的编译

org.gradle.parallel=true

开启编译守护进程:该进程在第一次启动后回一直存在,当你进行二次编译的时候,可以重用该进程。

org.gradle.daemon=true

加大可用编译内存:

org.gradle.jvmargs=-Xms256m -Xmx1024m

11、jar文件输出

android Studio常常有输出jar包的需求,只要下面这段代码即可:

task makeJar(type: Copy) {
delete 'build/libs/my.jar'
from('build/intermediates/bundles/release/')
into('build/libs/')
include('classes.jar')
rename ('classes.jar', 'my.jar')
}


OK,Gradle研究了一个多星期,这篇博客耗时两个晚上,终于结束,另外如果时间来的急,在写一篇Gradle系列4或5,因为感觉自己还没有讲清楚,侧重多个Module中gradle的使用与Gradle常用命令的使用,下篇博客继续性能优化系列的更新,每一次写博客都花费很多的时间和精力也是一次锻炼,跟他人分享自己的学习成果,最后附上参考资料,比我写的好。

Gradle for Android : http://www.codeceo.com/article/gradle-for-android-1.html

深入理解Gradle : http://blog.csdn.net/luohai859/article/details/48319129
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: