Android-Gradle阅读笔记

Gradle(2012) 是什么?

类似Ant(2000)和Maven(2004)概念的项目自动化建构工具。

Ant

Apache Ant是一个java库,主要用来构建Java程序。(C、C++、Java项目、Android项目)
主要功能:描述互相依赖, 提供了一个内置的编译,安装,测试和运行任务(target相当于gradle的task)。

Java项目的编译规则:
这里写图片描述

ADT
这里写图片描述
\ADT\adt-bundle-windows-x86_64-20140702\sdk\tools\ant
这里写图片描述
Build.xml
这里写图片描述

Maven

Maven与 Ant的区别(引用自http://hintcnuie.iteye.com/blog/122597

第一:ant脚本是可以直接运行在maven中的。maven和ant最大的差别就是在于maven的编译以及所有的脚本都有一个基础,就是POM(project object model)。这个模型定义了项目的方方面面,然后各式各样的脚本在这个模型上工作,而ant完全是自己定义,显然maven更胜一筹。
第二:Maven对所依赖的包有明确的定义,如使用哪个包,版本是多少,一目了然。而ant则通常是简单的inclde 所有的jar。导致的最终结果就是,你根本无法确定JBoss中的lib下的common-logging 是哪个版本的,唯一的方法就是打开 META-INF 目录下MANIFEST.MF。估计JBoss迟早会转向Maven的。
第三:Maven是基于中央仓库的编译,即把编译所需要的资源放在一个中央仓库里,如jar,tld,pom,等。当编译的时候,maven会自动在仓库中找到相应的包,如果本地仓库没有,则从设定好的远程仓库中下载到本地。这一切都是自动的,而ant需要自己定义了。这个好处导致的结果就是,用maven编译的项目在发布的时候只需要发布源码,小得很,而反之,ant的发布则要把所有的包一起发布,显然maven又胜了一筹。
第四:maven有大量的重用脚本可以利用,如生成网站,生成javadoc,sourcecode reference,等。而ant都需要自己去写。试试 maven site 的效果。
第五:maven目前不足的地方就是没有象ant那样成熟的GUI界面,不过mavengui正在努力中。目前使用maven最好的方法还是命令行,又快又方便。

Gradle

Gradle结合了前两者的优点,在此基础之上做了很多改进。
Gradle不用XML,它使用基于Groovy的专门的DSL, Gradle构建脚本变得比用Ant和Maven写的要简洁清晰,功能更强大,可以直接调用javase代码。

ANT、Gradle都可以单独安装,ANT通过命令行+build.xml运行,Gradle通过命令行+build.gradle运行

Gradle Wrapper

Wrapper,是Gradle的下载器,便于在团队开发过程中统一Gradle构建的版本,这样大家都可以使用统一的Gradle版本进行构建,避免因为版本不同带来不必要的问题。
这里写图片描述

Gradle日志

日志级别

级别 用于
ERROR 错误消息
QUIET 重要消息
WARNING 警告消息
LIFECYCLE 进度消息
INFO 信息消息
DEBUG 调试信息

Gradle命令行

操作 命令
帮助 gradle –? 或者 gradle –h
查看所有可执行的Tasks gradle tasks
查看每个Task描述信息 gradle help –task : xxmodule:xxxxtask
强制刷新依赖(版本库的version与snapshot版本变动后需要手动刷新) gradle –refresh-dependencies assemble

补充:SNAPSHOT
Maven中的SNAPSHOT版本(快照版本)
快照版本和正式版本的主要区别在于,本地获取这些依赖的机制有所不同。依赖正式版本,构建的会先在本次仓库中查找是否已经有了这个依赖库,如果没有的话才会去远程仓库中去拉取,不会拉取修改过已存在的同版本库。
.
开发的时候,需要SNAPSHOT版本(快照版本),在构建时去查看是否有同版本修改过的最新包,有则下载。在配置Maven的Repository可以配置查找的频率,频率共有四种,分别是always、daily、interval、never。
在Gradle,可以设置本地缓存的更新策略。
// check for updates every build
resolutionStrategy.cacheChangingModulesFor 0,’seconds’

Task

Groovy闭包≈Action

Groovy中的闭包(Closures)

学点Groovy来理解build.gradle代码

Gradle开发快速入门——DSL语法原理与常用API介绍

AndroidConfig 接口里声明 标签 android{}的配置

Groovy是基于JVM虚拟机的一种动态语言,语法与Java相似,完全兼容Java,增加了动态类型和灵活特性(支持闭包、支持DSL)。

闭包、FP函数式编程、lambda表达式

闭包是Groovy的一个非常重要的特性,可以说它是DSL的基础。

闭包(Closure)

维基百科:
1)闭包是引用了自由变量的函数。
2)由函数与其相关的引用环境组合而成的实体。

《JS中的闭包理解》:https://zhuanlan.zhihu.com/p/22486908?refer=study-fe

「函数」和「函数内部能访问到的变量」(也叫环境)的总和,就是一个闭包。
这里写图片描述

在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。运行时,一旦外部的 函数被执行,一个闭包就形成了,闭包中包含了内部函数的代码,以及所需外部函数中的变量的引用。其中所引用的变量称作上值(upvalue)。

闭包的作用

闭包常常用来「间接访问一个变量」。换句话说,「隐藏一个变量」。

Groovy中的闭包(Closures):

闭包在groovy中是一个处于代码上下文中的开放的,匿名代码块。它可以访问到其外部的变量或方法。
这里写图片描述

闭包也是对象

闭包在groovy中是groovy.lang.Closure类的实例,这使得闭包可以赋值给变量或字段。

Groovy中的委托策略(略)

lambda表达式

——java1.8支持了lambda表达式

lambda表达式与闭包的关系:
lambda表达式大量用到了闭包和函数回调
lambda表达式说到底只是一个函数表达式的表现形式而已
准确的说lambda表达式与闭包没有直接的联系
如果支持函数表达式而不支持闭包,相当于买椟还珠

函数编程

函数风格的编程特点:
1.第一等公民是函数
2.带有闭包的Lambdas/Anonymous函数
3.不变性,大部分无态处理,没有状态和变量
4.高并发
5.无副作用的调用
6.通过tail call实现递归的性能优化。
7.模式匹配(Haskell, Erlang)
8.懒赋值(Miranda, Haskell)
9.Homoiconicity(类似LISP)

SQL是非常类似FP,它能渗透到业务中,它使用一致的数据结构(数据表结构Schema),一些基本函数能组合成很多查询语句,它是declarative声明式的, 也就是说,写出的SQL是告诉数据库我需要什么,数据库就为你返回,而不必指定数据库如何具体去查询。

Haskell 是纯函数式编程语言,研究函数式编程必修之路

Gradle groovy解读

Gradle Android 配置集锦

