Android之Gradle<第六篇>:Android Grad

2020-09-26  本文已影响0人  NoBugException

我们需要知道所以Android插件,其实就是利用Gradle构建的插件。

[Android Gradle 插件分类]

(1)App插件: com.android.application
(2)Library插件:com.android.library
(3)Test插件:com.android.test

[Android插件何依赖在哪里引用]

(1)Android插件引用如何引用

在Android项目根目录下的build.gradle下找到如下代码:

buildscript {

    repositories { // 下载插件用到的仓库
        google() // 仓库1
        jcenter() // 仓库2
        customName() // 仓库3
    }

    dependencies { // 引用各种插件
        // android Gradle 插件引用
        classpath 'com.android.tools.build:gradle:3.6.3'
        // 其它插件
        classpath 'xxx.xxx.xxx:1.0.1'
    }
}

具体说明请看注释。

(2)各Project下的依赖引用方式(这里的Project其实就是Module)

每个Project中都存在独立的build.gradle文件,随便选择一个Project,贴下如下代码:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

dependencies函数下定义了一系列的依赖,这些依赖也是从仓库中下载的,那么,在Andoid工程中,除了要引用Android插件的远程仓库之外,还需要引用各Project引用依赖的远程仓库。

在Android工程根目录下的build.gradle文件中,还存在这样的代码:

allprojects {
    repositories {
        google()
        jcenter()
    }
}

allprojects函数下引用了所有Project依赖的仓库。

[插件如何应用到某个Project中]

在某个Project中的build.gradle文件下应用插件,代码如下:

apply plugin: 'pluginID'

[包名]

通常情况下,应用的包名由applicationId指定,它在主Project的build.gradle文件下的
defaultConfig函数中被定义,applicationId是ProjectFlavor的一个属性,如图:

图片.png

如果不设置applicationId,那么应用的包名由AndroidManifest.xml文件中manifest标签下的package字段来指定,如图:

图片.png

[签名]

使用signingConfigs方法,完成签名配置,本文不做详细介绍。

[构建的应用类型]

应用的类型由buildTypes函数定义,默认情况下,有两种类型,分别是debugrelease,也就是说,即使没有buildTypes函数,Project也默认存在两种类型,当然,如果需要,还可以新增任意类型,如图新增了custom类型:

图片.png 图片.png

buildTypes常常和productFlavors一起使用,比如:

图片.png 图片.png

那么,在Build Variants中的显示就会变化,如图:

图片.png

productFlavors是Gradle非常重要的知识,当项目存在多个特性时,以前的做法是拉取其它分支,在某各固定的仓库分支中实现对应的特性,productFlavors的出现可以避免频繁的切换分支。
productFlavors将在下一章单独讲解。

在某类型中,可以使用如下属性:

[使用混淆]

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

看如上代码,默认情况下,minifyEnabled的值是false,即不启用混淆,想要使用混淆,这个参数必须设置为true;
其次,就是混淆文件了,可以使用proguardFile指定某个混淆文件,但只能指定一个文件;
使用proguardFiles可以指定多个文件,如上代码指定了两个文件,分别是“getDefaultProguardFile('proguard-android-optimize.txt')”和“proguard-rules.pro”,后者在项目中就可以找到,前者在Android SDK目录,具体路径是:\tools\proguard\proguard-android-optimize.txt,这个目录下还有一个混淆文件proguard-android.txtproguard-android.txt是优化前的,proguard-android-optimize.txt是优化后的。

[zipalign优化]

zipalign是Android提供的一个整理优化apk的工具,它能提高系统和应用的运行效率,更快地读写apk中的资源,降低内存的使用,所以对于要帆布的App,在发布之前一定要使用zipalign进行优化,具体使用如下:

图片.png

在Android工程中,只需要将zipAlignEnabled 属性设置为true即可。

[如何修改自动生成apk的名称]

图片.png

如图所示,Android工程默认打包的名称是app-debug.apk,这样的命名方式只能区分安装包是debug还是release,如果项目加入了多渠道打包的功能,举例如下:

//多渠道
productFlavors {
    xiaomi{
    }
    vivo{
    }
    oppo{
    }
}

显然,在多渠道打包的项目中,我们需要重新定制一个安装包的命名方式,这样才能区分安装包属于哪个渠道。

代码如下:

android.applicationVariants.all { variant ->
    variant.outputs.all {
        outputFileName = "${variant.name}_v${variant.versionName}.apk"
    }
}

最终生成apk的路径、名称见下图:

图片.png

[版本号的管理]

通常情况下,Android项目的版本号是在主模块下的build.gradle中定义的,代码如下:

