• 04、Android--Gradle基础


    Gradle

    Gradle是Android项目的主流编译工具,是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。

    编译周期

    在解析Gradle的编译过程中会涉及到Gradle非常重要的两个对象:ProjectTask

    每个项目编译至少有一个Project,其中一个build.gradle就代表一个Project,而每个Project又包含多个Task,其中Task又包含多个Action,而Action是一个代码块,里面包含需要被执行的代码,它们的包含关系如下:

    Project > Task > Action

    在编译过程中,Gradle会根据Build相关文件来聚合所有的Project和Task,并执行Task中的Action。

    所有的Task的执行都按照一定的逻辑顺序,这种逻辑称之为依赖逻辑

    编译过程分为三个阶段:

    初始化阶段:创建Project对象,如果有多个build.gradle则会创建多个Project。

    配置阶段:会执行所有的编译脚本,同时还会创建Project中的所有Task,为下个阶段做准备。

    执行阶段:Gradle会根据传入的参数决定执行这些Task,真正的Action执行代码就在这里。

    Gradle结构

    Android项目中的Gradle最基础的文件配置如下:

    MyApp
    ├─ build.gradle
    ├─ settings.gradle
    └─app
      └─ build.gradle

    一个项目包含一个settings.gradle、包括一个顶层的build.gradle文件,每个Module都有各自的build.gradle文件。

    setting.gradle

    setting.gradle中的定义的是哪些Module应该被加入到编译过程,对于单Module项目可以不用该文件。对于多Module项目则需要该文件来指示需要加载哪些Module,它的代码在初始化阶段就会被执行。

    include ':app'
    include ':Module1'
    include ':Module2'    
    rootProject.name = "MyApp"
    

    多个Module通过include标签进行标明,项目就会根据指定的Module进行加载操作。

    Project - build.gradle

    一般Android项目中包含两个build.gradle,Project最顶层的build.gradle文件的配置会被应用到所有项目中。

    // Top-level build file where you can add configuration options common to all sub-projects/modules.
    buildscript {
        repositories {
            google()
            jcenter()
        }
        dependencies {
            classpath "com.android.tools.build:gradle:4.1.1"
            // NOTE: Do not place your application dependencies here; they belong
            // in the individual module build.gradle files
        }
    }
    
    allprojects {
        repositories {
            google()
            jcenter()
        }
    }
    
    task clean(type: Delete) {
        delete rootProject.buildDir
    }
    

    buildscript{}定义了Android编译工具的类路径,其中repositories里是gradle脚本执行所需的依赖,分别是对应Maven和插件。

    allprojects{}中定义的属性会被应用到所有的Module中,但是要保证项目的独立性,不要在这里操作过多共有的配置。

    App - build.gradle

    每个Module都有单独的build.gradle,可以针对不同的Module进行配置,如果这里的配置和顶层的build.gradle相同的话,后者将会覆盖前者。

    plugins {
        id 'com.android.application'
    }
    
    android {
        compileSdkVersion 30
        buildToolsVersion "30.0.3"
    
        defaultConfig {
            applicationId "com.legend.demo"
            minSdkVersion 16
            targetSdkVersion 30
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        }
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
        
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation 'androidx.appcompat:appcompat:1.1.0'
        implementation 'com.google.android.material:material:1.1.0'
        ......
    }
    

    plugins{}指定了Android程序的Gradle插件,plugin中提供了Android编译、测试和打包的所有Task。在早期的Gradle版本中不是使用id的方式指定插件的,而是通过如下方式(两种都可以使用):

    apply plugin: 'com.android.application'

    android{}是编译文件中最大的代码块,关于Android所有的特殊配置都在这里。其中'defaultConfig{}'就是程序的默认配置,如果在AndroidMainfest.xml文件定义相同的属性的话,会以这里的为准。

    buildTypes{}定义了编译类型,针对不同的类型可以有不同的配置和编译命令。默认的有debug和release选项。

    buildTypes{
        // 发布类型
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        // 测试类型,给测试人员
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        // 本地类型,和后端联调使用
        local {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    

    增加这些配置后,会在Android Studio的Build Variants面板中看到debuglocal两个构建类型,点击运行即可构建。

    Gradle Wrapper

    Gradle不断的更新版本,新版本会对以往项目存在向后兼容的问题,所以Gradle Wrapper就应运而生。
    在Android Studio构建的项目中会自带gradle-wrapper.jar文件,它还拥有一个配置文件:Project/gradle/wrapper/gradle-wrapper.properties,它的内容如下所示:

    #Sun Jan 17 11:15:10 CST 2021
    distributionBase=GRADLE_USER_HOME
    distributionPath=wrapper/dists
    zipStoreBase=GRADLE_USER_HOME
    zipStorePath=wrapper/dists
    distributionUrl=https://services.gradle.org/distributions/gradle-6.5-bin.zip
    

    distributionUrl是要下载的gradle地址以及版本,gradle-wrapper会去wrapper/list目录下查找,如果没有对应版本的gradle采取下载,当然我们也可以手动下载Gradle版本放入相应目录即可。

    全路径为C:Users<user_name>.gradlewrapperdistsgradle-x.x-bin<url-hash>

    Gradle有三种类型的版本:

    1、gradle-xx-all.zip是完整版,包含了各种二进制文件,源代码文件,和离线的文档。

    2、gradle-xx-bin.zip是二进制版,只包含了二进制文件(可执行文件),没有文档和源代码。

    3、gradle-xx-src.zip是源码版,只包含了Gradle源代码,不能用来编译你的工程。

    如果是直接从eclipse 中的项目转换过来的,程序并不会自动创建wrapper脚本,我们需要手动创建。在命令行输入以下命令即可

    gradle wrapper --gradle-version 2.4
    

    它会自动构建相应的目录结构。

    Gradle 命令

    Gradle 会根据build 文件的配置生成不同的task,我们可以直接单独执行每一个task。

    gradlew tasks:列出项目中所有的task。
    

    以上命令可以在Android Studio的Terminal面板中使用,如果出现以下提示:

    '.gradlew' 不是内部或外部命令,也不是可运行的程序或批处理文件。

    出现这种情况则需要在Android Studio的Gradle面板下的build setup中执行wrapper即可解决。

    Gradle命令的基本语法如下所示:

    gradle [taskName...] [--option-name...]

    gradlew是包装器,其中./gradlew、gradle是通用的。

    命令待补充......

    Android tasks

    Gradle中有四个基本的task,Android继承它们并进行了自己的实现:

    assemble:对所有的 buildType 生成 apk 包。
    clean:移除所有的编译输出文件,比如apk
    check:执行lint检测编译。
    build:同时执行assemble和check命令

    在实际项目中会根据不同的配置,会对这些task 设置不同的依赖。比如 默认的assmeble会依赖 assembleDebug 和assembleRelease,如果直接执行assmeble,最后会编译debug和release 的所有版本出来。我们运行的许多命令除了会输出到命令行,还会在build文件夹下生产一份运行报告。

    BuildConfig

    我们可以通过BuildConfig.DEBUG来判断当前版本是否是debug版本,以此输出只有在debug环境下才会执行的操作。这个类是有gradle根据配置文件生成的。其中Gradle使用的是Groovy语言,它是一种JVM语言,所以即使语法不同,它们最终都会生成JVM字节码文件。

    在app/gradle.build中的android{} 下有一个buildTypes{}可以配置key-value的键值对,它的功能非常强大。

    比如我们可以为debug何release两种环境定义不同的服务器地址,比如:

    android {
    	......
        buildTypes{
            release {
                buildConfigField "String", "API_URL", ""http://10.0.0.1/api/""
                buildConfigField "Boolean", "LOG_HTTP_CALLS", "true"
                ......
            }
            debug {
                buildConfigField "String", "API_URL", ""http://legend.com/api/""
                buildConfigField "Boolean", "LOG_HTTP_CALLS", "false"
                ......
            }
        }
    	......
    }
    

    除此之外,我们还可以为不同编译类型来设置不同的资源文件,比如:

    android {
    	......
        buildTypes{
            release {
                resValue "string", "app_name", "Example"
                ......
            }
            debug {
                resValue "string", "app_name", "Example DEBUG"
                ......
            }
        }
    	......
    }
    

    然后在BuildConfig.java文件中会生成相应的静态变量,直接引用即可。

    public final class BuildConfig {
      public static final boolean DEBUG = Boolean.parseBoolean("true");
      public static final String APPLICATION_ID = "com.legend.demo";
      public static final String BUILD_TYPE = "debug";
      public static final int VERSION_CODE = 1;
      public static final String VERSION_NAME = "1.0";
      // Field from build type: debug
      public static final String API_URL = "http://legend.com/api/";
      // Field from build type: debug
      public static final Boolean LOG_HTTP_CALLS = false;
    }
    

    共享变量

    在Android开发过程中,一个项目会有多个Module,如果想保持所有的Module和主Module的配置保持相同的话,可以在这里配置,具体步骤如下:

    1、在Project的build.gradle中定义版本号的常量,使用ext{}包裹:

    ......
    // Define versions in a single place
    ext {
        compileSdkVersion = 30
        buildToolsVersion = "30.0.3"
        applicationId = "com.legend.demo"
        minSdkVersion = 16
        targetSdkVersion = 30
        versionCode = 1
        versionName = "1.0"
    }
    ......
    

    2、在app下的build.gradle中使用 $rootProject.xxx的方式引用即可。

    ......
    android {
        compileSdkVersion rootProject.compileSdkVersion
        buildToolsVersion rootProject.buildToolsVersion
        defaultConfig {
            applicationId rootProject.applicationId
            minSdkVersion rootProject.minSdkVersion
            targetSdkVersion rootProject.targetSdkVersion
            versionCode rootProject.versionCode
            versionName rootProject.versionName
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        }
    	......
    }    
    ......    
    

    Repositories

    Repositories 就是代码仓库,我们平时的添加的一些 dependency 就是从这里下载的,Gradle 支持三种类型的仓库:

    Maven、Ivy、以及一些静态文件或者文件夹

    gradle 支持多种Maven仓库,除了默认的jCenter()外,还可以添加国内镜像或公司的私有仓库:

    repositories {
        maven {
            url "http://repo.legend.com/maven"
        }
    }
    

    如果仓库存在密码,也可以同时传入用户名和密码

    repositories {
        maven {
            url "http://repo.legend.com/maven"
            credentials {
                username 'legend'
                password '123456'
            }
        }
    }
    

    我们还可以使用相对路径配置本地仓库,以此来引用项目中存在的静态文件夹作为本地仓库:

    repositories {
        flatDir {
            dirs "aars"
        }
    }
    

    如果Gradle下载依赖过慢的情况下我们可以在这里配置阿里云的镜像:

    buildscript {
        repositories {
            maven { url 'https://maven.aliyun.com/repository/google/' }
            maven { url 'https://maven.aliyun.com/repository/jcenter/'}
        }
        dependencies {
            classpath "com.android.tools.build:gradle:4.1.1"
            // NOTE: Do not place your application dependencies here; they belong
            // in the individual module build.gradle files
        }        
    }
    
    allprojects {
        repositories {
            maven { url 'https://maven.aliyun.com/repository/google/' }
            maven { url 'https://maven.aliyun.com/repository/jcenter/'}
        }
    }
    

    Dependencies

    我们在引用库的时候,每个库名称包含三个元素:组名:库名称:版本号,如下:

    dependencies {
        implementation 'androidx.appcompat:appcompat:1.1.0'
        implementation 'com.google.android.material:material:1.1.0'
    }
    

    如果我们要保证我们依赖的库始终处于最新状态,我们可以通过添加通配符的方式,比如:

    dependencies {
        implementation 'androidx.appcompat:appcompat:1.1.0.+'
        implementation 'com.google.android.material:material:1.1.0.+'
    }
    

    使用这种做法每次编译都会去网络请求查看新版本,导致编译慢之外,新版本也可能存在很多问题。

    依赖方式

    dependencies{}中库的引入有三种方式:compile、implementation、api,它们的区别如下所示:

    compile:gradle升级3.+版本后,原来的依赖方法compile替换成了implementation和api,其中api是用来替换compile的。

    implementation:当前Module的依赖,使用implementation指令的依赖不会传递。(只编译当前模块,构建速度快)

    api:等同于compile,是用来替代compile的方式,使用api指令的依赖会传递。(编译所有模块,构建速度慢)

    所谓的依赖传递的意思是:假如ModuleA依赖于ModuleB,而ModuleB依赖于ModuleC,那么ModuleA可以引用ModuleC中的依赖。

    本地依赖

    未完待续

  • 相关阅读:
    寒假学习日报20
    寒假学习日报19
    Centos firewalld开放端口
    Full GC回收详解
    JVM调优6大步骤
    JVM的方法区和永久带是什么关系?
    sql优化的几种方式
    sentinel-dashboard安装、运行(ubuntu)
    RocketMQ工作原理
    linux:nohup 不生成 nohup.out的方法
  • 原文地址:https://www.cnblogs.com/pengjingya/p/5504912.html
Copyright © 2020-2023  润新知