Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

Java转身系列之安卓 —— Android Studio的代码工程组织方式

从Ant、Maven到Gradle

编程的最终目标是将软件运行起来,编译是连接编程与程序运行的关键环节。

Java编译指令是javac,它将文本源代码编译成.class字节码文件,虚拟机装载字节码文件,进行解析、链接和运行。

javac com.example.Main

软件体积越大,如何组织代码、管理依赖、编译运行及测试,就需要有成体系的方法和工具进行支持,否则依靠人工进行重复的命令输入编译,会令人不胜其烦,效率低下。

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

C语言程序猿有Makefile,Java在2000年有了Ant,它用java实现了cmake的机制,并且用当时的“网红”XML作为描述文件。Ant比cmake进步了好多,但依然需要在xml中写多个task来完成工作,Ant一开始没有一致的依赖仓库进行统一管理(后来通过子项目Ivy解决),不久后Maven出现。这两个项目都与Jakarta有关。

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

Maven采用Convention over Configuration(coc),引入项目对象、生命周期、依赖库管理、插件等理念和机制,将代码组织和软件运行部署等提升到新高度,经过多年经营,已成为代码工程组织管理的集大成者,得到业界最普遍支持。

2007年后,Ruby on Rails大热,这个Web框架将Ruby这门与Java一般年龄的语言重新带入大众视野。Ruby简洁、富有表现力的语法,独特的面向对象、天生的元编程以及动态语言等特性,加上ROR在MVC、Active Record数据访问等方面带来了奇高生产效率,令当时传统的Web开发技术如JSP、ASP、PHP等相形见绌,受到硅谷的青睐,成为创业公司首选,这股风潮迅速遍及全世界。

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

与ROR一起,号称Ruby Make的Rake,也因为Ruby语法在任务执行的“领域”特性吸引了开发者眼球。Rake从相似性讲更接近于Ant,但是它并没有使用xml或者其他结构化文本(如YAML)作为描述和设置文件,而是直接使用了Ruby语言,形成了“代码即配置,配置即代码”的“奇观”。由于Ruby强大的叙述表达能力,Rake在ROR开发、构建、运行、部署这个领域形成了领域特定语言(Domain Specific Language, DSL)。

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

ROR如一股清流,注入活力渐渐不够的Java社区,后来出现了JRuby与Groovy。Groovy是运行在JVM的动态语言,取法于JavaScript、Ruby、Java、Scala等语言,与Java具有亲缘性,它建立在Java生态环境之上。由于Rake实在是太成功,于是在Groovy之上就有了借鉴Rake思想的Gradle。与Ant、Maven相比,Gradle具有以下一些鲜明特点:

  1. 无xml配置,使用原生动态语言DSL进行工程管理,任务可灵活定制;
  2. 支持Maven、Ivy仓库,更多的依赖冲突解决方案;
  3. 代码工程目录不用遵循类似Maven的固定的目录结构,可定制;
  4. 支持Java、Kotlin、Scala、Groovy、Webpack、C、C++项目;
  5. 灵活的多项目管理,尤其适合大型工程组织,使用Gradle的有Android、Spring、Hibernate这类大型项目;
  6. 对于混合编译的多项目管理,类似用到JNI的Android项目,使用Gradle会更简单。

Android工程目录

这是模板工程的项目视图窗口:

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

这个视图呈现的并不是工程实际目录结构,而是一个切面(Facet,跟Eclipse的类似)。

一级 二级 三级 文件 说明
App manifests AndroidManifest.xml App入口声明文件
java 源代码目录
res 资源目录
drawable 矢量图xml文件,了解svg就不陌生
layout 布局xml文件
menu 菜单
mipmap app启动图标目录
values colors.xml strings.xml dimens.xml styles.xml 值引用,以样式、字符串、颜色、空间位置等分类
Gradle Scripts gradle脚本

实际的工程目录结构如下:

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

App代码构成和关系

Android App是手持设备上的“桌面”开发,这是从“外形”上进行简单类比。

经历过传统桌面开发时代的老程序猿知道,桌面开发就是所见所得,通过IDE的支持,拖拽UI组件进行界面设计和开发。那个时代最好的界面开发IDE有Delphi、VB、PB(Power Builder)。以Delphi为例,有专门的Form文件对界面进行描述,IDE有专用的界面设计器与开发者进行设计交互。

Web经过1.0、2.0时代的演进,各种开发理念和开发技术已经深入影响到桌面开发,如内容与样式分离、XML描述、Rich Internet App等,微软WPF XAML、Qt QML、JavaFx FXML是web与桌面融合的典范。

