Android Gradle配置完整详细分析

2021年3月8日16:43:16 发表评论 1,429 次浏览

本文概述

前言

Android Gradle完整配置

之前我们讨论了Gradle和Groovy的相关内容,本文具体介绍在Android Studio中使用Gradle的详细配置,希望得到一个Android项目的普遍配置,旨在在每个Android项目中重用这个配置。若无意外,每次新建项目,我们只需要复制这份配置即可,毕竟这个gradle配置也不见得需要经常改动。

项目顶层build.gradle

项目顶层build.gradle

该项目用于全局配置,其中添加的配置对于当前项目及其子项目或模块都有效,默认由Android Studio自动生成,其中的配置包括:

  • 所有项目的脚本依赖配置,脚本的依赖使用buildscript配置,包括使用的仓库和依赖的插件。
  • 所有项目的仓库设置,用于项目本身的依赖来源,使用allprojects配置。
  • 一个简单的clean清理任务,用于删除构建生成的文件。

该build.gradle配置和解释如下:

// Top-level build file where you can add configuration options common to all sub-projects/modules.
// 全局脚本依赖配置, 包括仓库和依赖项
buildscript {
    // 仓库配置
    repositories {
        // google仓库
        google()
        // jcenter仓库
        jcenter()
    }
    // 脚本依赖项
    dependencies {
        // android gradle构建工具, 提供使用Android plugin DSL
        classpath "com.android.tools.build:gradle:4.1.2"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

// 全局配置所有项目的project
allprojects {
    // 全局仓库配置
    repositories {
        google()
        jcenter()
    }
}

// 项目清理任务
task clean(type: Delete) {
    delete rootProject.buildDir
}

接着我们根据DSL分析一下这些配置:

  • buildscript{}:Project的方法,配置此项目的构建脚本类路径。闭包的代理为ScriptHandler。Dependencies和repositories都是ScriptHandler的方法,dependencies方法闭包的代理为DependencyHandler。Repositories方法闭包的代理为RepositoryHandler。
  • allprojects{}:Project的方法,配置此项目及其每个子项目,相当于调用for-each对每个项目进行配置。其闭包代理为当前项目和子项目的Project对象。因为是Project对象,所以在里面又可以使用该对象做一些全局的配置。其中的dependencies和repositories是Porject对象的方法(和ScriptHandler不同)。
  • Clean任务:该任务是Ddelete类型的任务,任务闭包代理为Delete对象,delete为Delete对象的方法,用以添加需要删除的文件。

子项目/模块中的build.gradle

插件配置

插件配置使用plugins方法配置(也可以用apply,但推荐使用plugins),其中可以配置三项:

  • Id:插件的唯一ID。
  • Version:插件的版本号,有的插件需要强制指定。
  • Apply:是否应用该插件,默认为true。

下面是Android Studio默认生成的代码:

plugins {
    id 'com.android.application'
}

Android本身支持两种插件:com.android.application和com.android.library,前者用于构建Android可执行应用程序,最终产品为.apk文件,后者用于构建Android库最终产品为.aar文件,这类似于Java项目中的application和java-library。

依赖配置

配置项目依赖使用Project对象中的dependencies方法,传入闭包的代理为DependencyHandler,添加依赖一般使用implementation或testImplementation,依赖第三方库的完整写法为(map风格):

implementation group: "", name: "", version:""

group就是maven中的groupid,name就是maven中的artifactid,也可以合并起来写,这是一种常见的形式,例如(字符串风格):

implementation 'commons-lang:commons-lang:2.6'

如果项目依赖相关的本地jar包,则可以将这些jar包以文件组的形式传给implementation,例如:

implementation files('spring.jar', 'hibernate.jar', 'mybatis.jar')
implementation fileTree('libs')

files和fileTree都是project对象的方法,要注意的是,files是用来收集文件/文件夹的,它不负责往下继续遍历文件;fileTree传入一个目录,会将目录及其子目录下的所有文件遍历出来。

Android构建配置

Android{}闭包开始表示Android相关的构建,所有Android相关的配置都在这个闭包里面进行配置。

android支持以下配置:

  • aaptOptions{}:指定Android资源打包工具(AAPT)的选项。
  • adbOptions{}:指定ADB调试桥的选项,例如APK安装选项。
  • buildTypes{}:封装此项目的所有构建类型配置。
  • compileOptions{}:指定Java编译器选项,例如Java源代码和生成的字节码的语言级别。
  • dataBinding{}:指定数据绑定库的选项。
  • defaultConfig{}:指定Android插件应用于所有构建变体(所有渠道)的默认变量属性。
  • dexOptions{}:指定DEX工具的选项,例如启用库预索引。
  • externalNativeBuild{}:使用CMake或ndk-build配置外部本地构建,下面会讨论配置使用CMake编译C++。
  • jacoco{}:配置用于离线检测和覆盖率报告的JaCoCo版本。
  • lintOptions{}:为检测工具指定选项。
  • packagingOptions{}:指定选项和规则,决定哪些文件Android插件包到你的APK。
  • productFlavors{}:封装此项目的所有产品渠道配置,可实现多渠道配置。
  • signingConfigs{}:封装可以应用于BuildType和ProductFlavor配置的签名配置,要注意的是仅用这个配置是无法进行签名的,需要在buildTypes{}或productFlavors{}中指定。
  • sourceSets{}:封装所有变体的源集配置。
  • splits{}:指定用于构建多个APK或APK拆分的配置。
  • testOptions{}:指定Android插件应该如何运行本地和测试的选项。

签名信息配置:signingConfigs{}

signingConfigs {
    debug {
        storeFile file('test.jsk')
        storePassword '123456'
        keyAlias 'test'
         keyPassword '123456'
    }
    release {
        storeFile file('test.jsk')
        storePassword '123456'
        keyAlias 'test'
        keyPassword '123456'
    }
}

也可以在Project Structure中填写相关的签名信息,生成APK的方式有两种:

  • 使用Build->Build apk生成APK。
  • Build->Generate Signed APK填写签名信息生成Apk,另外,如果你还没有签名密钥,则可以在这里生成。
  • 使用gradle build/assemble/assembleDebug/assembleRelease命令也可以生成,只是要注意build是否需要运行单元测试。

另外,要注意的是:当使用gradle build或Build>Build apk生成APK的时候,仅仅使用signingConfigs{}配置签名,APK是不会被签名的。

还有一个问题是:有些商店必须要使用 .keystore文件来进行签名,这时需要转换一下,将jks转为keystore,直接用命令行,先生成.p12文件,用p12生成keystore。

keytool -importkeystore -srckeystore D:\test.jks -srcstoretype JKS -deststoretype PKCS12 -destkeystore test.p12

keytool -v -importkeystore -srckeystore D:\test.p12 -srcstoretype PKCS12 -destkeystore D:\test.keystore -deststoretype JKS

通过以下命令来验证签名信息:

keytool -v -list -keystore D:\test.keystore

但是我觉得不如直接使用keystore签名就是了,使用以下命令直接生成keystore签名,以后统一使用keystore签名就是了:

keytool -genkey -alias test -keypass 123456 -keyalg RSA -keysize 1024 -validity 1000 -keystore C:/Users/Administrator/test.keystore -storepass 123456

SDK和构建工具版本配置

compileSdkVersion 30
buildToolsVersion "30.0.3"

其中compileSdkVersion用于指定SDK的版本,类似JDK的版本。BuildToolsVersion用于指定Android项目的构建工具版本,其中SDK版本可以参考:https://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels,构建工具版本可以参考:https://developer.android.com/studio/releases/build-tools.html。

一般这两个用最新版本就行了,对于构建工具版本:当使用Android plugin 3.0.0或更高版本时,这个属性是可选的。默认情况下,插件使用的是您所使用的插件版本所需的构建工具的最低版本。

应用构建默认配置:defaultConfig{}

 // 默认配置, 用于所有渠道的默认值
    defaultConfig {
        // APP唯一包名
        applicationId "com.org.android.appname"
        // 最低兼容SDK版本
        minSdkVersion 16
        // 目标SDK版本
        targetSdkVersion 30
        // 版本号
        versionCode 1
        // 版本名称
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

这些配置作为应用所有渠道构建的默认值,多渠道使用productFlavors{}配置,你也可以在productFlavors{}中覆盖这些默认值。

以上配置是Android studio生成的默认配置,参考DefaultConfig获取更多配置。

应用构建类型:buildTypes{}

// 构建类型配置
    buildTypes {
        debug {
            shrinkResources false
            minifyEnabled false
            zipAlignEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.debug
            buildConfigField "Boolean", "DEBUG_MODE", 'true'
        }
        release {
            //是否优化zip
            zipAlignEnabled true
            // 移除无用的resource文件
            shrinkResources true
            //启用代码混淆
            minifyEnabled true
            //混淆规则配置文件
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            //指明签名文件位置
            signingConfig signingConfigs.release
            buildConfigField "Boolean", "DEBUG_MODE", 'false'
        }
    }

以上是一些常用配置,更多配置请参考BuildType。

在上面的配置中你可以看到,这里是使用signingConfig配置应用的签名的,使用的就是上面配置的签名信息,在这里配置的签名在打包的时候会被真正使用到。

源码集设置:sourceSets{}

    sourceSets {//目录指向配置
        main {
            jniLibs.srcDirs = ['libs']//指定lib库目录
        }
    }

注意,Android插件使用自己的源集实现,也就是说Android有自己的默认项目标准结构,建议遵循Android的规范。更多关于你可以在这个区块中配置的属性的信息,见AndroidSourceSet。

打包配置:packagingOptions{}

    packagingOptions{
        //pickFirsts做用是 当有重复文件时 打包会报错 这样配置会使用第一个匹配的文件打包进入apk
        // 表示当apk中有重复的META-INF目录下有重复的LICENSE文件时  只用第一个 这样打包就不会报错
        pickFirsts = ['META-INF/LICENSE']

        //merges何必 当出现重复文件时 合并重复的文件 然后打包入apk
        //这个是有默认值得 merges = [] 这样会把默默认值去掉  所以我们用下面这种方式 在默认值后添加
        merge 'META-INF/LICENSE'

        //这个是在同时使用butterknife、dagger2做的一个处理。同理,遇到类似的问题,只要根据gradle的提示,做类似处理即可。
        exclude 'META-INF/services/javax.annotation.processing.Processor'
    }

你可以看到,这个配置一般用于处理打包文件重复的问题,需要在这里实现相关的处理逻辑。

代码分析:lintOptions{}

    //程序在编译的时候会检查lint,有任何错误提示会停止build,我们可以关闭这个开关
    lintOptions {
        abortOnError false //即使报错也不会停止打包
        checkReleaseBuilds false  //打包release版本的时候进行检测
    }

多渠道配置:productFlavors{}

android {  
    productFlavors {
        wandoujia {}
        xiaomi {}
        _360 {}
       //...
    }  

    productFlavors.all { 
        flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] 
    }
}

另外,还要添加风味维度(什么鬼),什么意思呢?多渠道的意思是,同一个app使用不同的方式进行打包(不同包名,manifest标记信息),这是对应不同厂商的渠道(小米、华为、应用宝等)。而风味维度的意思是对同一个渠道再次细化,对应同一个厂商的不同机型,但是一般使用一个维度(厂商)就行了。

android {
    flavorDimensions "color"
}

另外要注意的是:上面的渠道配置基本足够了,建议不要重新覆盖默认值的包名,如果多个渠道同一个app包名不同,提交应用可能会造成冲突。

多渠道需要结合统计才行的,否则没多大意义,例如使用友盟统计,在统计配置里可以添加渠道信息,这时就引用了上面配置的不同渠道信息。在清单文件的application下添加:

<meta-data android:value="000" android:name="UMENG_APPKEY"/>
<!--添加渠道号  这里使用的${UMENG_CHANNEL_VALUE}  如果是豌豆荚平台 这里就写成豌豆荚  如果是应用宝就写成应用宝-->
<meta-data android:value="${UMENG_CHANNEL_VALUE}" android:name="UMENG_CHANNEL"/>

Android Gradle完整配置

下面是Android Studio项目的完整gradle配置,但这是一般的Android项目。Android开发还有一种特殊的文件:JNI开发,引用C/C++动态库或静态库等,接下来我们就讨论这个问题。

plugins {
    // Android项目构建插件
    id 'com.android.application'
}

// Android配置
android {
    // 签名信息配置: debug + release
    signingConfigs {
        debug {
            storeFile file('C:/Users/Administrator/test.keystore')
            storePassword '123456'
            keyAlias 'test'
            keyPassword '123456'
        }
        release {
            storeFile file('C:/Users/Administrator/test.keystore')
            storePassword '123456'
            keyAlias 'test'
            keyPassword '123456'
            v1SigningEnabled true
            v2SigningEnabled true
        }
    }

    // 使用的SDK版本
    compileSdkVersion 30
    // 使用的构建工具版本
    buildToolsVersion "30.0.3"

    // 默认配置, 用于所有渠道的默认值
    defaultConfig {
        // APP唯一包名
        applicationId "com.id"
        // 最低兼容SDK版本
        minSdkVersion 16
        // 目标SDK版本
        targetSdkVersion 30
        // 版本号
        versionCode 1
        // 版本名称
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    // 构建类型配置
    buildTypes {
        debug {
            shrinkResources false
            minifyEnabled false
            zipAlignEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.debug
            buildConfigField "Boolean", "DEBUG_MODE", 'true'
        }
        release {
            //是否优化zip
            zipAlignEnabled true
            // 移除无用的resource文件
            shrinkResources true
            //启用代码混淆
            minifyEnabled true
            //混淆规则配置文件
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            //指明签名文件位置
            signingConfig signingConfigs.release
            buildConfigField "Boolean", "DEBUG_MODE", 'false'
        }
    }

    sourceSets {//目录指向配置
        main {
            jniLibs.srcDirs = ['libs']//指定lib库目录
        }
    }

    packagingOptions{
        //pickFirsts做用是 当有重复文件时 打包会报错 这样配置会使用第一个匹配的文件打包进入apk
        // 表示当apk中有重复的META-INF目录下有重复的LICENSE文件时  只用第一个 这样打包就不会报错
        pickFirsts = ['META-INF/LICENSE']

        //merges何必 当出现重复文件时 合并重复的文件 然后打包入apk
        //这个是有默认值得 merges = [] 这样会把默默认值去掉  所以我们用下面这种方式 在默认值后添加
        merge 'META-INF/LICENSE'

        //这个是在同时使用butterknife、dagger2做的一个处理。同理,遇到类似的问题,只要根据gradle的提示,做类似处理即可。
        exclude 'META-INF/services/javax.annotation.processing.Processor'
    }

    //程序在编译的时候会检查lint,有任何错误提示会停止build,我们可以关闭这个开关
    lintOptions {
        abortOnError false //即使报错也不会停止打包
        checkReleaseBuilds false  //打包release版本的时候进行检测
    }

    flavorDimensions "color"
    productFlavors {
        wandoujia {}
        xiaomi {}
        _360 {}
        //...
    }

    productFlavors.all {
        flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'com.google.android.material:material:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.navigation:navigation-fragment:2.2.2'
    implementation 'androidx.navigation:navigation-ui:2.2.2'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

Android JNI开发和Gradle配置

Android JNI开发和Gradle配置

在Android开发中,使用C/C++的整体思路是:

  • 根据指定的native函数生成头文件,Java调用的native函数有指定的格式,所以要使用javah生成。
  • 编译Android平台的C/C++代码需要使用NDK
  • 构建C/C++代码有两种方式,一种是ndk原生方式,另一种是使用CMake,这里仅介绍CMake的构建方式。
  • C/C++代码一般保存在main/cpp中,cpp目录作为源码目录,所有C/C++代码都放在这里(你也可以尝试放在其它地方,但要注意配置,但是这里的目录结果是推荐的结构)。
  • CMake的构建脚本CMakeLists.txt放在main/cpp中。

JNI在Gradle中的配置有两个地方,首先是在defaultConfig{}中配置externalNativeBui{},例如:

  defaultConfig {
    // 这个块不同于你用来链接Gradle到你的CMake或ndk-build脚本的块。
    externalNativeBuild {
        // 对于ndk-build,请使用ndkBuild块。
        cmake {
            // 将可选参数传递给CMake。
            arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"

            // 设置一个标志来为C编译器启用格式化宏常量。
            cFlags "-D__STDC_FORMAT_MACROS"

            // 为c++编译器设置可选的标志。
            cppFlags "-fexceptions", "-frtti"

            // 指定Gradle应该构建的CMake项目中的库和可执行目标。
            targets "libexample-one", "my-executible-demo"
        }
    }
}

这里一般是用来配置编译或构建可选参数的,但是一般情况没有特别的需求,可以留空,所以这里的配置是可选的。

JNI构建的重要配置在android{}下的externalNativeBuild{}中,在这里可以配置你需要的cmake或ndk-build构建,例如使用cmake构建,主要是使用path属性指定CMakeLists.txt文件的相对路径(相对项目根目录),另一个属性是version,指定cmake的版本:

android {
  externalNativeBuild {
    cmake {
        path "src/main/cpp/CMakeLists.txt"
        version "3.10.2"
    }
  }
}

只要配置好以上信息,基本上编译和打包都是没问题的了,也不同担心so库打不进apk。

总结:Android开发的起点

以上是所有Android项目的一般性完全配置,不能说是全面性的配置,毕竟可能有些开发者还有更多的需求,需要编写更多的任务来完成,但是我这里的目的是得到一份基本的Gradle配置,保证每新建一个Android项目可以直接复制使用了,接着就是埋头实现需求去了。

因为我觉得在构建脚本上花费过多的时间是一个噩梦!——如果你的技术基础不足,你大概有解决不完的IDE相关的问题,简化这个问题,集中在需求上就行了,不然项目完成遥遥无期!

至于后面JNI相关的配置来自于Android自动生成的Native项目,若有可能可以直接新建Native项目,这样从默认配置开始开发就行了。

好了,到这里大概解决了Android项目的基本配置问题了,下面接着研究Java或Java Web的Maven完整配置。

木子山

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: