多渠道打包——同一台设备上同时安装你的正式与测试环境APP

我们的APP请求的服务器经常会区分正式环境和测试环境,正常情况下一台设备上不能安装重复applicationid的app,这样如果我们测试正式环境和测试环境的时候回经常要在正式版和测试版中反复卸载安装,这样很麻烦,不方便测试,我们会想要是能在同一台设备上都安装上测试和正式的app,那么就能比较愉快地在正式环境和测试环境中切换了。

多渠道打包

既然一台设备上不能安装重复applicationid的app,那么我们就只能正式环境下对应一个applicationid,测试环境下对应一个测试版的applicaitionid。这个applicationid指的就是app module下的gradle文件中的applicaitonid。
那么需要每次打包的时候我们都去手动修改这个applicationid吗?答案是不用的,gradle可以配置productFlavors即所谓的多渠道打包来帮我们完成这件事。废话不多说接下来说具体的步骤。

1.首先贴一下我的配置完整的gradle文件

apply plugin: 'com.android.application'
apply from: 'tinker-support.gradle'//tinker没使用tinker的热更新不需要配置
apply plugin: 'bugly'
bugly {
    appId = "4892***ed"  //buglyid 如果你的工程没引进bugly sdk不需要配置
    appKey = "da76***********e46046a189"
    appVersion = "${rootProject.ext.versionName}"
}
android {
    signingConfigs {
        config {
            keyAlias KEY_ALIAS
            keyPassword KEY_PASSWORD
            storeFile file(STORE_FILE)
            storePassword STORE_PASSWORD

            v1SigningEnabled true
            v2SigningEnabled false
        }
    }

    compileSdkVersion 27
    buildToolsVersion '27.0.3'
    defaultConfig {
        applicationId "com.tindliang.www.demo"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.appTargetSdkVersion
        versionCode rootProject.ext.versionCode
        versionName rootProject.ext.versionName
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        multiDexEnabled true
        signingConfig signingConfigs.config


        flavorDimensions "test"//1.配置dimension
    }
//2配置buildTypes,这里的release的debuggable 一定要设为false
    buildTypes {
        release {
            minifyEnabled true//开启混淆 你若不需要混淆可设为false 跟多渠道打包没什么关系
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.config
            debuggable false
        }
    }
  //3.配置多渠道,这里配置2个渠道:dev测试环境,prod正式环境
    productFlavors {
        dev {
            applicationId "com.tindliang.www.demotest"//配置测试环境的applicationid 要和正式的不一样
            resValue "string", "app_name", "测试版"//设置测试版的appname,然后记得将strings文件中的app_name删掉
            buildConfigField "boolean", "SERVER_DEBUG", 'true'//给BuildConfig类增加一个boolean类型的字段,用来标记服务器地址是否是测试环境的
            dimension "test"//配置第一步中的flavorDimensions 配置的“test”,这里我们没有额外的需求不多做配置          
        }
        prod {
            applicationId "com.tindliang.www.demo"
            resValue "string", "app_name", "正式版"
            buildConfigField "boolean", "SERVER_DEBUG", 'false'
            dimension "test"
        }
    }

    android.applicationVariants.all { variant ->
//4.配置正式和测试环境下不同的buglyid和key 如果你没有引用bugly sdk 可不做此配置
        variant.outputs.all {
            def buglyAppId = null
            def buglyAppKey = null
            if (variant.flavorName == "dev") {//测试环境
                buglyAppId = 'f02d2adb52'
                buglyAppKey = '5e4d4ca8-8343-4909-ae2c-4be960c50a1c'
            } else if (variant.flavorName == "prod") {//正式环境
                buglyAppId = '4892fba0ed'
                buglyAppKey = 'da76b97c-f5d3-4a0d-9321-77e46046a189'
            }
            if(buglyAppId != null) {
                variant.ext.buglyAppId = buglyAppId
            }
            if (buglyAppKey != null) {
                variant.ext.buglyAppKey = buglyAppKey
            }
      //5.配置打包好的apk文件名称,如果你没有需要可不做此配置
            outputFileName = "${rootProject.getName()}_${buildType.name}_${variant.flavorName}_v${rootProject.ext.versionName}.apk"
        }
    }
}


dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation "com.android.support:appcompat-v7:${V7_APPCOMPAT_VERSION}"
    implementation "com.android.support:recyclerview-v7:${RECYCLER_VIEW_SUPPORT_VERSION}"
    implementation "com.android.support:design:${DESIGIN_SUPPORT_VERSION}"
    implementation "com.android.support.constraint:constraint-layout:${CONSTRAIT_SUPPORT_VERSION}"
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

步骤说明

1.首先在app module 下的gradle文件中配置了 flavorDimensions “test”(gradle3.0以后要求),这里我们对此没有额外的需求,可以不多做配置,有兴趣的同学可打开这篇文章自行了解。

2.配置buildTypes,buildTypes默认就已经有 release和debug 两种,如果你不开启混淆也就是不配置minifyEnabled 为true的话,那么你甚至可以直接跳过这步,什么都不配,默认就好。但是如果配置了,记得不要将debuggable 设为true,其默认值也是false(如果设为true,那么你的正式上线的app也就能被adb调试,这是应该不被允许的,所以一些安全扫描漏洞也会扫描此项并建议你要设为false)

