一套代码打包多个项目这一篇就够了

最近在做项目制的开发,有一套开发好的产品原型的代码,然后根据不同的客户进行不同的定制开发。如果一个项目搞一个分支,要多开多个as,还有一个缺点就是如果产品原型的代码进行了变更和优化,所有的项目分支都要将产品修改后果的代码复制转移一次,麻烦!

build.gradle可以帮我们解决这个问题。

一.先来讲重点,配置变种:

1.在android { } 中增加如下代码,适用于as建立的项目,eclipse转换过来的项目需要增加sourceSets {}

    // 维度组合,productFlavors中不同种类的dimension可以组合,名字根据实际需要自己取

    flavorDimensions "mode", "channel"

    productFlavors {

        trunk {

            // 维度小组名称

            dimension "mode"

            // 修改项目包名,在清单文件中的包名后增加后缀;

            // 也可以使用applicationId "com.xx.xx" 替换掉默认的包名

            applicationIdSuffix ".trunk"

            // 编译后会自动生成在BuildConfig.java中,代码中通过使用BuildConfig.IPADRESS获取后面的值

            // 参数规则“String”表示参数类型,也可以是boolean等,"IPADRESS"是常量名称,最后一个是内容

            buildConfigField "String", "IPADRESS", "\"192.168.x.xx:8080\""

            // 还可以增加各种不同的配置,defaultConfig中配置这里都可以用,重复的配置这里的会覆盖掉defaultConfig

        }

        guiyang {

            dimension "mode"

            applicationIdSuffix ".guiyang"

            buildConfigField "String", "IPADRESS", "\"192.168.x.xx:8082\""

        }

        huawei{

            dimension "channel"

            // 给清单文件中的渠道占位符赋值

            manifestPlaceholders = [CHANNEL_VALUE:"huawei"]

        }

        xiaomi{

            dimension "channel"

            manifestPlaceholders = [CHANNEL_VALUE:"xiaomi"]

        }

2.在工程目录中新增如下目录

如果productFlavors中没有main:main为默认目录,每个组合都会含有main中的代码;

如果productFlavors中有main:main也相当于一个风味,不会和trunk合并(根据实际需要,trunk既可以是main的扩展也可以和main平级,完全看自己怎么配置)

trunk和guiyang(注意:文件夹名称要和productFlavors中的名字对应)中放各个定制项目中的差异代码,其下的目录结构文件非必须,如果需要修改main中的资源或新增代码,就在各自项目中增加对应的文件夹和文件,如果没有修改可以只有个主目录

trunk/guiyang和main中的文件合并规则如下:

图片、音频、 XML 类型的 Drawable ,layout等资源文件,将会进行文件级的覆盖

字符串、颜色值、整型等资源以及 AndroidManifest.xml ,将会进行元素级的覆盖

代码资源,同一个类, buildTypes 、 productFlavors 、 main 中只能存在一次,否则会有类重复的错误

覆盖等级为:buildTypes > productFlavors > main

3.配置后在as左下角Build Variants中可以看到如下组合,维度组合2*2,每个组合再分debug和release,共8种

选中哪一个就会编译哪一个,维度数量根据自己需要可以减少,也可以增加

4.依赖库也可以根据风味进行不同的依赖

编译用的第三方库。例如我们的app在某些情况下有地图功能,有些情况下没有,如果我们在代码里控制有没有这个功能那么它依赖的包还是会打入apk,这样就会造成大量无用的代码进入apk中。

现在不同的变种风味有不同的依赖,dependencies {}中可以这样写:

compile 'xxx'  // 所有的项目都依赖

trunkCompile 'yyy'  // 只有trunk项目依赖

trunkCompile 'zzz' // 只有trunk项目依赖

guiyangCompile 'yyy' // 只有guiyang项目依赖

implementation也可以这样用

5.eclipse转换过来的项目,需要多一步

sourceSets {

        main {

            manifest.srcFile 'AndroidManifest.xml'

            java.srcDirs = ['src']

            resources.srcDirs = ['src']

            aidl.srcDirs = ['src']

            renderscript.srcDirs = ['src']

            res.srcDirs = ['res']

            assets.srcDirs = ['assets']

            jniLibs.srcDirs = ['libs']

        }

        trunk {

            java.srcDirs = ['src-trunk']

            res.srcDirs = ['res-trunk']

        }

        guiyang{

            java.srcDirs = ['src-guiyang']

            res.srcDirs = ['res-guiyang']

        }

}

sourceSets中主要指定代码目录,因为eclipse的目录结构和as不一样,as自动识别main下面的代码,需要通过sourceSets将eclipse的目录和as的目录进行对应

as中如果有特殊需要,也可以通过这种方式指定目录

二.重点讲完了,下面记一下build.gradle中各个配置的说明

apply plugin: 'com.android.application'

android {

    compileSdkVersion 27

    // 默认配置,还有很多配置属性,参照gradle API

    // 相当于所有productFlavor的父类,自建productFlavor中没有的都使用这个里面的,有的就覆盖

    defaultConfig {

        applicationId "com.example.xxx.gradledemo"

        minSdkVersion 14

        targetSdkVersion 27

        versionCode 1

        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

    // 签名的配置

    // productFlavor的属性,在所有的productFlavor都可以使用,一般用在buildTypes中,覆盖登记最高

    signingConfigs {

        realse { // 这个名字随意取,在buildTypes中通过signingConfigs. 调用

            storeFile file("gradle_demo.jks")

            storePassword "xxxxxx"

            keyAlias "xxx"

            keyPassword "xxxxxx"

        }

    }

    // 相当于一个维度dimension(隐藏有一个debug),和productFlavors中的风味进行组合

    buildTypes {

        // 也是一个productFlavor

        // 一般增加这个风味是为了不同的签名需要,还有很多配置属性,参照gradle API

        // release,debug中的buildConfigField会覆盖productFlavors中同名字的

        release {

            signingConfig signingConfigs.realse

            buildConfigField "boolean", "ISSHOW", "false"

            // 是否减小apk体积,打开混淆

            minifyEnabled true

            // 混淆文件的配置文件,getDefaultProguardFile('proguard-android.txt')是默认的,在SDK的tools/proguard目录下可以找到

            // proguard-rules.pro是自定义

            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

        }


    }

    // 设置java的编译版本,通常是为了使用某些版本中的一些语言新特性

    // 一般不用设置

    /*compileOptions {

        sourceCompatibility JavaVersion.VERSION_1_8

        targetCompatibility JavaVersion.VERSION_1_8

    }*/

    // 编译时加速,lint检查有错误时继续打包,不影响使用,最好是把lint错误全部修改

    // 还有很多配置属性,参照gradle API

    /*lintOptions {

        abortOnError false // 如果发现错误,lint是否停止打包

    }*/

    // 维度组合,productFlavors中不同种类的dimension可以组合,名字根据实际需要自己取

    flavorDimensions "mode", "channel"

    productFlavors {

        // 一个productFlavor

        trunk {

            // 维度小组名称

            dimension "mode"

            // 修改项目包名,在清单文件中的包名后增加后缀;

            // 也可以使用applicationId "com.xx.xx" 替换掉默认的包名

            applicationIdSuffix ".trunk"

            // 编译后会自动生成在BuildConfig.java中,代码中通过使用BuildConfig.IPADRESS获取后面的值

            // 参数规则“String”表示参数类型,也可以是boolean等,"IPADRESS"是常量名称,最后一个是内容

            buildConfigField "String", "IPADRESS", "\"192.168.3.23:8080\""

            // 还可以增加各种不同的配置,defaultConfig中配置这里都可以用,重复的配置这里的会覆盖掉defaultConfig

        }

        guiyang {

            dimension "mode"

            applicationIdSuffix ".guiyang"

            buildConfigField "String", "IPADRESS", "\"192.168.8.47:8082\""

        }

        huawei{

            dimension "channel"

            // 给清单文件中的渠道占位符赋值

            manifestPlaceholders = [CHANNEL_VALUE:"huawei"]

        }

        xiaomi{

            dimension "channel"

            manifestPlaceholders = [CHANNEL_VALUE:"xiaomi"]

        }

    }

    /*sourceSets {

        trunk {

            res.srcDirs = ['src/trunk/res']

            manifest.srcFile 'src/trunk/AndroidManifest.xml'

        }

        guiyang {

            res.srcDirs = ['src/guiyang/res']

            manifest.srcFile 'src/guiyang/AndroidManifest.xml'

        }

    }*/

}

dependencies {

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

    implementation 'com.android.support:appcompat-v7:27.1.1'

    implementation 'com.android.support.constraint:constraint-layout:1.1.3'

    testImplementation 'junit:junit:4.12'

    androidTestImplementation 'com.android.support.test:runner:1.0.2'

    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

}

buildConfigField配置的数据在BuildConfig.java中,由as自动生成

在这个目录下面

三.build.gradle中的一些参数使用公用资源文件,主要是一些编译版本和jar包版本,modle和主工程都要用到的需要统一的

1.可以在根目录的build.gradle文件中增加ext{ },或者新建一个.gradle文件,然后引入到根目录的build.gradle

根目录下build.gradle 增加

ext {

    CompileSdkVersion = 27

    BuildToolsVersion = "27.0.3"

    MinSdkVersion = 14

    TargetSdkVersion = 27

}

主工程和modle的build.gradle中这样使用

android {

    compileSdkVersion rootProject.ext.compileSdkVersion

    buildToolsVersion rootProject.ext.buildToolsVersion

    defaultConfig {

        applicationId "com.example.xxx.gradledemo"

        minSdkVersion rootProject.ext.minSdkVersion

        targetSdkVersion rootProject.ext.targetSdkVersion

        versionCode 1

        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

或者在任意地方新建xxx.gradle,然后引入到根目录下build.gradle(在根目录中增加 apply from : 'xxx.gradle',需要根据xxx.gradle路径写)

ext{

    // 可以直接用

    /*compileSdkVersion = 27

    buildToolsVersion = "27.0.3"

    minSdkVersion = 14

    targetSdkVersion = 27*/

    // 也可以外面套一层

    android = [

            compileSdkVersion : 27,

            buildToolsVersion : "27.0.3",

            minSdkVersion : 14,

            targetSdkVersion : 27

    ]

    dependencies = [

            appcompatV7 : 'com.android.support:appcompat-v7:27.1.1',

            constraintVersion : 'com.android.support.constraint:constraint-layout:1.1.3'

    ]

}

主工程和modle的build.gradle中这样使用

android {

    compileSdkVersion rootProject.ext.android.compileSdkVersion

    buildToolsVersion rootProject.ext.android.buildToolsVersion

    defaultConfig {

        applicationId "com.example.zhangchi.gradledemo"

        minSdkVersion rootProject.ext.android.minSdkVersion

        targetSdkVersion rootProject.ext.android.targetSdkVersion

        versionCode 1

        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

    dependencies {

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

        implementation rootProject.ext.dependencies.appcompatV7

        implementation rootProject.ext.dependencies.constraintVersion

        testImplementation 'junit:junit:4.12'

    }

}

2.可以在gradle.properties中增加(一般放敏感信息,比如打包用的签名信息)

    在gradle.properties中添加敏感数据

#签名文件写实际路径

STOREFILE=../gradle_demo.jks 

STOREPASSWORD=xxxxxx

KEYALIAS=xxx

KEYPASSWORD=xxxxxx

主工程的build.gradle中这样使用

realse {

            storeFile file(STOREFILE)

            storePassword STOREPASSWORD

            keyAlias KEYALIAS

            keyPassword KEYPASSWORD

        }

四.gradle命令打包

1.Gradle配置(配不配置都行)

下载地址http://services.gradle.org/distributions/

下载你所需要的gradle版本,带all的,gradle-4.5-all.zip

下载后解压到你想要的目录

设置环境变量

新建系统变量:GRADLE_HOME,值为grdle路径

设置后测试是否配置成功

在as的Terminal工具栏中:

使用gradlew命令使用的是项目使用的gradle版本,使用gradle命令使用的是环境变量中配的gradle版本

多变种使用命令打包的命令:gradlew assembleGuiyangHuaweiRelease

gradle assemble维度组合名称(每个风味首字母大写)buildType下面的名字(首字母大写)

注:本篇博客纯属个人笔记,如果错误之处,还望帮忙指正,谢谢!

      如果想了解的更详细,可以读一本书籍《Android Gradle权威指南》

你可能感兴趣的:(一套代码打包多个项目这一篇就够了)