android {

//    SourceSet——源代码集合——源集
    sourceSets{
        release{
            // java 指定代码文件
            // res 指定资源文件
//            java.srcDirs = xxxx
//            res.srcDirs = yyyy
        }
        debug{

        }
    }

    compileSdkVersion 23 // 配置SDK的API Level,对应的是Android 6.0 SDK
    buildToolsVersion '23.0.3' //Android构建工具的版本(是一个工具包,包括appt、dex等工具)
    // defaultConfig是默认配置,它是一个ProductFlavor。ProductFlavor允许我们根据不同的情况同时生成多个不同的APK包。
    defaultConfig{
//        applicationId "com.tongcheng.android"  包名
//        minSdkVersion rootProject.ext.minSdkVersion  App最低支持的Android操作系统版本
//        targetSdkVersion rootProject.ext.targetSdkVersion
//        versionCode Integer.valueOf(appVersionCode)  App内部版本号,用于版本的升级AA.BB.CC(BB为版本迭代,CC为bug修复版本)
//        versionName appVersionName  App版本名称
//        multiDexEnabled true  配置BuildType是否启用自动拆分多个Dex的功能。解决65535问题
        // 如果启动期间需要的任何类未在主 DEX 文件中提供,那么您的应用将崩溃并出现错误 java.lang.NoClassDefFoundError。
        // 如果类在 multiDexKeepFile 或 multiDexKeepProguard 文件中匹配,则该类会添加至主 DEX 文件。
//        multiDexKeepProguard file("keep_in_main_dex.txt")
//        resConfigs "zh-rCN" 只有中文资源
//        ndk{
//            abiFilters "armeabi"
//        }
//        signingConfig  配置签名信息,对生成的App签名
//        proguardFile 配置App ProGuard混淆所使用的ProGuard配置文件。

    }

    // buildTypes是一个NamedDomainObjectContainer类型,是一个域对象(与SourceSet一样)。
    buildTypes{
        release{
            // 配置App ProGuard混淆所使用的ProGuard配置文件。
//            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro', 'proguard-im.pro', 'rn-proguard-rules.pro'
//            proguardFiles:同事配置多个混淆文件
//            debuggable false 是否生成一个可供调试的apk
//            jniDebuggable false 类上
//            minifyEnabled true 是否启用Proguard混淆
//            Android SDK提供了两个Proguard配置文件,proguard-android.txt和proguard-android-optimize.txt,一个是没有优化的,另一个是优化的。

//            构建时,打包前,会检测所有资源,如果没有被引用,这些资源就不会被打包到apk包中,不管自己的还是第三方的都会被处理
//            shrinkResources true 是否自动清理未使用的资源,默认为false
//            zipAlignEnabled true  zipalign优化:是一个整理优化APK文件的工具,客提高系统和应用的运行效率,更快地读写apk中的资源,降低内存的使用。

//            修改生成的apk文件名
//            applicationVariants.all { variant ->
//                variant.outputs.each { output ->
//                    def outputFile = output.outputFile
//                    if (outputFile != null && outputFile.name.endsWith('.apk')) {
//                        // 输出apk名称为AppName_v1.0_2015-01-15_wandoujia.apk
//                        def apkFile = "AppName_v${defaultConfig.versionName}_${buildTime()}" +
//                                "_${variant.productFlavors[0].name}.apk"
//                        output.outputFile = new File(outputFile.parent, apkFile)
//                    }
//                }
//            }
        }
        debug{
//            Debug模式的签名使用的就是SDK自动生成的debug证书(.andriod/debug.keystore)

        }
    }

//    Android Gradle任务
//    assemble、check、build、connectedCheck、deviceCheck、lint、install、uninstall、gigningReport、androidDependencies等任

    signingConfigs {
        key_config {
//            keyAlias '17ukeystore'
//            keyPassword '17ukeystore'
//            storeFile file('/Users/android/share_root/etongAndroid.keystore')
//            storePassword '17ukeystore'
//            v2SigningEnabled false
        }
        debug {
            storeFile file('debug.keystore')
        }
    }

    flavorDimensions 'channel','pay'
    productFlavors{
        google{
//            动态配置AndroidManifest文件
//            在构建过程中,动态修改AndroidManifest文件中的内容(友盟统计等)
//            可以使用manifestPlaceholder、Manifest占位符
            manifestPlaceholders.put("UMENG_CHANNEL","google")
//            buildConfigField 'String','WEB_URL','"http://www.google.com"'// 自定义BuildConfig属性
//            resValue 'string','channel_tips','google渠道欢迎你' 动态添加自定义的资源

            dimension 'channel'

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

            dimension 'channel'
        }

        free{
            dimension 'pay'
        }

        paid{
            dimension 'pay'
        }
        // 构建产物variant = ProductFlavor x BuildType
        // ProductFlavor = 以dimension为分组的乘积
        // variant = {  FreeGoogleRelease FreeGoogleDebug FreeBaiduRelease FreeBaiduDebug PaidGoogleRlease PaidGoogleDebug PaidBaiduRlease PaidBaiduDebug }
    }

    dexOptions {
//        jumboMode true 解决65535问题
//        javaMaxHeapSize "3072M" 配置最大堆内存
//        preDexLibraries属性,是否预执行dex Libraries库工程,开启后会大大提高增量构建的速度,不过可能会影响clean构建的速度,默认为true,如需要使用dx的—multi-dex选项生成多个dex,导致和库工程有冲突的时候,需要将该选项设置为false
//        threadCount 2  用来配置Gradle运行dx命令时使用的线程数量,适当的线程数量可以提高dx效率
//        incremental 配置是否启用dx的增量模式(或许有时候不工作)

    }

}


def buildTime() {
    def date = new Date()
    def formattedDate = date.format('yyyy-MM-dd', TimeZone.getTimeZone("UTC"))
    return formattedDate
}

使用Gradle配置进行模块化开发

以下所有内容为笔记——均可忽略

Gradle基础:

xxx 作用
Settings文件 主要是为了配置子工程。
Build文件(每个Project都会有一个Build文件) 配置版本,接入插件,依赖库,依赖的Maven中心库
Task 是一个操作,一个原子性的操作,比如打个jar包,复制一份文件,编译一次Java代码,上传一个jar到Maven中心库等,和Ant里的Target,Maven中的goal是一样的。(所谓task其实是project一个属性Project.hasProperty(‘xxxTask’) 为 true)
任务依赖 dependsOn: xxxxTask