3.重点关注productFlavors ,这里我们配置了2个渠道,注释其实已经说明得很清楚了,其中的
buildConfigField "boolean", "SERVER_DEBUG", 'true',当你重新构建工程(Build->RebuildProject)的时候,会自动生成一个类
com.tindliang.www.demo.BuildConfig(你的applicationid.BuidlConfig),自动生成的代码如下

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");//对应buildTypes的debuggable
  public static final String APPLICATION_ID = "com.tindliang.www.demo";
  public static final String BUILD_TYPE = "debug";//对应我们buildTypes的名称
  public static final String FLAVOR = "prod";//对应我们productFlavors 中的配置的渠道名称
  public static final int VERSION_CODE = 4;
  public static final String VERSION_NAME = "1.0.1";
  // Fields from product flavor: prod
  public static final boolean SERVER_DEBUG = false;//对应我们productFlavors 中的配置的“SERVER_DEBUG”
}

最后一个字段SERVER_DEBUG 即为你在多渠道中声明的字段。
注意当你的清单文件中有引用到applicationid的地方,比如

      <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.tindliang.www.demo.fileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>

需要将写死的applicationid即com.tindliang.www.demo替换为${applicationId},修改后的应为如下这种写法

    <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.fileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>

剩下的4和5的步骤如注释说的,没有需求的可以不做此配置

当你在gradle文件中做好上述配置后,重新构建工程。
RebuildProject.png
构建完成会生成步骤3说的BuildConfig和variants.png
dev前缀就是我们gradle多渠道中配置的测试环境渠道。
prod前缀就是我们gradle多渠道中配置的测试环境渠道。
Debug后缀就是我们gradle中配置的debug buildType。
Release后缀就是我们gradle中配置的release buildType。

我们看studio右上角的gradle task也会多出来2个task:assembleDev和assembleProd
gradle task.png

这些东西我们稍后再做说明,现在我们只差一步就可以实现开头要的效果。

配置服务器地址

声明一个类来保存服务器地址的静态变量

public class Constants{
 /**
     * 控制此版本是否为平时测试版本,{@link BaseConstants#SERVER_DEBUG}为false的时候,DEBUG应该设置为false
     * 即上线的正式版 DEBUG应该设置为false
     */
    public static boolean DEBUG = BuildConfig.DEBUG;
    /**
     * 控制服务器URL是否为正式地址 false为正式地址,true为测试地址,打包的时候需要切换
     */
    public static boolean SERVER_DEBUG = BuildConfig.SERVER_DEBUG;

    /*----------------debug--------------------------------*/
    public static final String DEBUG_API_SERVER_URL = "https:你的测试服务器URL"


    /*-----------------release-----------------------------*/
    public static final String RELEASE_API_SERVER_URL = "https:你的正式服务器URL"

    //最终引用的数据请求接口的地址
    public static final String API_SERVER_URL = SERVER_DEBUG ? DEBUG_API_SERVER_URL : RELEASE_API_SERVER_URL;
}

我们这里DEBUG用来标记是否是测试设备具体用途比如在你的app启动页上判断是否显示一些信息,比如DEBUG为true证明是测试设备那么我们可以显示出当前app的versionName,versionCode之类的信息
SERVER_DEBUG 当然就是来判断引用正式还是测试的服务器地址了。

好了到目前为止准备工作都已经做完了,接下来说我们该怎么打包和直接run 不同环境的app module

直接运行不同环境的app

我们配置好环境后,比如我们要直接运行测试环境的,那么你选中前面截图中的variants的devDebug
测试环境.png
有人可能会问和选devRelease有什么区别,区别就在于选devRelease后,BuildConfig中的DEBUG字段会变成false,那么根据你的业务需求看你需要是这个字段是true还是false来选择是要Debug还是Release,dev和prod同理。

选中后工程会自动重新构建,等工程build完毕后,运行项目。再选prodDebug,再运行项目,就会发现你的设备上安装上了正式和测试环境两种个app,他们的区别只是服务器引用地址不一样而已。

放一张我项目的截图app_launcher.png

打包不同环境的apk

前面说的gradle task多出来2个task: assembleDevassembleProd加上本来就有的assembleReleaseassembleDebug,现在打包出来的组合为[debug,release][dev,prod]的2x2的排列组合。
apk.png
我们需要选择哪一个task打包呢?

如果你选assembleProd那么gradle会打出prod渠道下(即正式服务器环境下)的2个apk,分别为releaseProddebugProd两个apk。
如果你选assembleRelease那么gradle会打包出release BuildType的2个apk,分别为releaseDevreleaseProd两个apk。

所以自己内部测试的时候选assembleDebug这样打包出来即为开发设备的2个测试与正式服务器环境的2个apk,这样即和前面说的直接运行不同环境的app中的2个apk一样。
打要正式要发布的apk的时候选assembleProd这样生成可调试的和不可调试的正式服务器环境的2个apk,不可调试的apk(releaseProd)拿去上线发布,可调试的debugProd用来内部测试。

那么你可能要问,你不需要每次都生成2个apk,比如你只要打上线要发布的一个releaseProd就好,我只知道可以选这种task,有同学知道其他方法的可以评论中告诉我下,谢谢!
installl.png
可以看到这里的task即为我们前面说的[debug,release][dev,prod]的2x2的排列组合的单个task。

总结

其实就是修改不同渠道下的applicationid加根据不同task来配置不同服务器环境变量来实现的。
ok,感谢阅读,希望对你有帮助,有什么不对的地方也希望可以在评论中指正。

猜你喜欢

转载自blog.csdn.net/tinderliang/article/details/80377128
今日推荐