当前位置: 移动技术网 > IT编程>移动开发>Android > Gradle编译打包Android apk详细介绍

Gradle编译打包Android apk详细介绍

2019年07月24日  | 移动技术网IT编程  | 我要评论

锦旗尺寸,空港大亨,许佳麒

gradle编译打包android apk详细介绍

理解gradle构建过程,解读android gradle插件的配置

阅读本文一定是要使用过gradle生成apk,文中不会讲如何安装运行gradle,如有需要可先看文末的参考文章。

apk包是一个zip压缩包,从java源代码、资源文件到生成这个apk,经过了编译打包一系列特定的过程,sdk文档(/docs/tools/building/)中找到。而这一系列特定的过程,重复繁琐,构建工具(build tool)就是来流程化这些过程,解放你的双手。ant作为apk早期的构建工具,构建过程显得很直观,像配置;gradle可以方便地配置,但更像脚本,可以编程。

理解gradle构建

1.简单理解构建工具

从一个程序员的角度,你该如何编写代码来自动化你的apk生成过程呢?首先得知道你需要的sdk、ndk在什么位置,android工程有几个库工程,它们的java源代码、资源文件分别有哪些?命令行的输入参数肯定无法满足需求,那自然而然想到配置文件。因此你的自动化工具就是解析这些配置文件,按照生成apk文件要求执行的程序。gradle就是这样的工具程序,配置文件就是你常见的settings.gradle,build.gradle,不过他还提供了更多的功能,如依赖管理,流程控制,还有插件机制来定制你的生成过程。

gradle的编程语言是groovy,其需要的配置文件支持groovy。groovy语言像java一样是基于jvm的,而且能够很好的支持java,因此可以用java代码编写扩展插件,像普通编程一样来写配置文件,而不用像ant一样用xml来编写配置逻辑。

2.groovy

groovy的语法,把自己的代码缩略的看上去像脚本,本人也只是看了一点点文档,列出我们常用的介绍一下:
首先groovy是面向对象的动态语言
(1)语句的末尾可省略分号
(2)变量定义可以用def,也可以直接使用
(3)函数定义可以用def,也可以不用(有返回值声明也可)

  函数可省略参数类型
  函数调用可省略括号

来看个例子:

println 'hello'                  
 
int power(int n) { 2**n }             
 
println "2^6==${power(6)}" 

第一行输出字符串hello,第二行定义一个函数,第三行输出函数调用值,双引号中间的${}可被解析成表达式运行。

(4)list和map类型

list实现就是java的java.util.arraylist,变量由[]包围,用逗号分隔,比如

def heterogeneous = [1, "a", true] //其元素可以是任何对象 

map的实现是java.util.linkedhashmap,也是由[]包围,用逗号分隔,其中的键值对是key:value形式,如:

def colors = [red: '#ff0000', green: '#00ff00', blue: '#0000ff']   
 
assert colors['red'] == '#ff0000' //取值可以[key]或者.key的形式 
assert colors.green == '#00ff00'  

(5)闭包(closures)

闭包我的理解类似c里的函数指针,或者说函数对象,可以像函数一样调用的对象
{ [closureparameters -> ] statements }
例子

def testclosure = {int arg1, string arg2 ->//def可省略,参数可省略,默认有it,当只有->表示没有参数 
 println "arg2:${arg2}" //执行的代码,返回值是最后一句,也可以用return 
} 
 
调用: 
testclosure(1,'2') 
testclosure.call(1,'2') 

3.gradle

接着说自动化工具,编程语言有了,那实现一系列特定过程生成apk,就看如何实现了。gradle里有project,表示一个待编译打包处理的工程,可以生成apk,可以生成jar,project中可以包含多个project;每个project由很多的task构成,可以理解为不同的过程;每个task里又是有不同的action,和一系列要执行的操作(或者说你写的要执行的语句)。project中的task的执行顺序,则是由其dependson来控制的。
gradle执行时是以task为单位执行,在命令行中以gradle task来执行,这一过程的生命周期分为三个阶段(官方使用手册中也有详细介绍the build lifecycle):

(1)初始化阶段

判断包含哪些工程,创建对应的project实例,可以看到最新执行的是在settings.gradle中的语句

(2)配置阶段

创建不同的task(task可以动态生成),并根据task之间的dependson,确定task的执行顺序,或者禁用某些task。此阶段完成后,task之间的依赖关系也就确定下来。

(3)执行阶段

在配置完成后,按照依赖关系,按顺序执行。

需要注意的是通常在task中的语句,都是在配置阶段执行的,而dofirst,dolast这类action是在执行阶段中的。因此会出现你的依赖关系中没有的task中语句也被执行了的问题

如下例子,打印出的task内容是不一样的

task printtasksname { 
 tasks.all {//all是一个方法,参数可以是闭包,函数的元括号可以省略 
 println "show tasks in configuration:${it.name}" //it是闭包的默认参数 
 } 
 dofirst { 
 tasks.all { 
 println "show tasks in execution:${it.name}" 
 } 
 } 
} 

4.gradle插件和androd插件
在提供了基本的流程控制之后,接下来是具体的要做什么,构建什么。gradle提供了针对语言的插件如java,groovy等负责编译,集成插件如application,war等生成java可执行程序,web程序的war文件。android根据apk生成的过程,编写了自己的插件,其中也使用了java插件。

