Android Studio下项目构建的Gradle配置及打包应用变体

Posted 断风尘

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Studio下项目构建的Gradle配置及打包应用变体相关的知识,希望对你有一定的参考价值。

Gradle简介

  Gradle是一个自动化构建工具,采用Groovy的Domain Specific Language(领域特定语言)来描述和控制构建逻辑。具有语法简洁、可读性强、配置灵活等特点。基于Intellij IDEA社区版本开发的android Studio天生支持Gradle构建程序。Groovy是一种基于JVM的敏捷开发语言,结合了Phthon、Ruby和Smalltalk的许多强大特性。同时,Groovy代码既能够与java代码很好地结合,也能够用于扩展现有的代码。
Gradle有如下特点:

  • Gradle支持多工程构建和局部构建
  • Gradle支持远程或本地依赖管理:支持从远程maven仓库(项目构建工具、软件项目管理综合工具)、nexus私服(局域网内一种特殊的远程仓库)、ivy仓库(管理项目依赖的工具)以及本地仓库获取依赖
  • Gradle与Ant、Maven兼容
  • Gradle可轻松迁移:Gradle适用于任何结构的工程,可以在同一个开发平台平行构建原工程和Gradle工程
  • Gradle使用灵活:Gradle的整体设计是以作为一种语言为导向的,而非成为一个严格死板的框架
  • Gradle免费开源
  • Gradle很好的与IDE集成
  • Gradle可以更容易地集成到自动化构建系统

Android Studio下项目构建的Gradle配置

  在Android Studio下创建一个Android项目,并切换到Project视图下,可以看到下图的项目结构。
这里写图片描述
  红框标记就是项目中Gradle相关的配置文件,下面我们从app模块下的build.gradle开始对各个配置文件进行分析。

一、app Module下的build.gradle文件说明

  app这个Module下的gradle配置文件,算是整个项目最主要的gradle配置文件,主要配置应用程序属性、配置应用程序签名、配置应用程序特性(渠道)、配置应用程序构建类型和配置应用程序依赖,下图是默认生成的文件内容:
这里写图片描述

1. 应用插件apply plugin总共有三种类型值,分别如下:

apply plugin: 
‘com.android.application’//这表示此module是一个可运行的应用程序,可以直接run的
apply plugin: ‘com.android.library’//这表示此module是一个安卓依赖库的工程,不可直接run
apply plugin: ‘java’//这表示此module是一个java项目,在此module中只能使用java的api

2. 关于android闭包,

  其默认有compileSdkVersion、buildToolsVersion、defaultConfig闭包和buildTypes闭包。
  compileSdkVersion和buildToolsVersion是两个关于版本描述的参数

compileSdkVersion 25//指定编译版本,开发采用的sdk版本
buildToolsVersion “25.0.2//编译时采用的构建工具的版本

  defaultConfig闭包参数说明

applicationId “com.jointem.variantpackaging”//应用的id,这里决定应用的唯一标识
minSdkVersion 17//决定此应用可运行的安卓系统最低版本
targetSdkVersion 25//决定此应用可运行的安卓系统最高版本
versionCode 1//应用版本号,更新需要,新包的值要大于老包的方可更新
versionName ‘1.0’//应用版本名,通常用于显示
testInstrumentationRunner”andrid.support.test.runner.AndroidJUnitRunner”//Android单元测试test runner

  buildTypes闭包参数说明
  关于构建类型buildType,默认有release和debug两种,默认只显示release方式,一般是正式发布的包。

minifyEnabled false//混淆开关
proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’// 指定混淆文件及混淆文件规则配置文件的位置

3.关于模块依赖dependencies

compile fileTree(include: [‘*.jar’], dir: ‘libs’)//编译文件树(编译依赖libs目录下所有jar)
androidTestCompile(‘com.android.support.test.espresso:espresso-core:2.2.2’, { 
exclude group: ‘com.android.support’, module: ‘support-annotations’ 
})
compile ‘com.android.support:appcompat-v7:24.2.1’//是从repository(默认是jCenter())里下载一个依赖包进行编译并打包
compile ‘com.android.support.constraint:constraint-layout:1.0.0-beta5’
testCompile ‘junit:junit:4.12’// 仅仅是针对单元测试代码的编译编译以及最终打包测试apk时有效,而对正常的debug或者release apk包不起作用
compile project(‘:andr-library’)//是将另一个module进行编译并打包

  其实,app下的脚本对应的是项目的Project Structure对话框中的设置,从左到右依次是属性、签名、渠道特性、构建类型和依赖,快捷键Ctrl+Shift+Alt+S(Windows/Linux),如下。