自定义属性
Project和Task以及SourceSet都可以添加额外的自定义属性,通过应用所属对应的ext属性即可实现。
添加之后可以通过ext属性对自定义属性读取和设置。

ext.age = 19
ext{
    phone = 123423
    address = ‘xxx’
}

自定义属性作用域广,可以跨Project,跨Task访问这些自定义属性。

脚本即代码,代码也是脚本
Gradle文件中写脚本,但是写的都是代码,可以即刻使用Groovy、Java以及Gradle的任何语法和API。可以定义Class、内部类、导包、定义方法、常量、接口、枚举等。

Gradle任务

多种方法访问任务
一:

task exTask1
exTask1.doLast{
    println ‘exTask1.doLast’
}

二:

task exTask2
tasks[‘exTask2’].doLast{
    println ‘exTask2.doLast’
}

tasks[‘exTask2’] 其实就是调用 tasks.getByName(‘exTask2’) 源码是调用 findByName(String name) 实现的
也可以通过路径进行访问:
findByPath(‘:example:exTask2’)
getByPath(‘:example:exTask2’)
get如果找不到会抛出UnknownTaskException异常,而find在找不到该任务的时候会返回null

任务的分组和描述

在创建任务的时候,尽量进行分组和描述,便于对任务进行归类整理,也便于阅读:
def Task myTask = task exTask3
myTask.group = BasePlugin.BUILD_GROUP
myTask.description = ‘这是一个构建的引导任务’

可以通过gradle tasks查看任务的分类描述

<<操作符
“<<” 操作符在Gradle的Task上是doLast方法的短标记形式,也就是说”<<”可以代替doLast

任务的执行分析
当我们执行Task的时候,其实就是执行其拥有的actions列表,这个列表保存在Task对象实力中的actions成员变量中,其类型是一个List
doFirst其实是add在第一个位置,doLast添加在最后一个位置

任务顺序
shouldRunAfter和mustRunAfter可以控制任务应该或者一定在某个任务之后执行。
例如:先执行单元测试,才能进行打包

任务的启用和禁用
Task中有个enabled属性,用于启用和禁用任务,默认是true,表启用;设置为false则禁止改任务执行,输出会提示该任务被跳过。

任务的onlyif断言
断言就是一个条件表达式。Task有个onlyIf方法,接受闭包作为参数,如果返回true则该任务执行,否则跳过。
用途:比如控制哪些情况下打什么包,什么时候执行单元测试,什么情况下执行单元测试的时候不执行网络测试等。
Example:
这里写图片描述

任务规则

当执行、依赖一个不存在的任务时,Gradle会执行失败。可以使用规则对其进行改进,当执行、依赖不存在的任务时,不会执行失败,而是打印提示信息,提示改任务不存在:
这里写图片描述

Gradle插件

Gradle本身提供了基本的概念和整体核心的框架,其他用于描述真实使用场景逻辑的都以插件扩展的方式来实现,比如构建Java应用,就是通过Java插件来实现的。

插件的作用:个人理解就是编译过程中,能用程序代码做的任何事情(代码生成、库拉取、编译修改、代码目录指定、扩展属性等等)

应用插件

二进制插件:就是实现了org.gralde.api.Plugin接口的插件
apply plugin:’java’
上面语句把Java插件应用到我们的项目中了,其中’java’是Java插件的plugin id,他是唯一的。

也可以:
apply plugin:org.gradle.api.plugins.JavaPlugin
也可以:
apply plugin:JavaPlugin
二进制插件一半都是被打爆在一个jar里独立发布的。我们自定义的插件,发布的时候制定plugin id最好是一个全限定名称,就像包名,避免重复。

脚本插件
build.gradle

apply from:’version.gradle’
其实就是把脚本加载进来,和二进制插件不同的使用from关键字,后面紧跟一个脚本文件,可以是本地的,也可以是网络的,如果是网络上的话要使用HTTP UR。

应用第三方发布的插件
第三方发布的jar的二进制插件,应用的时候,必须要现在buildscript{}里面配置classpath才能使用。
比如Andriod Gradle插件,就属于Andrid发布的第三方插件,如果要使用就要进行配置:
这里写图片描述

buildScript{}块是一个在构建项目前,为项目进行前期准备和初始化相关配置以来的地方,配置好所需的以来,就可以应用插件了:
apply plugin:’com.android.application’

