Gradle实战

Posted 安卓行动派

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gradle实战相关的知识,希望对你有一定的参考价值。


通过下面几个案例来演示下Gradle的强大之处,因为在一般的开发中我们只是简单的配置下build.gradle文件(添加依赖),并不能体现它的强大,也不知道它还有那些用途。


目录

案例一:单独定义一个Gradle文件,存放应用中的所有配置变量和依赖,以便统一管理

common.gradle
 1//用来存放应用中的所有配置变量,统一管理,而不再是每个moudle里都自己写一份,修改起来更加的方便
2ext {//扩展Project的变量
3
4  android = [compileSdkVersion   : 25,
5             buildToolsVersion   : '25.0.0',
6             applicationId       : 'com.gradle.demo',
7             minSdkVersion       : 16,
8             targetSdkVersion    : 23,
9             versionCode         : 1,
10             versionName         : '1.0.0',
11             multiDexEnabled     : true,
12             manifestPlaceholders: [UMENG_CHANNEL_VALUE: 'study']]
13
14  signConfigs = ['storeFile'    : 'sign.jks',
15                 'storePassword''123456',
16                 'keyAlias'     : 'alias',
17                 'keyPassword'  : '123456']
18
19  java = ['javaVersion': JavaVersion.VERSION_1_7]
20
21
22  dependence = ['libSupportV7'           : 'com.android.support:appcompat-v7:25.0.0',
23                'libSupportMultidex'     : 'com.android.support:multidex:1.0.1',
24                'libPullAlive'           : ':lib_pullalive',
25                'libCircleImageView'     : 'de.hdodenhof:circleimageview:2.1.0',
26                'libSystembarTint'       : 'com.readystatesoftware.systembartint:systembartint:1.0.3',
27                'libUmengAnalytics'      : 'com.umeng.analytics:analytics:latest.integration',
28                'libUniversalImageLoader''com.nostra13.universalimageloader:universal-image-loader:1.9.5',
29                'libOkhttp'              : 'com.squareup.okhttp3:okhttp:3.3.0',
30                'libAutoScrollViewPager' : 'cn.trinea.android.view.autoscrollviewpager:android-auto-scroll-view-pager:1.1.2',
31                'libSlidableActivity'    : 'com.r0adkll:slidableactivity:2.0.5',
32                'libAndfix'              : 'com.alipay.euler:andfix:0.5.0@aar',
33                'libLogger'              : 'com.orhanobut:logger:+',
34                'libTinker'              : "com.tencent.tinker:tinker-android-lib:1.7.7",
35                'libTinkerAndroid'       : "com.tencent.tinker:tinker-android-anno:1.7.7"]
36}
定义好文件后我们在根工程中引入该文件
 1//引入文件
2apply fromthis.file('common.gradle')
3buildscript {
4    //配置工程的仓库地址
5    repositories {
6        jcenter()
7    }
8    //配置工程的"插件"依赖地址
9    dependencies {
10        classpath "com.android.tools.build:gradle:2.2.2"
11    }
12}
引入文件后,我们在app工程build.gradle文件中使用
 1apply plugin: 'com.android.application'