这里写图片描述

二、library Module下的build.gradle配置

  除了app Module,每一个Module也都有一个gradle配置文件,语法都一样,区别在于开头声明的是apply plugin: ‘com.android.library’,表明该Module是以依赖模块的形式存在。

三、全局配置文件

  所有的全局配置文件都会放置在项目的根目录下,包括忽略文件、本地配置文件、gradlew的批处理文件以及项目gradle相关的配置文件。

1.gradle配置文件gradle-wrapper.properties,默认内容如下:

#Thu Mar 09 16:27:37 CST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\\://services.gradle.org/distributions/gradle-3.3-all.zip

  此配置文件中主要声明了gradle的目录与下载路径以及当前项目使用的gradle版本,这些默认的路径我们一般不会更改的,这个文件里指明的gradle版本不对也是很多导包不成功的原因之一。

2.关于project下build.gradle配置文件

  整个项目的gradle基础配置文件,用来配置项目的构建任务,内容如下:

// Top-level build file where you can add configuration options common to all sub-projects/modules.// 项目构建文件,可以在各子项目/模块添加常用的配置选项。
buildscript {
    //Android插件从这个仓库下载
    repositories {
        jcenter()//依赖仓库源的名称,兼容maven的远程中央仓库
    }
    //项目依赖
    dependencies {
        //android gradle插件
        classpath 'com.android.tools.build:gradle:2.3.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}
allprojects {
    //此处配置Project中默认的仓库源,包括每个module的依赖,无需每个module单独配置
    repositories {
        jcenter()
    }
}
//打包前执行clean任务
//任务类型是Delete
//clean任务就是删除项目根目录下的build目录中的文件
task clean(type: Delete) {
    delete rootProject.buildDir
}

3.关于Gradle配置文件gradle.properties

  gradle.properties是Gradle的配置文件,build.gradle通过读取这个文件配置的参数来进行相应构建,可在此配置文件中配置一些全局参数(全局代理)。默认文件内容如下:

# Project-wide Gradle settings.

# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.

# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html

# 配置守护进程的jvm参数
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m

#配置完成后,Gradle将在并行模式下运行(并行编译)
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
#http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

4.关于全局配置文件setting.gradle

  默认文件内容include ‘:app’。是项目的全局配置文件,主要声明一些需要加入构建的模块。在Android studio中通常都是默认自动在此文件中添加依赖的module。

Android Studio下的依赖方式

一、Project Structure

  按快捷键Ctrl+Shift+Alt+S(Windows/Linux),打开Project Structure弹窗。
这里写图片描述
  在Dependencies下有个scope,这里可以选择依赖的方式,可选项有Compile、Provided、APK、Test compile、Debug compile和Release compile,下面分别对这些可选项进行简单说明。

  • Compile:是对所有的build type以及favlors都会参与编译并且打包到最终的apk文件中,是从repository(默认是jCenter())里下载一个依赖包进行编译并打包。
  • Provided:Provided是对所有的build type以及favlors只在编译时使用,类似eclipse中的external-libs,只参与编译,不打包到最终apk,是提供给那些只编译不打包场景的命令。就是我在编译的时候对某一个jar文件有依赖,但是最终打包apk文件时,我不想把这个jar文件放进去,可以用这个命令。
  • APK:只会打包到apk文件中,而不参与编译,所以不能再代码中直接调用jar中的类或方法,否则在编译时会报错。
  • Test compile:仅仅是针对单元测试代码的编译编译以及最终打包测试apk时有效,而对正常的debug或者release apk包不起作用。
  • Debug compile:仅仅针对debug模式的编译和最终的debug apk打包。
  • Release compile:仅仅针对Release 模式的编译和最终的Release apk打包。

二、关于Compile的两个子参数:

  • compile files:是从本地的libs目录下寻找picasso-2.4.0.jar这个文件进行编译并打包。类似的命令有compile fileTree(dir: 'libs', include: '*.jar'),即,将libs目录下所有jar文件进行编译并打包。
  • compile project:是将另一个module(等同eclipse中的library)进行编译并打包。

打出多变体包

一、为什么要打包变体包?

  在开发过程中经常遇到多个环境切换的问题,比如,正在连接测试环境进行调试,此时又想看生产环境的数据显示,通常的处理方式是卸掉测试环境的然后运行一个生产环境的包。我们都知道,build一个大项目,直到安装到手机上,整个过程是很耗时的。如果同一台手机上可以同时安装同一个应用的多个不同环境的apk,那么,当想看其它环境的数据时,就不需要重新卸载再安装了(同理,同一应用,需要不同版本的安装包[免费版/收费版]时,也可以这样处理,而无需为不同版本创建不同的分支)。

二、打包变体包的条件

  在创建完一个项目后,我们在app这个module下的build.gradle中的android节点下defaultConfig节点中,可以看到一个applicationId属性,默认的 applicationId 和 AndroidManifest.xml 中的 package 属性是相同的。applicationId 和 packageName 它们各自代表什么?为何会是相同的呢?其实,package 代表了 java 代码中的包名,在应用中的资源,就是通过package标识的(比如R文件,包名.R);applicationId 则代表了应用中的唯一标识,和应用签名一起用来区别于其他应用。
  在手机上,系统同样是通过applicationId和签名来标识一个app的。所以,我们只要在打包apk时,使得应用之间的applicationId和签名各不相同,就能够实现同一个应用的不同变体同时安装在同一台手机上了。同时,为了更好的区分给个变体应用,我们还可以通过对占位符的使用,使用gradle配置不同的资源文件、不同的应用名称与图标、不同的java文件等有区别的属性,打出不同的变体包。

三、打出变体包的步骤

1.配置签名、渠道(applicationId)

  选择app module,选择Signing选项卡,首先配置好了这两个变体版本的签名文件,文件名可任意取(见名知意)。
这里写图片描述
  签名属性字段描述:

  • keyAlias//指定签名文件的别名
  • keyPassword//指定签名文件的别名配对密码
  • storeFile//签名文件路径
  • storePassword//签名文件的密码

  切换到Flavors选项卡,配置变体包的相关属性,由于下一步创建资源文件夹时,需要与这里的文件名对应,所以此处的取名尽量简约。这里我创建两个命名dev和rea的Flavor,所有的Flavor都会复写defaultConfig中的属性,所以可以看到我并没有填写其中的一些属性。在这里非常关键的一点,是对不同的变体设置不同的applicationId,并选择对应的Signing Config。
这里写图片描述

2.新建不同变体的sourceSet

  在修改完变体的配置文件后,我们还需要再项目的src文件夹下,新建以我们的Flavor名称命名的文件夹,并在这些文件夹下新建如main中相同的目录结构。
这里写图片描述
  我们正常编写项目都是写在main这个sourceSet下的,但是如果我们的项目的变体有不同的资源文件、Java文件时,我们就需要使用不同的sourceSet来区别开。
  需要注意的是,Flavor下的资源文件会与main中的合并,如果存在重复,则Flavor中优先级高于main中。我们可以将不同变体中共用的资源存放在main中,只将不同的内容存放在flavor的sourceSet中。
  如果不同变体有内容不同的Java文件则要注意,需要将这个Java文件放置到每个flavor 的sourceSet文件夹下,main中不可以有这个Java文件,如果main中也存在此文件,编译时会提示文件重复。比如说有两个变体,有着不同的TargetActivity.java,那么main中就不能有这个文件了,需要把这个Java文件放到各个flavor的sourceSet下,同时这个Java文件在sourceSet中要按照main中的包结构保存。

3.关于配置不同的launcher图标和应用名

  通过之前的操作,已经可以有效区分变体包的applicationId、Signing Config和java文件,那么应用logo和name又该如何处理呢,这里有两种方法。

1)配置不同的资源:我们可以使用类似于处理java文件的方法,在不同的sourceSet中配置string.xml中app_name属性,在mipmap文件夹中放置对应的ic_app.png图标资源。不过当我们需要更换启动图和应用名而变体包又比较多的情况下,这种方法会比较麻烦,因为我们要到每一个sourceSet资源文件下去对应的修改,如果使用占位符的方式,那么会变得简单一些。
2)使用占位符的方式:在AndroidManifest文件中使用占位符然后在flavor中直接配置,在flavor中使用的属性是manifestPlaceholderss(manifestPlaceholders =[NAME1:VALUE1]),而图标资源和应用名称我们都可以直接放置到main中。
结果如下图:
这里写图片描述

