Gradle构建系统

Gradle是什么?

Android Studio 基于 Gradle 构建系统,并通过 Android Gradle 插件提供更多面向 Android 的功能

Gradle 和 Android 插件独立于 Android Studio 运行。这意味着,您可以在 Android Studio 内、使用计算机上的命令行工具或在未安装 Android Studio 的计算机(例如持续性集成服务器)上构建 Android 应用。如果您不使用 Android Studio,可以学习如何从命令行构建和运行您的应用。无论您是从命令行、在远程计算机上还是使用 Android Studio 构建项目,构建的输出都相同。

Gradle与Android项目相关的文件

Gradle 设置文件 —— settings.gradle

Gradle构建系统_第1张图片
setting.gradle.png

顶级构建文件 —— build.gradle

Gradle构建系统_第2张图片
build.gradle(project).png

模块级构建文件 —— build.gradle

模块级 build.gradle 文件位于每个 // 目录

apply plugin: 'com.android.application'

/**
 *  android {}块配置所有Android特定的构建选项
 */

android {

  /**
   * compileSdkVersion指定Gradle应该使用的Android API级别
   * 编译您的应用程序。 这意味着您的应用可以使用的API等于
   * 或低于这个API。
   *
   * buildToolsVersion指定SDK构建工具的版本,命令行
   * 以及Gradle用来构建应用程序的编译器。 你需要
   * 使用SDK管理器下载构建工具。
   */

  compileSdkVersion 25
  buildToolsVersion "25.0.0"

  /**
   * defaultConfig {}块封装了所有构建variants的默认设置,
   * 并可以覆盖AndroidManifest.xml中的某些属性例如包名、
   * minSdkVersion 、versionCode扥
   */

  defaultConfig {

    /**
     * applicationId唯一,等同于AndroidManifest.xml文件中的package属性。
     */

    applicationId 'com.example.myapp'

    // 最小API版本
    minSdkVersion 15

    // 项目测试运行的版本
    targetSdkVersion 25

    // 版本号
    versionCode 1

    // 在app中显示的版本名称
    versionName "1.0"
  }

  /**
   * buildTypes {}块可以配置多个构建类型。
   * 默认情况下,构建系统定义了两种构建类型:debug和release。 
   */

  buildTypes {

    /**
     * 默认情况下,Android Studio会配置发布版本类型以启用缩减
     * 代码,使用minifyEnabled,并指定Proguard设置文件。
     */

    release {
        minifyEnabled true // 启用缩减代码
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
  }

  /**
   * productFlavors {}块是您可以配置不同定制的产品,例如免费和付费
   */

  productFlavors {
    free {
      applicationId 'com.example.myapp.free'
    }

    paid {
      applicationId 'com.example.myapp.paid'
    }
  }

  /**
   * split {}块中配置不同的APK版本,用于分包
   */

  splits {
    // Screen density split settings
    density {

      // 启用或禁用密度分割机制
      enable false

      // 不包含以下密度
      exclude "ldpi", "tvdpi", "xxxhdpi", "400dpi", "560dpi"
    }
  }
}

/**
 * dependencies {}块指定模块需要添加的依赖
 */

dependencies {
    compile project(":lib")
    compile 'com.android.support:appcompat-v7:25.1.0'
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

gradle.properties

您可以在其中配置项目范围 Gradle 设置,例如 Gradle 后台进程的最大堆大小。如需了解详细信息,请参阅构建环境。

local.properties

为构建系统配置本地环境属性,例如 SDK 安装路径。由于该文件的内容由 Android Studio 自动生成并且专用于本地开发者环境,因此您不应手动修改该文件,或将其纳入版本控制

:由于 Gradle 和 Android 插件独立于 Android Studio 运行,您需要单独更新构建工具。请阅读版本说明,了解如何更新 Gradle 和 Android 插件。

Gradle可以做什么?

  • 自定义、配置和扩展构建流程。
  • 使用相同的项目和模块为您的应用创建多个具有不同功能的 APK。
  • 在不同源代码集中重复使用代码和资源。

Gradle如何构建APK?

Gradle构建系统_第3张图片
build-process.png

如图所示,典型 Android 应用模块的构建流程通常依循下列步骤:

  1. 编译器将您的源代码转换成 DEX(Dalvik Executable) 文件(其中包括运行在 Android 设备上的字节码),将所有其他内容转换成已编译资源。
  2. APK 打包器将 DEX 文件和已编译资源合并成单个 APK。不过,必须先签名,才能将应用安装并部署到 Android 设备上。
  3. APK 打包器使用调试或发布密钥签名:
  • 如果您构建的是调试版本的应用(即专用于测试和分析的应用),打包器会使用调试密钥库签名。Android Studio 自动使用调试密钥配置新项目。
  • 如果您构建的是打算向外发布的发布版本应用,打包器会使用发布密钥签名。要创建发布密钥,请阅读在 Android Studio 中签名。
  1. 在生成最终 APK 之前,打包器会使用 zipalign 工具对应用进行优化,减少其在设备上运行时的内存占用。

构建流程结束时,您将获得可用来进行部署、测试的调试 APK,或者可用来发布给外部用户的发布 APK。

如何配置不同构建类型?

您必须至少定义一个构建类型才能构建应用——Android Studio 默认情况下会创建调试和发布构建类型。尽管调试构建类型不会出现在构建配置文件中,Android Studio 会将其配置为 debuggable true
。要开始为应用自定义打包设置,请学习如何配置构建类型。

 buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        debug {
            applicationIdSuffix ".amy"
            versionNameSuffix ".amy"
        }

        /**
         * The 'initWith' property allows you to copy configurations from other build types,
         * so you don't have to configure one from the beginning. You can then configure
         * just the settings you want to change. The following line initializes
         * 'jnidebug' using the debug build type, and changes only the
         * applicationIdSuffix and versionNameSuffix settings.
         */

        jnidebug {

            // This copies the debuggable attribute and debug signing configurations.
            initWith debug

            applicationIdSuffix ".jnidebug"
            jniDebuggable true
        }
    }

我们现在设置了debug构建类型的applicationIdSuffix属性和versionNameSuffix属性,那么如何验证app的debug版本的包名和版本名改变了呢?

这里我们就需要用到aapt(Android Asset Packaging Tool)了,我们可以在..\sdk\build-tools[sdk version(例如我的电脑里面是23.0.2)]目录下找到该工具,然后将之前打包的app-debug.apk文件复制到该目录下,然后在命令行中使用如下命令:

D:\Users\Administrator\AppData\Local\Android\sdk\build-tools\23.0.2>aapt dump ba
dging app-debug.apk

可以得到以下输出:

package: name='mylibrary.zero.com.customview.jmy' 
versionCode='1' 
versionName='1
.0.jmy' 
platformBuildVersionName=''
sdkVersion:'17'
targetSdkVersion:'24'

从上面的输出可以看到package和versionName的后缀都多了 .jmy

如需详细了解对于构建类型可以配置的所有属性,请阅读构建类型 DSL 参考

如何发布不同版本的应用?

productFlavors代表您可以发布给用户的不同应用版本,例如免费和付费的应用版本。您可以将productFlavors自定义为使用不同的代码和资源,同时对所有应用版本共有的部分加以共享和重复利用。productFlavors是可选项,并且您必须手动创建。要开始创建不同的应用版本,请学习如何配置productFlavors。

defaultConfig实际上属于 ProductFlavor 类。这意味着,您可以在 defaultConfig {}代码块中提供所有ProductFlavor的基本配置,例如 applicationId

:您仍需在清单文件中使用 package 属性指定程序包名称。这样,您可以使用 applicationId为每个productFlavors分配一个唯一的 ID,这样就可以在不更改您的源代码的情况下打包和分发。

:要利用 Google Play 中的多 APK 支持分发您的应用,请为所有Variant分配相同的 applicationId值并为每个Variant分配一个不同的 versionCode。要在 Google Play 中以独立应用的形式分发应用的不同Variant,您需要为每个Variant分配一个不同的 applicationId。

manifestPlaceholders

如果您需要将变量插入到您的build.gradle文件中定义的AndroidManifest.xml文件中,则可以使用manifestPlaceholders属性。
build.gradle:

android {
    defaultConfig {
        manifestPlaceholders = [hostName:"www.example.com"]
    }
    ...
}

manifest :


    
    ...

默认情况下,构建工具还会在$ {applicationId}占位符中提供应用程序的应用程序ID。例如,如果您的build.gradle文件如下所示:

android {
    defaultConfig {
        applicationId "com.example.myapp"
    }
    productFlavors {
        free {
            applicationIdSuffix ".free"
        }
        pro {
            applicationIdSuffix ".pro"
        }
    }
}

manifest:


    
    ...

当你构建“free”product flavor时,manifest结果如下:


   
    ...

如果想发布不同版本例如debug、release、demo等,那么组织项目文件?

默认的app模块的build.gradle文件如下:

...
android {
    compileSdkVersion 24
    buildToolsVersion "24.0.2"
    defaultConfig {
        applicationId "mylibrary.zero.com.customview"
        minSdkVersion 17
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
...

Android Plugin for Gradle 提供了一项有用的 Gradle 任务,可向您展示如何针对每种构建类型、产品风味和构建变体组织您的文件:

  1. 点击 IDE 窗口右侧的 Gradle
  2. 导航至 MyApplication > Tasks > android 并双击 sourceSets
  3. 要查看报告,请单击 IDE 窗口底部的 Gradle Console

用上面的代码执行Gradle任务后得到的报告如下:

------------------------------------------------------------
Project :app
------------------------------------------------------------

androidTest
-----------
Compile configuration: androidTestCompile
build.gradle name: android.sourceSets.androidTest
Java sources: [app\src\androidTest\java]
Manifest file: app\src\androidTest\AndroidManifest.xml
Android resources: [app\src\androidTest\res]
Assets: [app\src\androidTest\assets]
AIDL sources: [app\src\androidTest\aidl]
RenderScript sources: [app\src\androidTest\rs]
JNI sources: [app\src\androidTest\jni]
JNI libraries: [app\src\androidTest\jniLibs]
Java-style resources: [app\src\androidTest\resources]

debug
-----
......

main
----
......

release
-------
......

test
----
Compile configuration: testCompile
build.gradle name: android.sourceSets.test
Java sources: [app\src\test\java]
Java-style resources: [app\src\test\resources]

testDebug
---------
......

testRelease
-----------
......

而当我们增加构建类型、productFlavors或构建vriant,则会增加对应的输出,例如我们添加productFlavors:

...
android {
    ...
    productFlavors{
        demo{
            applicationId "mylibrary.zero.com.customview.demo"
            versionName "1.0.demo"
        }
    }
}
...

执行Gradle任务后则增加了和demo对应的报告:

androidTestDemo
---------------
Compile configuration: androidTestDemoCompile
build.gradle name: android.sourceSets.androidTestDemo
Java sources: [app\src\androidTestDemo\java]
Manifest file: app\src\androidTestDemo\AndroidManifest.xml
Android resources: [app\src\androidTestDemo\res]
Assets: [app\src\androidTestDemo\assets]
AIDL sources: [app\src\androidTestDemo\aidl]
RenderScript sources: [app\src\androidTestDemo\rs]
JNI sources: [app\src\androidTestDemo\jni]
JNI libraries: [app\src\androidTestDemo\jniLibs]
Java-style resources: [app\src\androidTestDemo\resources]

demo
----
......

demoDebug
---------
......

demoRelease
-----------
......

testDemo
--------
Compile configuration: testDemoCompile
build.gradle name: android.sourceSets.testDemo
Java sources: [app\src\testDemo\java]
Java-style resources: [app\src\testDemo\resources]

testDemoDebug
-------------
......

testDemoRelease
---------------
......

根据报告,知道项目文件应该如何组织,那么如何创建文件呢?

  1. 在该** Project 窗格中,右键点击 src 目录并选择 New > XML > Values XML File **。
  2. 为 XML 文件输入名称或保留默认名称。
  3. 从** Target Source Set 旁边的下拉菜单中,选择 debug**。
  4. 点击** Finish**。
Gradle构建系统_第4张图片
debug-directories.png

打包时,Gradle要如何构建呢?

Gradle查找目录的优先级顺序

  1. src/demoDebug/ (构建variant源集)
  2. src/debug/ (构建类型源集)
  3. src/demo/ (productFlavors源集)
  4. src/main/ (主源集)
  5. library (依赖库)

Gradle构建规则

Java 类合并

集中编译 java/ 目录中的所有源代码以生成单一的输出

注:对于给定的构建变体,如果找到两个或两个以上定义同一 Java 类的源集目录,Gradle 就会引发一个构建错误。例如,在构建调试 APK 时,您不能同时定义 src/debug/Utility.java and src/main/Utility.java。这是因为 Gradle 会在构建过程中检查这两个目录并引发“duplicate class”错误。如果您需要针对不同的构建类型构建不同版本的 Utility.java,您可以让每个构建类型定义自己的文件版本,而不将其包含在 main/ 源集中。

清单文件合并

合并工具通过基于每个清单文件的优先级顺序地合并它们将所有清单文件组合成一个文件,如图:


Gradle构建系统_第5张图片
manifest-merger_2x.png
清单文件合并规则:
  1. 元素中的属性从不会合并在一起 - 只使用来自最高优先级清单的属性。
  2. 元素中的android:required属性使用OR合并,因此如果存在冲突,则应用“true”,并且始终包括一个清单所需的功能部件或库。
  3. 元素中的属性始终使用优先级更高的清单中的值,但以下情况除外:
  • 当优先级较低的清单具有更高的minSdkVersion值时,会发生错误,除非您应用 overrideLibrary
    合并规则。
    main的清单文件如下定义时,其他清单文件定义的minSdkVersion比main中的还低时就不会报错:

  
...
  • 当优先级较低的清单的targetSdkVersion值较低时,合并工具使用来自较高优先级清单的值,但它还会添加必要的系统权限,以确保导入的库继续正常工作(对于其中较高的Android版本已经增加了许可限制)。有关此行为的详细信息,请参阅 implicit system permissions。
  1. 元素永远不会在清单之间匹配。每个都被视为唯一,并添加到合并清单中的公共父元素。
  2. 对于属性之间的所有其他冲突,您将收到错误,并且必须通过在更高优先级的清单文件中添加特殊属性来指示合并工具如何解决它(请参阅merge rule markers)
Element Merge policy Match key
Merge android:name attribute
Merge android:name attribute
Merge There is only one per
Merge android:name attribute
Merge There is only one per
Merge There is only one per
Merge android:name attribute
Keep No matching; several declarations within the parent element are allowed
Merge children only There is only one per file
Merge android:name attribute
Merge There is only one per
Merge android:name attribute
Merge android:name attribute
Merge android:name attribute
Merge android:name attribute
Merge android:name attribute
Merge android:screenSize attribute
Merge android:name attribute
Merge android:name attribute
Merge There is only one per
Merge There is only one per
Merge android:name attribute (if not present, then the android:glEsVersion attribute)
Merge android:name attribute
Merge android:name attribute
Merge There is only one per
Custom elements Merge No matching; these are unknown to the merger tool so they are always included in the merged manifest
标记合并规则
node
  1. tools:node="merge",默认行为
    低优先级manifest:

    
        
        
    

高优先级manifest:


        
    

  1. tools:node="merge-only-attributes"
    低优先级manifest:

    
        
        
        
    

高优先级manifest:

 
  
  • tools:node="remove"
    低优先级manifest:
  • 
      
      
    
    

    高优先级manifest:

    
      
    
    

    合并后的manifest:

    
      
    
    
    1. tools:node="removeAll"
      低优先级manifest:
    
      
      
    
    

    高优先级manifest:

    
      
    
    

    合并后的manifest:

    
    
    
    1. tools:node="replace"
      低优先级manifest:
    
      
      
    
    

    高优先级manifest:

    
      
    
    

    合并后的manifest:

    
      
    
    
    1. tools:node="strict",如果优先级较低的清单只是包含一个额外的属性,都会构建失败(而默认行为将额外的属性添加到合并的清单中)。
      低优先级manifest:
    
        
            
            
        
    
    

    高优先级manifest:

     低优先级manifest: 
      
     
    
    

    高优先级manifest:

    
    

    合并后的manifest:

    
    
    1. tools:replace="attr, ..."
      低优先级manifest:
    
    

    高优先级manifest:

    
    

    合并后的manifest:

    
    
    1. tools:strict="attr, ...",当优先级较低的清单中的这些属性在较高优先级清单中不完全匹配时,则构建失败。
      低优先级manifest:
    
    
    

    高优先级manifest:

    
    
    如何查看清单文件合并冲突?
    Gradle构建系统_第6张图片
    manifest-merged-view_2x.png

    如果您想查看合并的完整日志,您可以在模块的build / outputs / logs /目录中找到名为manifest-merger-buildVariant-report.txt的日志文件

    values合并

    例如存在两个 strings.xml 文件,将按照上述列表中的相同顺序指定优先级。也就是说,在构建类型源集中的文件中定义的值将会替换产品风味中同一文件中定义的值,依此类推。

    res和asset合并

    如果两个或两个以上的源集中定义有同名资源,将按照上述列表中的相同顺序指定优先级。

    库模块依赖项合并

    Gradle 会为随库模块依赖项包含的资源和清单分配最低的优先级

    如何添加依赖?

    直接依赖项分类

    模块依赖项

     // Dependency on the "mylibrary" module from this project
        compile project(":mylibrary")
    

    远程依赖项

    // Remote binary dependency
        compile 'com.android.support:appcompat-v7:25.1.0'
    

    本地依赖项

     // Local binary dependency
        compile fileTree(dir: 'libs', include: ['*.jar'])
    

    如何生成依赖项树?

    demoDebug
    \--- com.android.support:appcompat-v7:24.2.1
         +--- com.android.support:support-v4:24.2.1
         |    +--- com.android.support:support-compat:24.2.1
         |    |    \--- LOCAL: internal_impl-24.2.1.jar
         |    +--- com.android.support:support-media-compat:24.2.1
         |    |    +--- LOCAL: internal_impl-24.2.1.jar
         |    |    \--- com.android.support:support-compat:24.2.1
         |    |         \--- LOCAL: internal_impl-24.2.1.jar
         |    +--- com.android.support:support-core-utils:24.2.1
         |    |    +--- LOCAL: internal_impl-24.2.1.jar
         |    |    \--- com.android.support:support-compat:24.2.1
         |    |         \--- LOCAL: internal_impl-24.2.1.jar
         |    +--- com.android.support:support-core-ui:24.2.1
         |    |    +--- LOCAL: internal_impl-24.2.1.jar
         |    |    \--- com.android.support:support-compat:24.2.1
         |    |         \--- LOCAL: internal_impl-24.2.1.jar
         |    \--- com.android.support:support-fragment:24.2.1
         |         +--- LOCAL: internal_impl-24.2.1.jar
         |         +--- com.android.support:support-compat:24.2.1
         |         |    \--- LOCAL: internal_impl-24.2.1.jar
         |         +--- com.android.support:support-media-compat:24.2.1
         |         |    +--- LOCAL: internal_impl-24.2.1.jar
         |         |    \--- com.android.support:support-compat:24.2.1
         |         |         \--- LOCAL: internal_impl-24.2.1.jar
         |         +--- com.android.support:support-core-ui:24.2.1
         |         |    +--- LOCAL: internal_impl-24.2.1.jar
         |         |    \--- com.android.support:support-compat:24.2.1
         |         |         \--- LOCAL: internal_impl-24.2.1.jar
         |         \--- com.android.support:support-core-utils:24.2.1
         |              +--- LOCAL: internal_impl-24.2.1.jar
         |              \--- com.android.support:support-compat:24.2.1
         |                   \--- LOCAL: internal_impl-24.2.1.jar
         +--- com.android.support:support-vector-drawable:24.2.1
         |    \--- com.android.support:support-compat:24.2.1
         |         \--- LOCAL: internal_impl-24.2.1.jar
         \--- com.android.support:animated-vector-drawable:24.2.1
              \--- com.android.support:support-vector-drawable:24.2.1
                   \--- com.android.support:support-compat:24.2.1
                        \--- LOCAL: internal_impl-24.2.1.jar
    
    demoDebugAndroidTest
    \--- com.android.support.test.espresso:espresso-core:2.2.2
         +--- com.android.support.test:rules:0.5
         |    \--- com.android.support.test:runner:0.5
         |         \--- com.android.support.test:exposed-instrumentation-api-publish:0.5
         +--- com.android.support.test:runner:0.5
         |    \--- com.android.support.test:exposed-instrumentation-api-publish:0.5
         \--- com.android.support.test.espresso:espresso-idling-resource:2.2.2
    
    demoDebugUnitTest
    No dependencies
    
    demoRelease
    ......
    
    demoReleaseUnitTest
    No dependencies
    
    1. 点击 IDE 窗口右侧的 Gradle
    2. 导航至 MyApplication > Tasks > android 并双击 androidDependencies
    3. 要查看报告,请单击 IDE 窗口底部的 Gradle Console

    如需了解在 Gradle 中管理依赖项的详细信息,请参阅《Gradle 用户指南》中的依赖项管理基础知识

    如何配置依赖项?

    compile

    Gradle 将此配置的依赖项添加到类路径和应用的 APK。这是默认配置。

    apk

    指定 Gradle 需要将其与应用的 APK 一起打包的运行时依赖项。您可以将此配置与 JAR 二进制依赖项一起使用,而不能与其他库模块依赖项或 AAR 二进制依赖项一起使用。

    provided

    指定 Gradle 不与应用的 APK 一起打包的编译时依赖项。如果运行时无需此依赖项,这将有助于缩减 APK 的大小。您可以将此配置与 JAR 二进制依赖项一起使用,而不能与其他库模块依赖项或 AAR 二进制依赖项一起使用。

    如何使用Gradle签名打包?

    1. 创建密钥
    2. 创建私钥
    3. 将签名配置添加到模块级 build.gradle 文件中:
    ...
    android {
        ...
        defaultConfig {...}
        signingConfigs {
            release {
                storeFile file("myreleasekey.keystore")
                storePassword "password"
                keyAlias "MyReleaseKey"
                keyPassword "password"
            }
        }
        buildTypes {
            release {
                ...
                signingConfig signingConfigs.release
            }
        }
    }
    

    注:将发布密钥和密钥库的密码放在构建文件中并不安全。作为替代方案,您可以将此构建文件配置为通过环境变量获取这些密码,或让构建流程提示您输入这些密码。

    如何以安全地方式获取密码?

    过环境变量获取这些密码

    storePassword System.getenv("KSTOREPWD")
    keyPassword System.getenv("KEYPWD")
    

    从命令行调用此构建时提示您输入这些密码

    storePassword System.console().readLine("\nKeystore password: ")
    keyPassword System.console().readLine("\nKey password: ")
    

    你可能感兴趣的:(Gradle构建系统)