2android {
3    compileSdkVersion rootProject.ext.android.compileSdkVersion
4    buildToolsVersion rootProject.ext.android.buildToolsVersion
5
6    defaultConfig {
7        applicationId rootProject.ext.android.applicationId
8        minSdkVersion rootProject.ext.android.minSdkVersion
9        targetSdkVersion rootProject.ext.android.targetSdkVersion
10        versionCode rootProject.ext.android.versionCode
11        versionName rootProject.ext.android.versionName
12        multiDexEnabled rootProject.ext.android.multiDexEnabled //突破应用方法数65535的一个限制
13    }
14
15    signingConfigs {
16        //签名打包
17        release {
18            storeFile file(rootProject.ext.signConfigs.storeFile)
19            storePassword rootProject.ext.signConfigs.storePassword
20            keyAlias rootProject.ext.signConfigs.keyAlias
21            keyPassword rootProject.ext.signConfigs.keyPassword
22        }
23    }
24
25    buildTypes 
{
26        release {
27            minifyEnabled true
28            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
29            signingConfig signingConfigs.release
30        }
31    }
32
33    lintOptions 
{
34        abortOnError false
35        lintConfig file("lint.xml")
36    }
37
38    //recommend
39    dexOptions 
{
40        jumboMode = true
41    }
42
43    compileOptions {
44        sourceCompatibility rootProject.ext.java.javaVersion
45        targetCompatibility rootProject.ext.java.javaVersion
46    }
47
48    sourceSets {
49        main {
50            jniLibs.srcDirs = ['libs'//修改so库存放位置
51        }
52    }
53}
54
55//为应用程序添加第三方库依赖
56dependencies {
57    compile fileTree(include: ['*.jar'], dir: 'libs')
58    compile rootProject.ext.dependence.libSupportV7
59    compile rootProject.ext.dependence.libSupportMultidex
60    compile project(rootProject.ext.dependence.libPullAlive)
61    compile rootProject.ext.dependence.libCircleImageView
62    compile rootProject.ext.dependence.libSystembarTint
63    //添加友盟统计
64    compile rootProject.ext.dependence.libUmengAnalytics
65    compile rootProject.ext.dependence.libUniversalImageLoader
66    compile rootProject.ext.dependence.libOkhttp
67    //okttp依赖
68    //compile 'com.github.chrisbanes:PhotoView:1.3.0'
69    compile(rootProject.ext.dependence.libAutoScrollViewPager
{
70        exclude module: 'support-v4' //排除依赖
71        exclude group'com.android.support' //排除该组下所有的依赖
72        transitive false //禁止传递依赖
73    }
74    compile rootProject.ext.dependence.libSlidableActivity
75    //滑动关闭Activity库
76    compile rootProject.ext.dependence.libAndfix
77    //阿里热修复andfix
78    compile rootProject.ext.dependence.libLogger
79    //日志库logger
80    //Tinker相关依赖
81    compile(rootProject.ext.dependence.libTinker) {
82        changing = true //每次都从服务端拉取
83    }
84    provided(rootProject.ext.dependence.libTinkerAndroid) { changing = true }
85}

这是配置后,我们的app工程的build.gradle文件更有语义性,版本变化后也便于管理,同时还可以复用。

案例二:自动维护版本发布文档

我们APP每次迭代需要记录APP的版本信息和对应的变化信息,以某一特定的格式记录,方便以后对比管理。下面先看一下版本发布文档的样式:(当然你也可以定义自己想要的样式)

 1<releases>
2  <release>
3    <versionCode>100</versionCode>
4    <versionName>1.0.0</versionName>
5    <versionInfo>App的第1个版本,上线了一些最基础核心的功能.</versionInfo>
6  </release>
7
8<release>
9  <versionCode>101</versionCode>
10  <versionName>1.0.1</versionName>
11  <versionInfo>App的第2个版本,修复了XXXXXBug,优化XXXX功能</versionInfo>
12</release>
13  .................. 省略 ...................
14<release>
15  <versionCode>110</versionCode>
16  <versionName>1.1.0</versionName>
17  <versionInfo>第10个版本。。。</versionInfo>
18</release>
19</releases>

有了这样的文档我们可以很方便的对比每个版本的差异,我们可以写一个脚本来自动生成这样的文档,而不需要手动维护。

releaseinfo.gradle
  1import groovy.xml.MarkupBuilder
 2
 3/**
 4 * 描述:版本发布文档自动维护脚本
 5 * 流程描述: 
 6 *           1、将版本相关信息解析出来
 7 *           2、将解析出的数据生成xml格式数据
 8 *           3、写入到已有的文档数据中
 9 **/

10ext {
11    versionName = rootProject.ext.android.versionName
12    versionCode = rootProject.ext.android.versionCode
13    versionInfo = 'App的第2个版本,上线了一些最基础核心的功能.'
14    destFile = file('releases.xml')//指定输出文件
15    if (destFile != null && !destFile.exists()) {
16        destFile.createNewFile()
17    }
18}
19//创建一个Task,并指定输入输出
20task writeTask {
21    inputs.property('versionCode'this.versionCode)
22    inputs.property('versionName'this.versionName)
23    inputs.property('versionInfo'this.versionInfo)
24    outputs.file this.destFile
25    doLast {
26        //将输入的内容写入到输出文件中去
27        def data = inputs.getProperties()
28        File file = outputs.getFiles().getSingleFile()
29        def versionMsg = new VersionMsg(data)
30        //将实体对象写入到xml文件中
31        def sw = new StringWriter()
32        def xmlBuilder = new MarkupBuilder(sw)
33        if (file.text != null && file.text.size() <= 0) {
34            //没有内容
35            xmlBuilder.releases {
36                release {
37                    versionCode(versionMsg.versionCode)
38                    versionName(versionMsg.versionName)
39                    versionInfo(versionMsg.versionInfo)
40                }
41            }
42            //直接写入
43            file.withWriter { writer -> writer.append(sw.toString())
44            }
45        } else {
46            //已有其它版本内容
47            xmlBuilder.release {
48                versionCode(versionMsg.versionCode)
49                versionName(versionMsg.versionName)
50                versionInfo(versionMsg.versionInfo)
51            }
52            //插入到最后一行前面
53            def lines = file.readLines()
54            def lengths = lines.size() - 1
55            file.withWriter { writer ->
56                lines.eachWithIndex { line, index ->
57                    if (index != lengths) {
58                        writer.append(line + '\r\n')
59                    } else if (index == lengths) {
60                        writer.append('\r\r\n' + sw.toString() + '\r\n')
61                        writer.append(lines.get(lengths))
62                    }
63                }
64            }
65        }
66    }
67}
68//信息实体类
69class VersionMsg {
70    String versionCode
71    String versionName
72    String versionInfo
73}
74//通过输入输出来指定Task的执行顺序
75task readTask {
76    //指定输入文件为上一个task的输出
77    inputs.file this.destFile
78    doLast {
79        //读取输入文件的内容并显示
80        def file = inputs.files.singleFile
81        println file.text
82    }
83}
84
85task taskZ {
86    dependsOn writeTask, readTask//通过依赖指定Task的执行顺序
87    doLast {
88        println '输入输出任务结束'
89    }
90}
91
92//将文档复制到指定的文件中
93task handleReleaseFile {
94    def srcFile = file('releases.xml')
95    def destDir = new File(this.buildDir, 'generated/release/')
96    doLast {
97        println '开始解析对应的xml文件...'
98        destDir.mkdir()
99        def releases = new XmlParser().parse(srcFile)
100        releases.release.each { releaseNode ->
101            //解析每个release结点的内容
102            def name = releaseNode.versionName.text()
103            def versionCode = releaseNode.versionCode.text()
104            def versionInfo = releaseNode.versionInfo.text()
105            //创建文件并写入结点数据
106            def destFile = new File(destDir, "release-${name}.txt")
107            destFile.withWriter { writer -> writer.write("${name} -> ${versionCode} -> ${versionInfo}")
108            }
109        }
110    }
111
112}

当版本改变的时候,我们只需要修改versionInfo 对应的内容,然后执行Task就可以生成对应的文档了,不过上述脚本还是需要我们手动修改versionInfo 的内容,我们可以把versionInfo 的内容从服务器获取(获取版本信息),这样的话就不需要我们手动修改了,groovy对网络访问这块没有特殊的扩展,所以我们用java的请求网络的方式实现即可。

案例三:多渠道打包

渠道包就是要在安装包中添加渠道信息,也就是channel,对应不同的渠道,例如:小米市场、360市场、应用宝市场等 。我们要在安装包中添加不同的标识,应用在请求网络的时候携带渠道信息,方便后台做运营统计(这就是添加渠道信息的用处)。

在AndroidMainfest.xml配置相应的渠道
1<meta-data android:value="UMENG_CHANNEL"         
2      android:name="${UMENG_CHANNEL_VALUE}"/>
  <!--动态更改渠道号-->
在build.gradle中配置渠道信息和自动替换脚本
 1//多渠道打包
2productFlavors {
3    xiaomi {}
4    huawei {}
5    yingyongbao {}
6    wandoujia {}
7}
8
9//自动替换清单文件中的渠道号
10productFlavors.all {
11    flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
12}
默认配置
1defaultConfig {
2    applicationId "com.gradle.demo"
3    minSdkVersion 11
4    targetSdkVersion 25
5    versionCode 1
6    versionName "1.0"
7    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
8    multiDexEnabled true //突破应用方法数65535的限制
9}

所有渠道默认使用这一配置,如果渠道有特殊需求,可以在productFlavors对应的渠道号中单独配置。

打包后自动修改APK的名字
 1//release包的命名格式为:产品名_版本号_渠道名.apk
2//debug包的命名格式为:产品名_版本号-debug.apk
3applicationVariants.all { variant ->
4    variant.outputs.each { output ->
5        def outputFile = output.outputFile
6        if (null != outputFile && outputFile.name.endsWith('.apk')) 
{
7            File outputDir = new File(outputFile.parent);
8            def baseName = PRODUCT_NAME + "${defaultConfig.versionName}" + "_" + variant.productFlavors[0].name
9            def newApkName
10            if (variant.buildType.name.equals('release')) 
{
11                newApkName = baseName + '.apk'
12            } else if (variant.buildType.name.equals('debug')) {
13                def debugName = PRODUCT_NAME + "${defaultConfig.versionName}"
14                newApkName = debugName + "-debug.apk"
15            }
16            output.outputFile = new File(outputDir, newApkName)
17        }
18    }
19}
完整build.gradle文件内容如下:
 1apply plugin: 'com.android.application'
2
3//产品名
4def PRODUCT_NAME = "BuglyDemo"
5
6android {
7    //添加签名文件配置
8    signingConfigs {
9        mysigns {
10            keyAlias 'alias'
11            keyPassword '123456'
12            storeFile file('sign.jks')
13            storePassword '123456'
14        }
15    }
16
17    compileSdkVersion 25
18    buildToolsVersion "25.0.2"
19
20    defaultConfig {
21        applicationId "com.gradle.demo"
22        minSdkVersion 11
23        targetSdkVersion 25
24        versionCode 1
25        versionName "1.0"
26        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
27        multiDexEnabled true //突破应用方法数65535的限制
28    }
29
30    //多渠道打包
31    productFlavors {
32        xiaomi {}
33        huawei {}
34        yingyongbao {}
35        wandoujia {}
36    }
37
38    //自动替换清单文件中的渠道号
39    productFlavors.all {
40        flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
41    }
42
43
44    buildTypes {
45        release {
46            minifyEnabled false //是否启用混淆
47            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
48            debuggable false
49            jniDebuggable false
50            signingConfig signingConfigs.mysigns
51            renderscriptDebuggable false
52            minifyEnabled false
53            pseudoLocalesEnabled false
54            zipAlignEnabled true
55        }
56    }
57
58//release包的命名格式为:产品名版本号渠道名.apk
59//debug包的命名格式为:产品名_版本号-debug.apk
60    applicationVariants.all { variant ->
61        variant.outputs.each { output ->
62            def outputFile = output.outputFile
63            if (null != outputFile && outputFile.name.endsWith('.apk')) {
64                File outputDir = new File(outputFile.parent);
65                def baseName = PRODUCT_NAME + "${defaultConfig.versionName}" + "_" + variant.productFlavors[0].name
66                def newApkName
67                if (variant.buildType.name.equals('release')) {
68                    newApkName = baseName + '.apk'
69                } else if (variant.buildType.name.equals('debug')) {
70                    def debugName = PRODUCT_NAME + "${defaultConfig.versionName}"
71                    newApkName = debugName + "-debug.apk"
72                }
73                output.outputFile = new File(outputDir, newApkName)
74            }
75        }
76    }
77}
78
79dependencies {
80    compile fileTree(include: ['*.jar'], dir: 'libs')
81    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
82        exclude group: 'com.android.support', module: 'support-annotations'
83    })
84    compile 'com.android.support:appcompat-v7:25.1.1'
85    testCompile 'junit:junit:4.12'
86    //添加友盟统计库依赖
87    compile 'com.umeng.analytics:analytics:latest.integration'
88}

打包工具:ApkChannelPackag

以上是关于Gradle实战的主要内容,如果未能解决你的问题,请参考以下文章

跟Maven一样流行的Gradle,构建Java项目实战

跟Maven一样流行的Gradle,构建Java项目实战

Gradle 构建:从入门到实战

solr分布式索引实战分片配置读取:工具类configUtil.java,读取配置代码片段,配置实例

Gradle实战:发布aar包到maven仓库

Gradle实战:Android多渠道打包方案汇总