四、Debug时选择变体

1.选择要调试的应用

  通过之前的操作,已经成功配置了多个变体,那么问题来了,这么多个变体,我们在debug的时候,我们怎么选择要debug的变体呢?这里有两种方式进行切换。

  • 选择Build->Select Build Variant,在左下角会显示出Build Variant视图,点击选择要编译的变体。
    这里写图片描述
  • 另一种方式则是直接点击左下角Build Variant选下卡,之后的操作同上。

2.需要注意的的地方

  • 在这里可以看到有4个变体,这是为什么呢?我们明明只配置了两个变体呀?在这需要提到Build Type配置,这个选项卡下默认有两个类型,分别是debug和release,变体包的数量正是(build type数量)*(flavor数量)。
    这里写图片描述
  • 当你选择完要编译的变体后,会发现未被选中的变体下会报找不到资源的错误,这是很正常的,当被选中之后就会变成正常的工程文件夹样式了,其这种报错也并不会影响我们进行批量打包。

五、常用gradlew命令

  首次运行命令行,没有gradle的要下载的哦,下载需要的时间根据网络状况而定。

1.八个常用命令:

  • ./gradlew -v //查看gradle版本
  • ./gradlew assembleDebug //编译并打出Debug版本的包
  • ./gradlew assembleRelease //编译并打出Release版本的包
  • ./gradlew build //执行检查并编译打包,打出所有Release和Debug的包
  • ./gradlew clean //删除build目录,会把app下面的build目录删掉
  • ./gradlew installDebug //编译打包并安装Debug版本的包
  • ./gradlew uninstallDebug //卸载Debug版本的包
  • ./gradlew -info //使用-info查看任务详情