Android界面由一个个Activity组成,通过Activity切换与用户进行交互,Activity也是通过XML进行界面描述。

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

Activities就像一副扑克牌,用户通过点触一张一张地交互。以下一张图显示了主入口Activity的代码关系:

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

XML是界面DSL,Activity在onCreate这个钩子方法被调用时,通过R.layout.activity_main这个layout资源id进行界面加载和绘制,R类由IDE自动生成:

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

Gradle脚本

这是一个Gradle管理的项目:

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

  1. gradle-wrapper.jar,起到bootstrap的作用,例如自动下载安装gradle;

  2. gradle-wrapper.properties,wrapper的属性配置,例如gradle安装包的下载url地址;

  3. build.gradle,当前项目的配置任务脚本;

  4. gradlew,nix环境shell执行脚本;

  5. gradlew.bat,windows批处理执行文件;

  6. settings.gradle,配置脚本,配置哪些项目包含在本工程中。

Gradle是什么?运行一下这个东西看看。安装好Gradle后(安装方法如同Maven,解压,设置bin路径变量),然后进入一个目录,运行gradle init就可以初始化一个项目:

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

build.gradle就是一个gradle文件,里边有一个任务hello,输出“hello, gradle”字样,执行gradle hello运行一次:

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

这个执行发生了以下几件事:

  1. gradle批处理命令通过jvm启动groovy环境,加载gradle;

  2. gradle找到build.gradle文件;

  3. 根据批处理入参hello,找到hello任务;

  4. 执行hello任务,打印出”hello, gradle”

这里build.gradle其实就是groovy脚本,task hello {}这个语法就是前面一直提到的DSL, {}之内的内容是闭包——这就是gradle的魔法真相!

关于gradle和groovy的关系,groovy的魔法,由于不是本文重点,在此略过。

Android Gradle脚本

以Android、Project视图查看Android工程,

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

Android工程是以Gradle多项目形式进行组织,有一个*的build.gradle文件(Project: MyApplication),以及一个app子目录,app拥有自己的build.gradle文件。

settings.gradle用来配置多项目构建,这里指明app子目录是一个gradle模块项目:

include ':app'

以下是*Gradle build文件,

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

  1. 下载插件定义块,告诉gradle从google和jcenter两个源下载android的gradle插件;

  2. android的gradle插件;

  3. *以及模块项目的配置,这里定义了依赖库位置;

  4. 当前工程的特定(ad hoc)任务,这里是做build目录的清空操作。

除了*gradle文件负责总体配置外,app下的build.gadle设置了app这个具体子项目的管理信息:

app build.gradle

第一行指明使用android插件。

  • compileSdkVersion指定Android SDK编译版本,通常是最新版本;

  • defaultConfig栏包含是app的各项属性;

  • applicationId是app名,在Google Play商店中必须唯一;

  • minSdkVersion,app要支持的最小api版本;

  • targetSdkVersion,应与compileSdkVersion一致;

  • versionCode,整数版本号,在Google Play商店中与applicationId一起起到描述标识作用;

  • versionName,版本名称,主要是内部版本管理用;

  • testInstrumentationRunner,配置Junit4作为app的测试运行工具。

dependencies块定义了工程依赖库管理。

配置式引入依赖

Guava对jdk很多地方进行“补强”,现在在工程中引入guava依赖:

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

编辑根下的build.gradle文件,加入mavenCentral仓库。前面提到,gradle自身也支持maven依赖库管理。

在app下的build.gradle中,引入guava包:

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

在引入新的依赖后,AS自动会在主编辑区上方提示需要同步gradle设置,点击后自动会下载依赖并构建整个项目:

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

界面式引入依赖

不喜欢原始的配置式引入依赖,可以通过界面交互来引入依赖。在工程导航Android视图中选择app,右键打开上下文菜单,选择open module settings(打开模块设置),可以通过网络和本地导入依赖:

Java转身系列之安卓2 : Android Studio的代码工程组织——Gradle、App构建

小结

  1. Android工程通过Gradle进行项目代码组织和管理,目录呈现父子关系,包含了源代码目录、资源目录、测试目录、gradle脚本;
  2. Android App以Activity界面方式与用户交互,Activity采用代码与用户界面描述xml分开设计和加载模式;
  3. 开发者与可以通过AS界面完成与Gradle的设置和调用,但只有了解Gradle的思想,明白它的作用,熟悉基本的配置,才能籍助IDE更好管理App工程,尤其是遇到编译错误时,知道如何解决问题。