美文网首页Android01_必知必会
Android应用构建-BuildType ProductFla

Android应用构建-BuildType ProductFla

作者: MinicupSimon | 来源:发表于2019-07-02 09:34 被阅读0次
    个人原创,欢迎指正

    场景:

    用同一套核心代码来维护多个订制的app
    现开发出一套Android app, 在此app基础上,复制出另一个app, 两个app之间略微有一些不同之处。
    将不同环境的APP安装在同一设备中。

    使用Gradle构建的优点:

    提高效率,产品未成熟,包括需求上的修改及各种BUG,用一套代码来维护,可以减少不必要的重复劳动
    免除传统的重复修改环境,打包,安装繁琐的流程,期间漏改在所难免。而使用Gradle构建,只需选择环境-安装。快捷,方便,不出错。

    实现需求:

    • 两个APP, 用一套代码来维护,不同之处在于应用名称,图标,包名,部分跳转逻辑,部分页面,部分页面显示文字。

    • 三个环境: 开发, 测试, 生产, 每个环境的版本,版本号,图标不同

    • 命名规则:ProductFlavor_产品版本号_日期_BuildType.apk
      whgc_1.0.0_20190624_Dev.apk
      whgc_1.0.0_20190624_Test.apk
      whgc_1.0.0_20190624_Release.apk

    • 各环境APP可安装在同一手机

    环境

    AndroidStudio 3.4
    Gradle插件:3.4.0
    Gradle:5.1.1

    三个环境 实现步骤:

    • 新建项目
      app: build.gradle
    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 28
        defaultConfig {
            applicationId "com.minicup.test"
            minSdkVersion 19
            targetSdkVersion 28
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
    
        ...
    }
    
    • app: build目录结构


      image.png
    • 执行assemble任务

    • 任务执行后在build目录中生成相应文件


      image.png

    assemble任务会自动创建debug和release两个目录
    这里的debug和release目录对应build.gradle中buildTypes的配置

    可项目中buildTypes中明明只有release,并没有debug,原因是这里的release和debug是系统默认的两个构建类型,即使没有配置,系统依然会生成这两个目录
    修改一下build.gradle,将release配置也删除,再次运行assemble任务,发现同样生成了release和debug两个目录

    android {
        buildTypes {
        }
    }
    

    接下来在buildTypes中添加个dev 类型

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

    同步后首先看到变化如下:


    image.png
    image.png

    再次执行assemble任务:


    image.png

    以上符合预期。

    自此在buildTypes中,我们可以配置三种环境,解决关于三个环境的需求

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

    也可以这样配置

    android {
        ...
        buildTypes {
    //            常用配置:
    //            signingConfig,
    //            buildConfigField,
    //            versionNameSuffix
    //            multiDexEnabled,
    //            zipAlignEnabled,
    //            applicationIdSuffix
            debug{
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
    
            appDev{
                initWith(debug)
            }
    
            appTest{
                initWith(debug)
            }
    
            appRelease {
                initWith(debug)
            }
        }
    }
    

    assemble后的结果


    image.png

    我们也可以打开ProjectStructure,在这里配置所有可以配置的东西


    image.png

    assemble是构建所有buildTypes productFlavors嵌套配置生成的目录。
    buildTypes中的配置命名不能以test开头, //BuildType names cannot start with 'test'
    除了debug,其他构建类型都是release类型的,因此,在没有设置签名配置时,会生成含有unsigned的标志。
    所有的module必须有同样的构建类型,即使是空的

    为三个环境指定签名文件,及不同的BASE_URL

    先生成jks文件,后配置build.gradle

    android {
        signingConfigs {
            release {
                storeFile file('app.jks')
                storePassword '123456'
                keyAlias = 'app'
                keyPassword '123456'
            }
        }
       
        ...
    
        buildTypes {
            debug{
                buildConfigField "String", "BASE_URL", "\"http://192.168.0.1:8080/debug\""
            }
    
            appDev{
                buildConfigField "String", "BASE_URL", "\"http://192.168.0.1:8080/appDev\""
                signingConfig signingConfigs.release
            }
    
            appTest{
                buildConfigField "String", "BASE_URL", "\"http://192.168.0.1:8080/appDev\""
                signingConfig signingConfigs.release
            }
    
            appRelease {
                buildConfigField "String", "BASE_URL", "\"http://192.168.0.1:8080/appRelease\""
                signingConfig signingConfigs.release
            }
        }
    }
    

    编译后,生成相应的BuildConfig,里面的内容与我们配置的一致


    image.png

    运行APP


    image.png

    EventLog

    11:07   Executing tasks: [:app:assembleDebug]
    11:07   Gradle build finished in 4 s 175 ms
    11:07   Install successful
    

    默认运行app时,执行的是assembleDebug任务。
    通过设置BuildVariants来切换任务。


    image.png

    切换为appDev构建类型后运行app的效果:


    image.png

    EventLog

    11:19   Executing tasks: [:app:assembleAppDev]
    11:19   Gradle build finished in 6 s 488 ms
    11:19   Install successful
    

    对应的构建类型BuildConfig中生成了不同的值,运行 appTest, appRelease也得到相应的结果。

    但在运行APP时出现一个问题,也是上面我们需要解决的需求之一, 三种BuildTypes运行时,生成的APP会被替换掉,最后运行三种BuildTypes在手机中只生成一个APP。如何让开发,测试,生产环境的APP存在于同一个手机上。为了解决这个问题,我们先来看看如何为不同的环境指定不同的应用名称及图标,用以安装后展示效果

    为三个环境指定不同的应用名称,应用图标

    按照开发-测试-生产的顺序准备三张应用图标


    ic_launcher_dev.png ic_launcher_test.png ic_launcher_release.png

    strings.xml

    <resources>
        <string name="app_name">test</string>
        <string name="app_name_dev">DEV</string>
        <string name="app_name_test">TEST</string>
        <string name="app_name_release">RELEASE</string>
    </resources>
    
    android {
        ...
        buildTypes {
    
            debug{
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
                buildConfigField "String", "BASE_URL", "\"http://192.168.0.1:8080/debug\""
                manifestPlaceholders  = [
                        app_icon :"@mipmap/ic_launcher",
                        app_label : "@string/app_name"
                ]
            }
    
            release{
                buildConfigField "String", "BASE_URL", "\"http://192.168.0.1:8080/release\""
                signingConfig signingConfigs.release
                manifestPlaceholders  = [
                        app_icon :"@mipmap/ic_launcher",
                        app_label : "@string/app_name"
                ]
            }
    
            appDev{
                buildConfigField "String", "BASE_URL", "\"http://192.168.0.1:8080/appDev\""
                signingConfig signingConfigs.release
                manifestPlaceholders  = [
                        app_icon :"@mipmap/ic_launcher_dev",
                        app_label : "@string/app_name_dev"
                ]
            }
    
            appTest{
                buildConfigField "String", "BASE_URL", "\"http://192.168.0.1:8080/appTest\""
                signingConfig signingConfigs.release
                manifestPlaceholders  = [
                        app_icon :"@mipmap/ic_launcher_test",
                        app_label : "@string/app_name_test"
                ]
            }
    
            appRelease {
                buildConfigField "String", "BASE_URL", "\"http://192.168.0.1:8080/appRelease\""
                signingConfig signingConfigs.release
                manifestPlaceholders  = [
                        app_icon :"@mipmap/ic_launcher_release",
                        app_label : "@string/app_name_release"
                ]
            }
        }
    }
    
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.minicup.test">
    
        <application
            android:allowBackup="true"
            android:icon="${app_icon}"
            android:label="${app_label}"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    

    通过设置不同的BuildVariants获得的结果:

    BuildType.appDev BuildType.appTest BuildType.appRelease

    以上结果针对BuildType的应用名称,应用图标,及打开APP中的BASE_URL都一一对应。接下来让三种BuildType运行在一台机器上。

    三种不同的BuildType,安装在同一设备上。

    android {
        ...
        buildTypes {
             ...
            appDev{
                buildConfigField "String", "BASE_URL", "\"http://192.168.0.1:8080/appDev\""
                signingConfig signingConfigs.release
                manifestPlaceholders  = [
                        app_icon :"@mipmap/ic_launcher_dev",
                        app_label : "@string/app_name_dev"
                ]
                applicationIdSuffix = ".dev"
            }
    
            appTest{
                buildConfigField "String", "BASE_URL", "\"http://192.168.0.1:8080/appTest\""
                signingConfig signingConfigs.release
                manifestPlaceholders  = [
                        app_icon :"@mipmap/ic_launcher_test",
                        app_label : "@string/app_name_test"
                ]
                applicationIdSuffix = ".test"
            }
    
            appRelease {
                buildConfigField "String", "BASE_URL", "\"http://192.168.0.1:8080/appRelease\""
                signingConfig signingConfigs.release
                manifestPlaceholders  = [
                        app_icon :"@mipmap/ic_launcher_release",
                        app_label : "@string/app_name_release"
                ]
                applicationIdSuffix = ".release"
            }
        }
    }
    

    切换三种BuildType,安装APP到设备中:


    image.png

    实现一套代码维护两个应用,每个应用都有dev,test,release三个构建类型,两个应用的图标,名称,包名不同

    • app build.gradle 指定不同productFlavor下的包名,及不同buildType下的图标,名称及包名后缀
    apply plugin: 'com.android.application'
    
    android {
        signingConfigs {
            release {
                storeFile file('appDev.jks')
                storePassword '123456'
                keyAlias = 'appDev'
                keyPassword '123456'
            }
        }
        compileSdkVersion 28
    
        defaultConfig {
            minSdkVersion 19
            targetSdkVersion 28
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            flavorDimensions "default" 
        }
        buildTypes {
            appDev{
                buildConfigField "String", "BASE_URL", "\"http://192.168.0.1:8080/appDev\""
                signingConfig signingConfigs.release
                manifestPlaceholders  = [
                        app_icon :"@drawable/ic_launcher_dev",
                        app_label : "@string/app_name_dev"
                ]
                applicationIdSuffix = ".dev"
            }
    
            appTest{
                buildConfigField "String", "BASE_URL", "\"http://192.168.0.1:8080/appTest\""
                signingConfig signingConfigs.release
                manifestPlaceholders  = [
                        app_icon :"@drawable/ic_launcher_test",
                        app_label : "@string/app_name_test"
                ]
                applicationIdSuffix = ".test"
            }
    
            appRelease {
                buildConfigField "String", "BASE_URL", "\"http://192.168.0.1:8080/appRelease\""
                signingConfig signingConfigs.release
                manifestPlaceholders  = [
                        app_icon :"@drawable/ic_launcher_release",
                        app_label : "@string/app_name_release"
                ]
                applicationIdSuffix = ".release"
            }
        }
    
        productFlavors {
            orange {
                applicationId "com.minicup.orange"
            }
    
            green {
                applicationId "com.minicup.green"
            }
    
        }
    
        //去掉默认的debug及release两个buildType
        variantFilter { variant ->
            if(variant.buildType.name.equals('release') || variant.buildType.name.equals('debug')) {
                variant.setIgnore(true)
            }
        }
    }
    
    ...
    

    *创建以下的目录结构,不同productFlavor对应相应的图标,名称


    image.png

    green目录下的图标及名称:


    ic_launcher_dev.xml ic_launcher_test.xml ic_launcher_release.xml
    <resources>
        <string name="app_name_dev">DEV_G</string>
        <string name="app_name_test">TEST_G</string>
        <string name="app_name_release">RELEASE_G</string>
    </resources>
    

    orange目录下的图标及名称:


    ic_launcher_dev.xml ic_launcher_test.xml ic_launcher_release.xml
    <resources>
        <string name="app_name_dev">DEV_O</string>
        <string name="app_name_test">TEST_O</string>
        <string name="app_name_release">RELEASE_O</string>
    </resources>
    
    • MainActivity
    public class MainActivity extends AppCompatActivity {
        private TextView mTextView;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView = (TextView) findViewById(R.id.textView);
            mTextView.setText(BuildConfig.FLAVOR + " "+ BuildConfig.BUILD_TYPE);
        }
    }
    
    • 同步项目,查看BuildVariant, 生成了两个应用对应的构建类型 2(productFlavor)*3(buildType)=6(buildVariant)


      image.png
    • 执行assemble任务:


      image.png
    • 验证APP,将生成的6个APP安装到设备中


      image.png
    • 不同的productFlavor,不同的buildType生成的应用可安装到同一个设备
    • 同一个productFlavor的不同buildType,根据之前配置生成不同名称,不同图标,不同包名的app
    image.png image.png image.png image.png image.png image.png

    设置生成APP的文件名称

        productFlavors {
            orange {
                applicationId "com.minicup.orange"
                versionCode 2
                versionName "2.2"
            }
    
            green {
                applicationId "com.minicup.green"
                versionCode 3
                versionName "3.3"
            }
        }
    
    
        applicationVariants.all { variant ->
            variant.outputs.all { output ->
                    //产品名称_产品版本号_日期_软件环境
                    outputFileName  = "${variant.productFlavors[0].name}_" +
                            "v${variant.versionName}_" +
                            "${releaseTime()}_" +
                            "${variant.buildType.name}" +
                            ".apk"
            }
        }
    
    
    def releaseTime(){
        return new Date().format("yyyy-MM-dd")
    }
    

    执行assemble任务:

    image.png

    集成JPush时,遇到的问题

    按照以上方法配置JPush时,打好的包,不能同时安装在同一个设备中


    image.png

    目前只能准备两台设备,同一设备只能安装同一buildtype的应用

    参考:
    https://developer.android.google.cn/studio/build/index.html
    http://google.github.io/android-gradle-dsl/current/index.html

    相关文章

      网友评论

        本文标题:Android应用构建-BuildType ProductFla

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