使用plugins DSL应用插件
Gradle2.1以上版本才可以用,如果该插件已经被托管在https://plugins.gradle.org/网站上,我们就不用在buidscript里配置classpath以来了,直接使用plugins就可以应用插件:
这里写图片描述

更多好用的插件:

https://plugins.gradle.org/
Github

自定义插件

脚本插件

这里写图片描述

Groovy工程插件(二进制插件)

这里写图片描述

Gradle插件基础

sourceSets源码目录

第三方依赖:
仓库配置:
Repositories{
mavenCentral()
}
库依赖
项目依赖
Jar依赖

如何构建Java项目

build任务:编译源码,处理资源文件等等
clean任务:删除build目录以及其他构建生成的文件。
assemble任务:不会执行单元测试,只会编译和打包
check任务:只会执行单元测试,有时候还会做一些质量检查

源码集合SourceSet概念
SourceSet——源代码集合——源集
有了源集,我们能针对不同的业务和应用进行源代码分组

发布构建
将代码发布到库中
这里写图片描述

Andriod Gradle插件

Android其实就是Gradle的一个第三方插件,由Google的Android团队开发的。

Andriod Gradle插件分类
App插件id:com.android.applicaton
Library插件id:com.android.library
Test插件id: com.android.test

Android Gradle插件
Android Gradle插件是第三方插件,托管在Jcenter上,应用之前,需要先配置classpath
这里写图片描述

compileSdkVersion

compileSdkVersion 23 配置SDK的API Level,对应的是Android 6.0 SDK
这里写图片描述
方法的括号可以省略,分号可以省略,所以就是上面的写法。

buildToolsVersion
buildToolsVersion ’23.0.1’:Android构建工具的版本(是一个工具包,包括appt、dex等工具)

defaultConfig
defaultConfig是默认配置,它是一个ProductFlavor。ProductFlavor允许我们根据不同的情况同时生成多个不同的APK包。

buildTypes
buildTypes是一个NamedDomainObjectContainer类型,是一个域对象(与SourceSet一样)。
buildTypes里面有release、debug等,也可以新增N多自定义类型。

Android Gradle任务
assemble、check、build、connectedCheck、deviceCheck、lint、install、uninstall、gigningReport、androidDependencies等任务
这里写图片描述

从Eclipse迁移到Android Gradle工程
1. 直接使用Android Studio导入
2. Eclipse>Export>Generate Gradle Build Files(生成Setting和build脚本)>Android Studio>Import Project (这种方式保持了Eclipse原本的项目格式,只是重写了SourceSet)

自定义Android Gradle工程

defaultConfig 是一个ProductFlavor,如果ProductFlavor没有被特殊定的话,默认会使用defaultConfig{}块值指定的配置(包名、版本号、版本名称等)

applicationId:包名

minSdkVersion:App最低支持的Android操作系统版本

versionCode:App内部版本号,用于版本的升级AA.BB.CC(BB为版本迭代,CC为bug修复版本)

versionName:App版本名称

testApplicationId:用于配置测试App的包名,默认情况下是applicationId + “.test”,默认即可

testInstrumentationRunner:配置单元测试时使用的Runner,默认是android.testInstrumenttationTestRunner,如果要自定义Runner,修改即可。

signingConfig:配置签名信息,对生成的App签名

proguardFile:配置App ProGuard混淆所使用的ProGuard配置文件。

proguardFiles:同事配置多个混淆文件

配置签名信息
这里写图片描述

Debug模式的签名使用的就是SDK自动生成的debug证书(.andriod/debug.keystore)

构建的应用类型
这里写图片描述

applicationIdSuffix:用于配置基于默认applicationId的后缀

debuggable:是否生成一个可供调试的apk

jniDebuggable:类上

minifyEnable:是否启用Proguard混淆

multiDexEnabled:配置BuildType是否启用自动拆分多个Dex的功能。解决65535问题

shrinkResources:是否自动清理未使用的资源,默认为false

混淆:
Android SDK提供了两个Proguard配置文件,proguard-android.txt和proguard-android-optimize.txt,一个是没有优化的,另一个是优化的。

zipalign优化:是一个整理优化APK文件的工具,客提高系统和应用的运行效率,更快地读写apk中的资源,降低内存的使用。
zipAlignEnabled true 即可

Gradle高级定义
使用共享库:com.google.android.maps、android.test.runner等,是独立库,并不会被系统自动连接,需要独立进行生成使用。
这里写图片描述

