美文网首页
《Android 群英传 神兵利器》第四章 与gradle的爱恨

《Android 群英传 神兵利器》第四章 与gradle的爱恨

作者: chenzhenlindx | 来源:发表于2018-03-30 13:30 被阅读68次
    1. Android Plugin DSL Reference
      Android Gradle DSL文档

    Gradle Task

    1. 要了解一个默认的Android工程有哪些Task,可以使用以下指令:
    gradlew task
    

    运行结果如下图所示。


    gradlew task
    1. 要查看各个Task的具体作用与各个Task之间的相互调用关系,可以使用以下指令。
    gradlew task --all
    

    运行结果如下图所示。


    gradlew task --all
    1. 更改项目结构
        sourceSets {
            main {
                java.srcDirs = ['src']
                res.srcDirs = ['res']
                assets.srcDirs = ['assets']
                jni.srcDirs = ['jni']
                jniLibs.srcDirs = ['libs']
                manifest.srcFile 'AndroidManifest.xml'
                //如下更加完整
                renderscript.srcDirs = ['src']
                aidl.srcDirs = ['res']
            }
        }
    

    自定义代码的目录结构

        sourceSets {
            main {
                res.srcDirs = ['src/main/res',
                               'src/main/res/layout/activity',
                               'src/main/res/layout/fragment']
            }
        }
    
    自定义目录
    PS:书上示例图目录结构不正确,在Android3.0.1上,必须在src/main/res/layout/activitysrc/main/res/layout/fragment目录下创建layout,资源文件才能被识别

    构建全局配置

    全局参数

    在项目的根目录下的build.gradle中,通过ext领域可以指定全局的配置信息,代码如下所示:

    ...
    ext {
        compileSdkVersion = 26
        buildToolsVersion = "26.0.2"
        minSdkVersion = 15
        targetSdkVersion = 22
        versionCode = 1
        versionName = "1.0"
    }
    
    引用配置

    配置好全局参数后,可以在每个module中使用这些配置

    android {
        compileSdkVersion rootProject.ext.compileSdkVersion
        ...
    }    
    

    另外,也可以把ext全局配置写在allprojects领域中,这样每个module中就可以直接引用申明的变量了。

    ...
    allprojects {
        repositories {
            google()
            jcenter()
        }
        ext {
            COMPILE_SDK_VERSION = 22
        }
    }
    ...
    

    引用

    android {
        //compileSdkVersion rootProject.ext.compileSdkVersion
        compileSdkVersion COMPILE_SDK_VERSION
        ...
    }    
    

    这样写的好处是可以将配置进行统一管理。但坏处是如果这样写的话,Gradle的版本更新通知检查机制就无效了。大部分时候,这种写法是利大于弊的。

    构建defaultConfig

    可以在脚本中写代码,以便动态控制编译过程。

        defaultConfig {
            applicationId "com.example.czl.gradle"
            minSdkVersion 15
            targetSdkVersion 26
            versionCode 1
            //versionName "1.0"
            versionName getCustomVersionName()
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
    

    在build.gradle脚本中,定义一个方法来动态获取生成的versionName ,代码如下:

    def getCustomVersionName(){
        return "1.0."+new Date().format("yyyy-MM-dd", TimeZone.getDefault())
    }
    

    这样就可以完全自定义,动态配置参数了,在java中引用versionName 查看编译效果:

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            TextView tv = findViewById(R.id.tv);
            try {
                tv.setText(getPackageManager().getPackageInfo(getPackageName(), 0).versionName);
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    
    动态生成versionName

    构建buildTypes

    构建类型基础

    通过构建不同的构建类型,从而生成不同类型的apk,可以帮助开发者完成很多事情。例如实现只有在debug类型下才开启的给你,如调试、Log等功能,以及为不同构建类型实现不同的参数配置,等等。
    gradle支持自定义创建的构造类型。例如,在脚本中增加一个czl类型,同时设置该类型的applicationIdSuffix参数为.czl,代码如下所示:

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

    生成的apk文件多了一个app-czl-unsigned.apk,这个就是自定义的buildType-czl类型。

    buildType
    applicationIdSuffix在构建类型的时候,可以指定applicationIdSuffix为默认的包名增加一个后缀。查看编译后的apk文件,可以看到包名
    image.png
    除了使用gradlew build指令完成整个build任务之外,当指定了自定义的构建类型时,还可以指定完成其中任何一个构建类型的构建任务,比如gradlew assembleDebuggradlew assembleRelease两个默认的构造类型。同理,系统也帮我们生成了gradlew assembleCzl的构建任务,单独运行整个Task就可以直接生成czl类型的构建任务。
    构建类型buildTypes 的继承

    当创建自定义的构建类型时,不仅仅可以完全创建一个新的类型,而且还可以通过继承一个已有的构建类型来创造新的构建类型。也就是继承的方式,代码如下:

        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
            czl.initWith(buildTypes.debug)
            czl{
                applicationIdSuffix ".czl"
            }
        }
    

    通过以上代码,czl构建类型就继承了默认的debug构建类型的配置,同时还添加了自定义配置。

    Android领域中的可选配置

    compileOptions

    设置Java的编译选项,通常可以指定Java的编译版本

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

    控制Lint的代码检查,在Lint Check的时候,编译会因为Lint的error而终止,通过设置这个选项,可以在Lint发生error的时候继续编译。

        lintOptions {
            abortOnError false
        }
    

    Lint的编译检查是一个耗时大户,在编译时可以去掉Lint以便提高编译速度。但是只有尽可能的修复Lint提示,才是最佳的开发策略。

    Gradle动态参数配置

    System.properties 方式

    首先,打开gradle.properties文件,添加以下配置。

    systemProp.keyAlias=czl
    systemProp.keyPassword=123456
    systemProp.storeFile=czl_key.jks
    systemProp.storePassword=123456
    

    在build.gradle中进行引用的时候,通过System.properties['KEY']获取这些参数

        signingConfigs {
            config {
                keyAlias System.properties['keyAlias']
                keyPassword System.properties['keyPassword']
                storeFile file(System.properties['storeFile'])
                storePassword System.properties['storePassword']
            }
        }
    
    Key\Value 方式

    除了使用System.properties方式获取自定义的配置参数之外,还可以使用Key\Value的方式来定义。在gradle.properties文件,添加以下配置。

    czl.keyAlias=czl
    czl.keyPassword=123456
    

    然后在build.gradle中进行引用

        signingConfigs {
            config {
    //            keyAlias System.properties['keyAlias']
    //            keyPassword System.properties['keyPassword']
                keyAlias project.properties['czl.keyAlias']
                keyPassword project.properties['czl.keyPassword']
                storeFile file(System.properties['storeFile'])
                storePassword System.properties['storePassword']
            }
        }
    

    通过project.properties[KEY]方法,就可以取出对应的Value。

    属性方式

    前面两种方式,均可以在命令行中设置参数,从而设置给编译指令。如果不需要再命令行中设置参数,那么直接写属性名,同样可以进行引用。在gradle.properties文件,添加以下配置。

    pkeyAlias=czl
    pkeyPassword=123456
    

    这样,在build.gradle脚本中就可以直接引用了

        signingConfigs {
            config {
    //            keyAlias System.properties['keyAlias']
    //            keyPassword System.properties['keyPassword']
    //            keyAlias project.properties['czl.keyAlias']
    //            keyPassword project.properties['czl.keyPassword']
                keyAlias pkeyAlias
                keyPassword pkeyPassword
                storeFile file(System.properties['storeFile'])
                storePassword System.properties['storePassword']
            }
        }
    
    系统参数

    Gradle内置了很多系统级别的参数,在使用中可以直接获取值。例如,在build.gradle脚本中,增加一个Task

    task printProperties << {
        println project
        println project.name
        println project.buildDir
        println project.rootDir
        println project.buildFile
        println project.version
        println name
        println buildDir
        println path
    }
    

    打印出一些gradle内置的系统变量gradlew app:printProperties

    > Task :app:printProperties
    project ':app'
    app
    C:\Users\czl\Documents\AndroidLearn\Gradle\app\build
    C:\Users\czl\Documents\AndroidLearn\Gradle
    C:\Users\czl\Documents\AndroidLearn\Gradle\app\build.gradle
    unspecified
    printProperties
    C:\Users\czl\Documents\AndroidLearn\Gradle\app\build
    :app:printProperties
    

    多渠道打包

    创建渠道占位符

    首先在AndroidManifest.xml文件的application节点下,创建如下所示的meta-data节点

            <meta-data
                android:name="PRODUCT"
                android:value="${CHANNEL_VALUE}" />
    

    其中"${CHANNEL_VALUE}就是要进行替换的渠道占位符

    配置Gradle脚本

    在项目的Gradle脚本的android领域中,添加定义的渠道名,使用manifestPlaceholders 指定要替换渠道占位符的值

        flavorDimensions("official")
        productFlavors {
            product1 {
                manifestPlaceholders = [CHANNEL_VALUE: "PRODUCT1"]
            }
            product2 {
                manifestPlaceholders = [CHANNEL_VALUE: "PRODUCT2"]
            }
            product3 {
                manifestPlaceholders = [CHANNEL_VALUE: "PRODUCT3"]
            }
        }
    

    manifestPlaceholders 的module传递,可以参考下文
    一个字符解决Gradle aar编译参数传递问题是怎样一种体验
    关于维度,可参考:
    Android Gradle(3)— FlavorDimensions,构建变体

    脚本优化

    对于上面的多渠道打包脚本,可以对脚本进行以下优化

        flavorDimensions("official")
        productFlavors {
            product1 {
    //            manifestPlaceholders = [CHANNEL_VALUE: "PRODUCT1"]
            }
            product2 {
    //            manifestPlaceholders = [CHANNEL_VALUE: "PRODUCT2"]
            }
            product3 {
    //            manifestPlaceholders = [CHANNEL_VALUE: "PRODUCT3"]
            }
        }
        productFlavors.all {
            flavor -> flavor.manifestPlaceholders = [CHANNEL_VALUE: name]
        }
    

    增加的 productFlavors.all领域对所有的productFlavors进行了遍历,并使用其name作为渠道名。这些name实际上就是product1 ,product2 ,product3。

    生成重命名包

    默认包文件名

    一般我们要求对包进行重命名,以满足运营需求

        applicationVariants.all {
            variant ->
                variant.outputs.all {
                    output ->
                        if (output.outputFile != null
                                && output.outputFile.name.endsWith(".apk")
                                && "release" == variant.buildType.name) {
                            outputFileName = "CZLApp_${variant.flavorName}_ver${variant.versionName}.apk"
                        }
                }
        }
    

    PS:书中output.outputFile = apkFile已失效。

    为不同版本添加不同代码

    如下是添加boolean类型的变量。

        buildTypes {
            release {
                buildConfigField "boolean", "testFlag", "true"
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
            czl.initWith(buildTypes.debug)
            czl {
                buildConfigField "boolean", "testFlag", "false"
                applicationIdSuffix ".czl"
                signingConfig signingConfigs.config
            }
        }
    

    查看BuildConfig.java

    public final class BuildConfig {
      public static final boolean DEBUG = Boolean.parseBoolean("true");
      public static final String APPLICATION_ID = "com.example.czl.gradle.czl";
      public static final String BUILD_TYPE = "czl";
      public static final String FLAVOR = "product1";
      public static final int VERSION_CODE = 1;
      public static final String VERSION_NAME = "1.0.2018-03-30";
      // Fields from build type: czl
      public static final boolean testFlag = false;
    }
    
    public final class BuildConfig {
      public static final boolean DEBUG = false;
      public static final String APPLICATION_ID = "com.example.czl.gradle";
      public static final String BUILD_TYPE = "release";
      public static final String FLAVOR = "product1";
      public static final int VERSION_CODE = 1;
      public static final String VERSION_NAME = "1.0.2018-03-30";
      // Fields from build type: release
      public static final boolean testFlag = true;
    }
    

    添加String类型的变量buildConfigField "String", "myname", '"czl"'
    在设置变量的时候,甚至可以继续使用变量,这个时候需要使用转义字符

            release {
                def param1="ddd"
                buildConfigField "boolean", "testFlag", "true"
                buildConfigField "String", "myname", '"czl"'
                buildConfigField "String", "param", "\"String.${param1}.String\""
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
    
    使用变量设置变量
    资源文件同样可以进行设置属性值resValue("string","app_name","AppRelease"),比如设置AppName
            release {
                resValue("string","app_name","AppRelease")
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
            czl.initWith(buildTypes.debug)
            czl {
                resValue("string","app_name","AppCzl")
                applicationIdSuffix ".czl"
                signingConfig signingConfigs.config
            }
    

    同时在defaultConfig领域添加默认设置

        defaultConfig {
            applicationId "com.example.czl.gradle"
            minSdkVersion 15
            targetSdkVersion 26
            versionCode 1
            versionName "1.0"
            resValue("string","app_name","App")
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
    

    注意:需要删掉string.xml的app_name才能编译通过。
    另外resValue配置的参数,不一定要替换代码中的占位,还可以直接增加变量到R文件中

    def buildTime() {
        return new Date().format("yyyy-MM-dd HH:mm:ss")
    }
    
        defaultConfig {
            applicationId "com.example.czl.gradle"
            minSdkVersion 15
            targetSdkVersion 26
            versionCode 1
            versionName "1.0"
            resValue("string", "app_name", "App")
            resValue("string", "build_time", buildTime())
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
    

    在Java中,直接引用R文件的变量build_time即可

    Log.d(TAG, "onCreate: " + getString(R.string.build_time));
    
    03-30 16:51:30.604 14146-14146/com.example.czl.gradle.czl D/MainActivity: onCreate: 2018-03-30 16:51:01
    

    相关文章

      网友评论

          本文标题:《Android 群英传 神兵利器》第四章 与gradle的爱恨

          本文链接:https://www.haomeiwen.com/subject/vhvbcftx.html