Gradle编译打包Android apk详细介绍

Gradle编译打包Android apk详细介绍

理解Gradle构建过程,解读Android Gradle插件的配置

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

APK包是一个ZIP压缩包,从Java源代码、资源文件到生成这个APK,经过了编译打包一系列特定的过程,SDK文档(/docs/tools/building/index.html)中找到。而这一系列特定的过程,重复繁琐,构建工具(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详解

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

(0)

相关推荐

  • Android如何在Gradle中更改APK文件名详解

    前言 本文主要给大家介绍了关于Android在Gradle中更改APK文件名的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: 默认情况下,Android Studio中的Gradle构建命名为.apk文件app-release.apk.对应用程序build.gradle文件进行了一些小的更改,可以将.apk名称更改为<app name>-release-<version>.apk. 需要在app的目录下面修改build.gradle文件. 第一步是将pro

  • Android 如何修改APK的默认名称

    Android 如何修改APK的默认名称 用Android Studio 打包App时生成的名称默认是 app-release.apk(已签名) 或 app-debug.apk(测试版). 要想打包时修改默认名称,可以打开在build.gradle(module:app)文件,在android{}中添加如下代码: android.applicationVariants.all { variant -> variant.outputs.each { output -> def outputFil

  • Gradle编译打包Android apk详细介绍

    Gradle编译打包Android apk详细介绍 理解Gradle构建过程,解读Android Gradle插件的配置 阅读本文一定是要使用过Gradle生成apk,文中不会讲如何安装运行Gradle,如有需要可先看文末的参考文章. APK包是一个ZIP压缩包,从Java源代码.资源文件到生成这个APK,经过了编译打包一系列特定的过程,SDK文档(/docs/tools/building/index.html)中找到.而这一系列特定的过程,重复繁琐,构建工具(build tool)就是来流程化

  • Taro打包Android apk过程详解

    首先,我们使用使用命令创建模板项目,创建的命令如下. taro init myApp 然后,使用 yarn 或者 npm install安装依赖包,并使用下面的命令编译Taro项目. yarn dev:rn 启动后会开启一个监听的进程. 不过,细心的你可能会发现,使用taro init命令初始化的项目是没有原生模块支持的,原来Taro使用了一个壳子工程,首先使用下面的命令下载壳子工程taro-native-shell,如下所示. git clone git@github.com:NervJS/t

  • IOS 打包静态库详细介绍

    IOS 打包静态库详细介绍 一.前言 前段时间看的一本书上说:"隔着一段距离看,很多有趣的知识看起来都很唬人."比如说这篇我要总结的"静态库知识",在我初出茅庐的时候着实觉得那些后缀名为".frameworke".".a".".dylib"的文件很神秘,很高冷.那时我虽然知道只要导入一个库就能引用库里面很多封装好的东西,但对这个"库"究竟是什么"鬼",一直都是云里雾里

  • Android超详细介绍自定义多选框与点击按钮跳转界面的实现

    总程:在avtivity_main.xml设计5个控件,btn1-5,点击btn1弹出一个多选对话框,点击按钮btn1弹出一个多选框可选择你喜欢的打野英雄,点击btn2跳转到activity_main2界面(就是图片,不可选择)设计思路流程:在activity_main.xml布局界面,总体在头目录进行垂直排列,然后镶嵌5个水平的线性布局(左是ImageView,右边是Button按钮)由于5张图的大小在一个屏幕显示不出来,所以添加一个ScoveView滚动,以使所有资源可以看到! 在MainA

  • IOS 静态库打包流程简化详细介绍

    IOS 静态库打包流程简化 在iOS开发中,我们经常会遇到开发SDK的需求.开发好的静态库后需要手动的合并.a文件,然后再拷贝相关的头文件,接着把静态库和头文件放在同一个文件里面打包发送给SDK的使用者.本文将介绍如何使用脚本,简化这一连串的过程.为了照顾广大初学者,教程将会详细介绍打包的基本流程. 关于静态库和动态库区别,可以看我的另外一篇文章: iOS静态库和Framework区别 项目配置 新建一个名为TestSDK的静态库工程 然后点击Target下边的加号按钮,添加新的Target 选

  • android AsyncTask详细介绍

    AsyncTask和Handler对比 1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程. 使用的优点: 简单,快捷 过程可控 使用的缺点: 在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来. 2 )Handler异步实现的原理和适用的优缺点 在Handler 异步实现时,涉及到

  • Android ADB详细介绍及用法

    Android ADB 用法 adb  全称是 Android Debug Bridge, 就是起到调试桥的作用. 用来操作android设备的 阅读目录 adb 有什么用 adb 下载 adb devices adb install  (安装软件) adb uninstall (卸载软件) adb shell (登录shell) adb push (从电脑上发送文件到设备) adb pull (下载文件到电脑) adb help (显示帮助信息) adb 有什么用 借助adb工具, 我们可以管

  • Android  ADB详细介绍及用法

    Android ADB 用法 adb  全称是 Android Debug Bridge, 就是起到调试桥的作用. 用来操作android设备的 阅读目录 adb 有什么用 adb 下载 adb devices adb install  (安装软件) adb uninstall (卸载软件) adb shell (登录shell) adb push (从电脑上发送文件到设备) adb pull (下载文件到电脑) adb help (显示帮助信息) adb 有什么用 借助adb工具, 我们可以管

  • Android Bitmap详细介绍

    复制代码 代码如下: package com.testbitmapscale; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Iterator; import com.testbitmapscale.R.drawable; im

  • Android Loader详细介绍及实例代码

    一,Android装载器基本方法 装载器从android3.0开始引进.它使得在activity或fragment中异步加载数据变得简单.装载器具有如下特性: 它们对每个Activity和Fragment都有效. 他们提供了异步加载数据的能力. 它们监视数据源的一将一动并在内容改变时传送新的结果. 当由于配置改变而被重新创建后,它们自动重连到上一个加载器的游标,所以不必重新查询数据. 装载器API概述 在使用装载器时,会涉及很多类和接口们,我们在下表中对它们总结一下: Class/Interfa

随机推荐