(1)自定义插件的插件名称在resources/meta-inf/gradle-plugins

在resources/meta-inf/gradle-plugins目录下有后缀为properties文件,该文件的命名就是你在build.gradle中使用插件的名字,里面声明了该插件的实现类。在android插件的源码中可以看到android.properties和com.android.application.properties中两个插件名称,因此在build.gradle中,应用工程使用android插件需要apply plugin: 'android'(已是deprecated)或者apply plugin: 'com.android.application'

(2)android插件中application的实现类是appplugin,继承自com.android.build.gradle.baseplugin ,调用apply方法,相应的configureproject(),解析local.properties,获得sdk位置,创建androidbuilder,应用javabaseplugin,而后createextension() 关联buildtype,productflavor,signingconfig,最后createtasks(),完成各个task的创建

5.dsl(domain specific language)

dsl我翻译成领域专用语言,就是在这里预先规定好的规则,或者说是行话。在gradle的dsl中一般常见的类型,一种是类型(type)有project、task等,给他们定义了不同的操作和用法,一种是语句块(build script block或configuration block)如build.gradle中常见的buildscript { },allprojects { }。android中也定义了非常多语句块,如buildtypes { },sourcesets { }。

android gradle插件配置

有了上述概念,再看android应用中的build.gradle,其实文中也只能讲一些,但是更多的可以自己查看android插件的dsl

apply plugin: 'com.android.application' // 使用android插件,非库工程,生成的是apk 
 
dependencies { 
 compile 'com.android.support:multidex:1.0.1' 
 compile filetree(dir: 'libs', include: '*.jar') 
 compile project(':库工程1') // 代码、资源包含在主程序apk中的 
 provided project(':库工程1') // 只参与编译,不输出到目标apk中 
} 
 
// android插件dsl中的appextension类型 
android { 
 compilesdkversion rootproject.ext.compilesdkversion // 多工程时配置统一的属性 
 buildtoolsversion rootproject.ext.buildtoolsversion 
 
 // lint检查,避免lint检测到不符合条件退出编译 
 lintoptions { 
 abortonerror false 
 } 
 
 // gradle编译会默认合并库工程的manifest到主工程,如果主程序和库工程的包名不一致会有问题 
 enforceuniquepackagename = false 
 
 // 所有的 product flavors继承 
 defaultconfig { 
 applicationid "cn.arainfo" 
 minsdkversion 14 
 targetsdkversion 10 
// multidexenabled true 
 dexoptions { 
 javamaxheapsize "2g" 
 jumbomode true 
 } 
 
 } 
 
 // 此处由于工程是从eclipse导入,所有路径都进行了声明 
 sourcesets { 
 main { 
 manifest.srcfile 'androidmanifest.xml' 
 java.srcdirs = ['src'] 
 resources.srcdirs = ['src'] 
 aidl.srcdirs = ['src'] 
 renderscript.srcdirs = ['src'] 
 res.srcdirs = ['res'] 
 assets.srcdirs = ['assets'] 
 } 
 
 debug.setroot('build-types/debug') 
 release.setroot('build-types/release') 
 } 
 
 // 编译类型,指定release的proguard配置文件 
 buildtypes { 
 release { 
 minifyenabled true 
 proguardfile 'proguard.flags' 
 } 
 } 
} 
 
 
 
 
afterevaluate { 
 println "afterevaluate set project dependson..." 
 project(':主程序工程').tasks.getbyname("assembledebug").dependson ":子程序工程:assembledebug" 
 
 
 if (project.hasproperty('testrelease')) { 
 project(':主程序工程').tasks.getbyname("assemblerelease").dependson ":子程序工程:assemblerelease" 
 
 } 
} 

说明几处

(1)如在根目录的build.gradle中声明公用属性

ext {
 compilesdkversion = 21
 buildtoolsversion = '25.0.0'
 isonwindows = os.isfamily(os.family_windows)
}

(2)buildtypes { },productflavors{ },signingconfigs { }

上述三个语句块,类型是nameddomainobjectcontainer<t>,其中t是buildtype productflavor signingconfig,而buildtypes { },productflavors{ }中增加新的类型,会对应有新的task生成,规则为assemble[flavor][buildtype],因此当compilesdkversion较低,又用了multidex时,还想用instantrun,就可以创建一个新的buildtype,只在测试时使用

(3)afterevaluate语句块

android的gradle插件版本在2.2.0时,task的创建已经在afterevaluate,因此,如果想继续使用tasks.getbyname("assembledebug"),必须要将自己的语句写到afterevaluate { }语句块中

总结:

越写越心虚,零零碎碎的内容非常多,按照自己的理解贯穿下来,涵盖了部分内容,基本可以理解build.gradle。但是语句块如buildtypes { }和buildtype如何关联起来(或者说如何解析出来并创建对象)的,并没有很好的理解。
如果有时间可以好好看下《深入理解android(一):gradle详解》,一般的问题,比如多渠道打包productflavors怎么配置啊,自己通过查android的dsl就能解决。

参考文档:

1.groovy官方文档
2.gradle用户手册
3.gradle的dsl
4.android插件的dsl
5.深入理解android(一):gradle详解

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网