2.命令行终端Terminal下执行打包命令

  我们直接利用./gradlew build打出所有的变体包,在Terminal终端下键入./gradlew build命令,需要等待大概20秒钟左右,当看到终端打印出BUILD SUCCESSFUL,说明打包任务执行完成,打开指定的apk生成目录(默认app/build/outputs/apk),就可以看到已经打出的apk。
这里写图片描述
  将两个release包都安装到同一台手机,可以发现能同时存在,并且都能正常打开运行。
这里写图片描述

3.关于打包需要注意的坑

  如果项目中存在使用Library Module,并且Library Module中manifest下的application也存在与app Module下的application相同的属性标签,那么会报manifest.xml合并错误的提示,如下。
这里写图片描述

  • 原因:这是因为AS的Gradle插件默认会启用Manifest Merger Tool,若Library项目中也定义了与主项目相同的属性(例如默认生成的android:icon和android:theme),则此时会合并失败,并报上面的错误。
  • 处理办法:在Manifest.xml的application标签下添加tools:replace=”android:icon, android:theme”(多个属性用,隔开,并且记住在manifest根标签上加入xmlns:tools=”http://schemas.android.com/tools”,否则会找不到namespace哦)。

注:本篇博客纯属个人笔记,如果错误之处,还望帮忙指正,谢谢!

以上是关于Android Studio下项目构建的Gradle配置及打包应用变体的主要内容,如果未能解决你的问题,请参考以下文章

Android Studio 中 gradle 版本问题

Android Studio下项目构建的Gradle配置及打包应用变体

Android Studio中checkout在git上面的项目后无法运行

Android NDK - 在没有Android Studio的情况下构建本机库

android studio节省C盘空间的配置方法

Android Studio 代码混淆(你真的会混淆吗)