如果手机系统没有需要的共享库,将不能安装该应用。

Android中,除了标准的SDK,还存在两种库:一种是add-ons库,位于add-ons目录下,大部分是第三方厂家或者公司开发的,为了让开发者使用,但是又不想暴露具体标准实现;第二类是optional可选库,位于platforms/android-xx/optional目录下,一般是为了兼容旧版本的API(org.apache.http.legacy 兼容 HttpClient)

add-ons附件库,Andriod Gradle会自动解析,自动添加到classpath里。
Optional可选库就不会了,使用需要:
这里写图片描述

批量修改生成的apk文件名

applicationVariants(仅仅适用于Android应用Gradle插件)、libraryVariants(仅适用于Andriod库Gradle插件)、testVariants(以上两种Gradle插件都使用)
这三个元素直译过来意思是变体,通俗地讲他们就是Android构建的产物。

触发这3种集合都会触发创建所有任务。
这里写图片描述

动态生成版本信息

版本:major.minor.path,第一个是主版本号,第二个是副版本号,第三个是补丁号

分模块:
这里写图片描述
这里写图片描述

从git的tag中获取
tag的名字一般就是版本名称,可以动态获取tag名称作为应用的名称。
Git命令: git describe –abbrev=0 –tags
使用Gradle的exec,Project对象里的exec方法
这里写图片描述

git tag动态获取版本名称
这里写图片描述
这里写图片描述

从属性文件中动态获取和递增
使用Properties属性,gradle进行读写。

隐藏签名文件信息
签名信息存放到服务器。

自动打包服务器代码:
这里写图片描述
如果是Jenkins,只需要在Jenkins里配置即可。
开发者直接使用debug签名即可
这里写图片描述

如果有时候需要使用正式发布的签名打正式包,比如Jenkins,给每个开发者开放一个账号,他们自己新建个Job就可以打正式的包了,打包之后可以在生成的构建里下载。

动态配置AndroidManifest文件
在构建过程中,动态修改AndroidManifest文件中的内容(友盟统计等)
可以使用manifestPlaceholder、Manifest占位符
ManifestPlaceholders是ProductFlavor的一个属性
这里写图片描述
这里写图片描述

还可以直接:
这里写图片描述
这里写图片描述

还可以使用占位符动态修改动态配置meta信息,比如ContentProvider的auth授权。

自定义BuildConfig
这里写图片描述

注意:值是用单引号括起来的,value的值是什么就写什么,要原封不动的放在单引号里。

动态添加自定义的资源
资源还可以在Gradle中定义:
实现这一功能的是resValue方法,他在BuildType和ProductFlavor这两个对象中都存在,也就是说我们可以分别针对不同渠道,或者不同的构建类型来自定义其特有的资源。
这里写图片描述

生成的资源位置,以Baidu为例,debug模式下,在build/generated/res/resValues/baidu/debug/values/generated.xml这个文件中。
这里写图片描述

个人总结:包括图片一样可以做,自定义图片名称即可,可以通过名称找图片。

Java编译选项
可以对Java源文件的编码、源文件使用的JDK版本进行调优修改。(比如编码UTF-8,配置Java源码的级别为1.6),Gradle提供了便捷入口:
这里写图片描述
这里写图片描述

Adb操作选项配置
这里写图片描述
这里写图片描述

这里写图片描述

DEX选项配置
对于生成DEX文件的过程和处理,Android Gradle插件会调用SDk中的dx命令进行处理。
有时会遇到内存不足的情况:java.lang.OutOfMemoryError:GC overhead limit exceeded
默认情况给dx分配的内存是一个G,1024MB
这里写图片描述

javaMaxHeapSize 配置最大堆内存
jumboMode属性,解决65535问题
preDexLibraries属性,是否预执行dex Libraries库工程,开启后会大大提高增量构建的速度,不过可能会影响clean构建的速度,默认为true,如需要使用dx的—multi-dex选项生成多个dex,导致和库工程有冲突的时候,需要将该选项设置为false
threadCount属性,用来配置Gradle运行dx命令时使用的线程数量,适当的线程数量可以提高dx效率
incremental 配置是否启用dx的增量模式(或许有时候不工作)
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

突破65535方法限制