defaultConfig {
    applicationId "com.example.m"
    minSdkVersion 19
    targetSdkVersion 29
    versionCode 1
    versionName "1.0.0"
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

versionCode:版本号
versionName:版本名称

对于用户而言,只有versionName可见,所以对用户而言,versionName就是版本号。

随着项目业务的迭代,模块越来越多,导致很难找到主Module;在组件化架构的项目中,任何Module都可能是一个主Module,每个组件都会定义版本号,如果需要修改版本号,需要修改所有组件中的版本,并且版本号保持一致,这种情况下,将版本号统一管理是必要的。
[方法一] 将版本号定义在根目录下的build.gradle文件中,添加一些列的版本信息

ext {
    minSdkVersion = 19
    targetSdkVersion = 29
    compileSdkVersion = 29
    buildToolsVersion = "29.0.3"
    versionCode = 1
    versionName = "1.0"
}

这些属性已经成为了公共信息,可以直接被调用。

在对应的Module中可以引用这些参数,代码如下:

compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion

defaultConfig {
    applicationId "com.example.m"
    minSdkVersion rootProject.ext.minSdkVersion
    targetSdkVersion rootProject.ext.targetSdkVersion
    versionCode rootProject.ext.versionCode
    versionName rootProject.ext.versionName
}

[方法二] 将配置信息单独写在一个文件中

在项目的根目录下,创建文件config.gradle,文件中的内容如下:

ext {
    appMinSdkVersion = 19
    appTargetSdkVersion = 29
    appCompileSdkVersion = 29
    appBuildToolsVersion = "29.0.3"
    appVersionCode = 1
    appVersionName = "1.0"
}

在Module下引用这个文件

apply from: '../config.gradle'

在Module下使用:

compileSdkVersion appCompileSdkVersion
buildToolsVersion appBuildToolsVersion

defaultConfig {
    applicationId "com.example.m"
    minSdkVersion appMinSdkVersion
    targetSdkVersion appTargetSdkVersion
    versionCode appVersionCode
    versionName appVersionName
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

[如何隐藏签名信息]

有时候,为了开发方便,我们会将App的打包签名会放在个人计算机本地,如果多人开发可能还会将签名放到项目中,其实这样是不对的。

签名的信息是非常重要的,需要进行保密,所以采用的方案是:将release的签名放在服务器,将debug的签名放在项目中,并且服务器的签名信息必须保密,代码如下:

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    signingConfigs {
        def appStoreFile = System.getenv("STORE_FILE")
        def appStorePassword = System.getenv("STORE_PASSWORD")
        def appKeyAlias = System.getenv("KEY_ALIAS")
        def appKeyPassword = System.getenv("KEY_PASSWORD")

        //当不能从环境变量里获取签名信息的时候,就使用项目中自带的debug签名
        if(!appStoreFile||!appStorePassword||!appKeyAlias||!appKeyPassword){
            appStoreFile = "debug.keystore"
            appStorePassword = "android"
            appKeyAlias = "androiddebugkey"
            appKeyPassword = "android"
        }
        release {
            storeFile file(appStoreFile)
            storePassword appStorePassword
            keyAlias appKeyAlias
            keyPassword appKeyPassword
        }
    }
}

[动态配置AndroidManifest文件]

在AndroidManifest中定义变量AMAP_KEY

<meta-data
    android:name="com.amap.api.v2.apikey"
    android:value="${AMAP_KEY}" />

然后在build.gradle中添加如下代码:

buildTypes {
    debug {
        manifestPlaceholders.put("AMAP_KEY", "111111111111111111")
    }
    release {
        manifestPlaceholders.put("AMAP_KEY", "2222222222222222222")
    }
}

Android Gradle提供了manifestPlaceholders占位符,在打包的过程中,可以替换AndroidManifest中的变量。

[自定义BuildConfig参数]

项目打包之后,在build文件夹中会自动生成BuildConfig配置文件,如下:

图片.png

这些属性都是些项目中的获取的,所以我们可以使用那些属性,如下图:

图片.png

但是,如果可以动态的在BuildConfig文件中添加属性就更加灵活了,使用Android Gradle的buildConfigField字段可以动态的在BuildConfig中添加属性,代码如下:

//多渠道
productFlavors {
    xiaomi{
        buildConfigField 'String', 'URL', '"https://www.xiaomi.com"'
        buildConfigField 'String', 'USERNAME', '"xiaomi"'
    }

    vivo{
        buildConfigField 'String', 'URL', '"https://www.vivo.com"'
        buildConfigField 'String', 'USERNAME', '"vivo"'
    }

    oppo{
        buildConfigField 'String', 'URL', '"https://www.oppo.com"'
        buildConfigField 'String', 'USERNAME', '"oppo"'
    }
}

根据不同的Flavor,BuildConfig属性值也会不同,最新生成的BuildConfig属性如下(就拿xiaomi举例):

图片.png

[自定义resValue]

在Android工程的res/values文件夹下定义了各种资源,然而Android Gradle可以做到动态自定资源,看下代码:

productFlavors {
    xiaomi{
        resValue 'string', 'who', 'xiaomi'
    }

    vivo{
        resValue 'string', 'who', 'vivo'
    }

    oppo{
        resValue 'string', 'who', 'oppo'
    }
}

编译之后,我们可以在/build/generated/res/resValues/xiaomi/debug/values/gradleResValues.xml文件下找到对应的资源

图片.png

Android Gradle的resValue方法,后面传递三个参数,分别是:Type、Name、Value,正好与资源定义格式对应,根据类似的做法,resValue还可以定义其它类型的资源。

[dex选项配置]

dexOptions {
    incremental true // 是否启动dx的增量模式,默认为false。增量模式速度更快,但目前有很多限制,慎用
    javaMaxHeapSize '4g' // 执行dx命令时,分配的最大堆内存,主要是为了解决执行dx内存不够的问题
    jumboMode true // 是否开启jumbo模式,如果函数超过65535个,那么就需要强制开启jumbo模式才可以构建成功
    preDexLibraries true // 是否预执行dex Libraries库工程,开启后会大大提高增量构建的速度,但是会影响clean构建的速度,默认开启。如果使用dx的--multi-dex选项生成多个dex,需要设置为false
    threadCount 2 // 执行dx使用的线程数量
}

[函数突破65535个的解决方案]

当Android工程中的函数总数超过65535个时,打包构建会失败,这时需要dex分包处理,解决方案如下:

【第一步】 开启MultiDex

图片.png

【第二步】 Android 5.0只有一个Dex,为了兼容5.0,还需要做下一步处理

集成MultiDexApplication

import androidx.multidex.MultiDexApplication;

public class MyApplication extends MultiDexApplication {


}

或者添加MultiDex.install(this);

import androidx.multidex.MultiDex;

public class MyApplication extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(this);
    }

}

[本章完...]

上一篇下一篇

猜你喜欢

热点阅读