Facebook给的第一个方案是:打补丁
后来官方给的方案是Multidex,Android 5.0之后的ART运行方式,天然支持App有多个DEX文件。ART在安装App的时候执行预编译,把多个DEX文件合并成一个oat文件执行。对于5.0之前的版本,虚拟机限制没个App只能有一个class.dex,要使用他们,需要使用Andriod提供的Multidex库:
Andriod Build Tools 和 Android Support Repository 到21.1,这是支持Multidex功能的最低支持版本。
要在我们的项目中使用Multidex,首先要修改Gradle build配置文件,启用Multidex,并同时配置Multidex需要的jar依赖:
这里写图片描述

开启了Multidex,会让我们的方法多于65535个的时候生成多个DEX文件,其名字为classes.dex,classes(…n).dex这样的格式。要想可运行,虚拟机需要把其他几个生成的classed记载进来,必须在App启程启动的入口控制,就是Application。直接使用MultiDexApplication或者继承它,或者重写attachBaseContext方法实现:
这里写图片描述

思考:
这里写图片描述
自动清理未使用的资源
Android Lint、Recource Shrinking(构建时,打包前,会检测所有资源,如果没有被引用,这些资源就不会被打包到apk包中,不管自己的还是第三方的都会被处理)

Resource Shrinking要集合Code Shrinking一起使用,就是ProGuard,也就是启用minifyEnabled。
这里写图片描述

自动清理未使用的资源可能会误删,因为我们再代码编写的时候,可能会使用反射引用资源文件,特别是第三方库会这么做,Gradle就区分不出来了,这种情况Gradle提供了keep方法来让我们配置哪些资源不被清理:res/raw/keep.xml
这里写图片描述

Keep.xml 有一个属性是tools:shrinkMode,用于配置自动清理资源的模式,默认是safe,是安全的,可以识别如下引用:
这里写图片描述
如果把清理模式改成strict就没办法识别了,这个资源会被认为没有被引用,而被清除掉。
这里写图片描述

resConfig使用非常广泛,不止于上面描述的语言,还包括Api Level、分辨率等。
上述姿势在打包的时候,不打包到apk中,实际并没有删除工程中的资源。

Android Gradle多项目构建
settings.gradle配置文件
这里写图片描述
这里写图片描述

库项目引用和配置
这里写图片描述

引用库项目其实是库项目发布出来的aar包,默认情况下,Android库项目发出来的包都是release版本的,当然可以通过配置来改变它,比如改成默认发布的,下面是debug版本:
这里写图片描述

还可以针对不同的flavor+buildtype配置:
这里写图片描述

同时发布多个版本的aar包以供不同项目引用:
这里写图片描述
引用方法:
这里写图片描述

库项目单独发布

搭建自己的Maven私服,推荐使用Nexus Repository Manger,版本选择2.x.x

发布Mavne:
这里写图片描述

快照版本SNAPSHOT:
比如配置成1.00-SNAPSHOT,这时候会发布到snapshot中心库,每次发布版本号不会变化,只会在版本号后按顺序号+1,如1.0.0-1、1.0.0-2、1.0.0-3等。这样的版本号,引用的时候版本号写成1.0.0-SNAPSHOT即可,Maven会帮我们下载最新(序列最大)的快照版本。这种方式适用于联调测试的时候,每次修复好测试的问题就发布一个快照版本,知道没有问题为止,然后再放出release版本,正式发布。

发布配置:
这里写图片描述

使用自己Maven服务器候中的库:
这里写图片描述
这里写图片描述

可以使用一个Maven库代替:Nexus Maven提供了一种group类型的repository,这种类型的repository可以同时集成好几个repository,把他们同意当成一个group来对外发布,比如Nexus内置的public group,就包含了release和snapshot,就可以改成:
这里写图片描述

Android Gradle多渠道构建

Android Gradle的Flavor完美解决多渠道问题。

Android Gradle中,定义了个Build Variant的概念,直译是构建变体,可以理解为构建的产物。一个Build Variant=Build Type + Product Flavor,Build Type就是我们构建的类型,比如Baidu、Google等,它们加起来就是baiduRelease、baiduDebug、googleRelease、googleDebug这几种组合的构建产出. Product Flavor是多渠道构建的基础.

Flurry多渠道和友盟多渠道构建
Flurry只需要配置不同的Flurry Key即可,可以自定义BildConfig:
这里写图片描述
使用: Flurry.init(this,FLURRY_KEY);

而友盟需要修改AndriodManifest.xml中的配置,通过配置meta-data标签来设置:
这里写图片描述
则我们可以使用manifestPlaceholders,来进行替换。

多渠道构建定制
Flavor:气味、味道,就是我们点菜说的口味。

applicationId:是ProductFlavor的属性,用于设置改渠道的包名

consumerProguardFiles:混淆文件配置
这里写图片描述
当发布库项目生成一个aar包的时候,使用consumerProguardFiles配置的混淆文件列表也会被打包到aar里一起发布,当应用项目引用这个aar包,并且启用混淆的时候,会自动使用aar包里的混淆文件对aar包代码进行混淆,这样就不用对该aar包进行混淆配置了。

ConsumerPoguardFiles方法是一直添加的,不会清空以前的混淆文件,而consumerProguardFiles属性配置的方式是每次都是新的混淆文件列表,以前配置的会先被清空。

proguardFiles:混淆使用的文件配置
signingConfig:签名配置

testApplicationId:单元测试有自己的专门apk测试包,testApplicationId是用来适配测试包的包名,使用方法与applicationId一样。
一般的testApplicatonId的值为App的包名+.test

testFunctionalTest和testHandleProfiling
testFunctionalTest表示是否为功能测试,testHandleProfiling表示是否启用分析功能。
他们主要用来控制测试包生成的AndroidManifest.xml,最终配置还要体现在AndroidManifest.xml文件中的instrumentation标签的配置上。

TestInstrumentationRunner、testInstrumentationRunnerArguments:测试相关(最终使用的都是adb shell am instrument这个命令),略

versionCode和versionName
渠道的版本号 和 版本名称

useJack
用于标记是否启用Jack和Jill这个全新的、高性能的编译器。
书上说目前还处于实验阶段,有些特性还不支持,比如注解、JDK8的特性等。
正式产品中还是不要使用。
这里写图片描述

dimension
针对不同渠道需要再细分给出的解决方案。
Dimension是productFlavor的一个属性,接受一个字符串,作为该productFlavor的维度。
需要现在flavorDimesions里面声明维度,才能在ProductFlavor中使用:
这里写图片描述
这里写图片描述
这里写图片描述

提高多渠道构建的效率
美团研究出了一个办法,利用了在apk的META-INF目录下添加文件不用重新签名的原理:
1. 利用Android Gradle打一个基本包(母包)
2. 然后基于该包复制一个,文件名要能区分出产品、打包时间、版本、渠道等
3. 然后对复制出来的apk文件进行修改,在其META-INF目录下新增空文件,但是空文件的文件名要有意义,必须包含能区分渠道的名字,如mtchannel_google
4. 重复步骤2、3生成我们所需的所有渠道包apk,可以使用Python脚本来做。

使用:在apk启动的时候,读取apk中的META-INF目录下的前缀为matchannel_文件,如果找到的话,把文件名取出来,然后就可以得到渠道标识了:
这里写图片描述
这里写图片描述
这里写图片描述

持续集成:
Jenkins
这里写图片描述
这里写图片描述

怎样更好地做持续集成
1. 需要一个统一的代码版本控制管理库(可以使用Git进行管理,企业内部,可以使用Gitlab来搭建Git代码管理平台,和Github是非常相似的)
2. 搭建类似于Jenkins的构建平台,Jenkins是把所有构建流程串起来的一个工具。
3. 单元测试、代码覆盖率,以及代码静态检查等平台工具,Android里我们可以使用jacoco作为代码覆盖率工具,单元测试使用Andriod自带的工具就可以,静态代码检查推荐Sonar(可以进行代码静态分析,还可以呈现代码覆盖率和单元测试报告)
4. 每天想代码库的主干提交代码,这样我们的代码修改才可以每天汇集到一起,进行验证。
5. 代码提交的时候,要出触发一次自动构建,这样才是持续的,才能早发现合并后的问题。
6. 发现问题要及时修改,每个参与的人都可以很快获取最近的修改后的代码。要做到这些需要每个人都清楚整个代码库的构建情况。
7. 每个开发者必须要自己现在本机测试后才能提交代码到主干,不能不测试直接提交。
8. 开发者必须每天要提交自己的代码,更新最新代码,要经常和最新的代码保持一致,避免长时间没有同步代码,造成不清楚其他开发者做了什么, 也避免了冲突。
9. 要保证每次构建都要通过,是100%通过
10. 要保证每个人构建的目标都是可以发布的产品,而不是一个测试版的甚至是半成品,这是我们构建的目标

猜你喜欢

转载自blog.csdn.net/yuanyang5917/article/details/79853790