Gradle敏捷打包,多版本,多渠道,多环境,多功能,多模块随心所欲

Posted 开发者技术前线

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gradle敏捷打包,多版本,多渠道,多环境,多功能,多模块随心所欲相关的知识,希望对你有一定的参考价值。


前段时间收到很多朋友的私信,咨询交流相关技术的,也有来约稿的,前一阵子收到幕课网的邀请,要不要去做视频课堂讲师(对他们来说就是特定课程来源渠道)或者出个特定的专辑教材,由他们统一修正出版!

私底下自己想来想去,感觉本身的分享成了商业化后,是不是会让本身想学习的人带来一定的抵触感呢?所以果断拒绝了,加之自己也没时间去录视频,也不想参与什么商业化活动,只是安静的做一个开发者,自我定位一下,虽然不能像老罗一样出身入化,也不能像极客一样开源万篇,更不想随天天发个入门的demo,就想有时间就分享下自己了解的东西,就这么简单!





开始

上篇介绍了怎样从本地module发布自己的maven包到JCenter,包括怎样构建远程maven企业私服,既然有了自己的maven包,对于后期出包版本迭代比中相对容易控制版本,那么版本渠道等相关问题又怎么控制,尤其涉及OEM业务的公司企业定制化更加严重,一般存在以下问题?

  • 渠道定制,比如不同厂商有不同的渠道包,出包是否一个个打包

  • 版本控制,不同厂商不同版本,是否需要不版本不同代码

  • 功能定制,不同厂商的apk要的功能不一样,是否需要二次开发

  • 应用定制, 不同厂商需要的包名,appName, 业务也不一样,是否需

    要重新构建App


    看了以上App问题,发现用一套代码是不行的,有的同学就开始从主分支拉取分支了,后期版本越来越多,分支迅速增加,这时候维护成本越来越大,对于一个普通定制的Apk, 很可能只是想关闭一个开关(比如不增加推送),改起来也很快,但是从客户厂商到商务,商务到开发手里,开发再从拉分支,到修改代码,到版本测试到输出包 ,到最后的移交版本到商务手里,最后移交厂商客户,发现一来一回时间浪费太多,因此我们想到了是否有能自动化敏捷的打包机制呢,以前用的eclipse开发的很多朋友喜欢用ANt构建自己的app,将会在远程服务器部署andoid开发环境,使用本地web页面,一个按钮调用部署在远程ant脚本,执行android的bat打包命令,并将远程的code git关联上,输出具体的apk,不管是QA,Bd, Rd,PM都可以操作,无需开发自己动手打包,实际上不同厂商的需求是不一样,BD手里接手的比较全面的需求资料,这时候BD直接通过可视化的网页来定制所有需求,并一键打包,那么今天的主题并不是教大家怎么构建自动化打包平台,今天先是解决上面的几个问题。等解决了本地基础,我们在试着搭建一个远程智能打包平台(非Jenkins)。

    ANT方式


    在这里大致介绍下ANT流程 

    echo off
    rem =========基本参数配置============
    rem jdk的路径
    set JAVA_HOME=D:/Program Files/Java/jdk1.6.0_24rem jdk的版本
    set JDK_Version=1.6rem sdk的路径
    set AndroidHome=D:/Android/android-sdk-windows
    rem 编译的android版本路径
    set AndroidVersion=/platforms/android-8rem 编译的android项目路径
    set AndroidProject=D:/yourProject rem 编译生成的未签名apk文件 set unsign_apk=yourProject.apk rem 编译生成的已签名apk文件 set sign_apk=yourProject-sign.apk rem 签名用的key set apk_key=keyname set apk_keypass=keypass set apk_keystore=D:/yourProject/key.keystorefor %%x in ("%AndroidProject%") do set AndroidProject=%%~sx for %%x in ("%JAVA_HOME%") do set JAVA_HOME=%%~sxfor %%x in ("%AndroidHome%") do set AndroidHome=%%~sx rem jdk工具包 set EXE_JAVA=%JAVA_HOME%/bin/java set JAVAC=%JAVA_HOME%/bin/javac set JAR=%JAVA_HOME%/bin/jar set KeyTool=%JAVA_HOME%/bin/keytool set Jarsigner=%JAVA_HOME%/bin/jarsigner rem sdk工具包 set AndroidAAPT=%AndroidHome%%AndroidVersion%/tools/aapt.exe set AndroidDx=%AndroidHome%%AndroidVersion%/tools/dx.bat set AndroidApkBuilder=%AndroidHome%/tools/apkbuilder.bat set AndroidJar=%AndroidHome%%AndroidVersion%/android.jar rem android项目引用的扩展jar包 set ExternerJar=%AndroidProject%/lib/commons-codec.jar;%AndroidProject%/lib/commons-httpclient-3.1.jar; set ReferJar=%AndroidProject%/lib/commons-codec.jar %AndroidProject%/lib/commons-httpclient-3.1.jar rem android项目基本目录及配置文件 set AndroidProjectRes=%AndroidProject%/res set AndroidProjectGen=%AndroidProject%/gen set AndroidProjectBin=%AndroidProject%/bin set AndroidProjectAsset=%AndroidProject%/assets set AndroidProjectAndroidMainfest=%AndroidProject%/AndroidManifest.xml set AndroidProjectSrc=%AndroidProject%/src/weibo/*.java set AndroidProjectSrc=%AndroidProjectSrc% %AndroidProject%/src/weibo/http/*.java set AndroidProjectSrc=%AndroidProjectSrc% %AndroidProject%/src/weibo/util/*.java set AndroidProjectSrc=%AndroidProjectSrc% %AndroidProject%/gen/yourProjectPackageName/*.java rem 编译输出文件 set AndroidProjectClassDex=%AndroidProject%/bin/classes.dex set AndroidProjectResources=%AndroidProject%/bin/resources.ap_ set AndroidProjectApk="%AndroidProject%/bin/%unsign_apk%"set AndroidProjectSignApk="%AndroidProject%/bin/%sign_apk%"echo 生成R.java%AndroidAAPT% package -f -m -J %AndroidProjectGen% -S %AndroidProjectRes% -I %AndroidJar% -M %AndroidProjectAndroidMainfest% echo 生成class%JAVAC% -encoding UTF-8 -target %JDK_Version% -bootclasspath %AndroidJar% -classpath %ExternerJar% -d %AndroidProjectBin% %AndroidProjectSrc% echo 生成dex cd %AndroidProjectBin% rem 把bin目录下*.class文件打成jar包%JAR% cvf %AndroidProjectBin%/yourProject.jar *.* cd %AndroidProject% rem 生成dex  这里需要注意,因为调用的是bat的脚本,因此必须用Call call %AndroidDx% --dex --output=%AndroidProjectClassDex% %AndroidProjectBin%/yourProject.jar %ReferJar% echo 打包资源文件%AndroidAAPT% package -f -M %AndroidProjectAndroidMainfest% -S %AndroidProjectRes% -A %AndroidProjectAsset% -I %AndroidJar% -F %AndroidProjectResources% echo 生成未签名的apk文件 call %AndroidApkBuilder% %AndroidProjectApk% -v -u -z %AndroidProjectResources% -f %AndroidProjectClassDex% -rf %AndroidProject%/src echo 生成数字签名key.keystore%KeyTool% -genkey -v -keystore %apk_keystore% -storepass %apk_keypass% -keypass %apk_keypass% -alias myKey -dname CN=Liux,OU=makingware.com,O=makingware,L=sz,ST=gd,C=cn -keyalg RSA -validity 10000 echo 进行数字签名%Jarsigner% -verbose -keystore %apk_keystore% -keypass %apk_keypass% -storepass %apk_keypass% -signedjar %AndroidProjectSignApk% %AndroidProjectApk% myKey echo 签名成功 pause

    这里介绍一篇详细的ant打包过程:,这里我就不再介绍。

    Gradle

    渠道包


    对于多渠道版本,gradle提供了productFlavors节点,

    开发者可以通过以下方式制定自己的渠道包:

    productFlavors {    c91mobile{    }
    
        c360{    }
    
         crelease{     }
    
        cdev {    }
    
        cqa{     }}

    上面自定义了五个渠道类型,如果你还有其他渠道可以一次加入自己的渠道名, channelname{},需要的来执行具体渠道命令即可。如: gradlew.bat assembleC360就是来打360的渠道包的。如果时候你需要一次性输出所有渠道的包,那么可以执行gradlew.bat build。

    版本控制


    对于厂商版本需求,很可能遇到客户不需要最新apk情况,但是就版本的一些功能又很浪费性能,那么我们不可能用以前的版本直接输出,这时候很可能用最新apk来构建,那么我们可以不降低工程版,只需降低module版本,如果最新版本依赖的1.2.5,那么我们将本地Module(远程maven)降低版本即可。

    dependencies {
    
    .....
    compile 'com.tamic.novate:novate:1.2.4'

    功能定制


    还有很多厂商需要的功能不一样,那么我们可以关掉一些常用的配置开关,比如推送,强制升级,启动广告等,那么可以用buildTypes来进行实现。

    buildTypes {
        release {
            minifyEnabled false
            shrinkResources true
            buildConfigField "boolean", "isPush", "false"
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    
        debug {
            minifyEnabled false
            shrinkResources true
            buildConfigField "boolean", "IsPush", "true"
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    这样我们就给自己的app设置了一个推送标识开关,那么java代码可以这样做:

    if (BuildConfig.isPush) {
          /..todo          }

    android自己自带一些常规参数,包括版本号,版本名称,渠道,打包方式等

     public final class BuildConfig {  
    public static final boolean DEBUG = Boolean.parseBoolean("true");  
    public static final String APPLICATION_ID = "com.tamic.apkdemo360";  
    public static final String BUILD_TYPE = "debug";  
    public static final String FLAVOR = "c360";  
    public static final int VERSION_CODE = 1;  
    public static final String VERSION_NAME = "1.0";  
    // Fields from the variant  public static final String APP_ENV = "c360 "; }

    通过以上这种方式我们可以联想到其他定制需求,比如不同渠道不同服务器,线上和线下不同Url等,输出log等。

    简单的可以这么做;

    首先我在gradle定义一个宏,

    def hostUrl = "https://github.com/Tamicer";

    接着我在不同版本定义不同域

    productFlavors {
    
        c91mobile{
    
    
        }
    
        c360{
    
        }
    
         crelease{
         }
    
        cdev {
            hostUrl = "http://www.baidu.com/"
    
        }
    
        cqa{
             hostUrl = "http://www.tencent.com/"
         }
    
    }

    这样就修改了App自身hostUrl, Java代码可以直接拿来用了, gradle也会生成一个HOST_URL字段,你想在哪儿调用就在哪儿用。

    public final class BuildConfig {  
     public static final boolean DEBUG = Boolean.parseBoolean("true");  
     public static final String APPLICATION_ID = "com.tamic.apkdemo360";  
     public static final String BUILD_TYPE = "debug";  
     public static final String FLAVOR = "c360";  
     public static final int VERSION_CODE = 1;  
     public static final String VERSION_NAME = "1.0";  
     // Fields from the variant  public static final String APP_ENV = "c360 ";  
     public static final String HOST_URL = " http://www.tencent.com/";  
     // Fields from build type: debug  public static final boolean LOG_DEBUG = true;

    Gradle敏捷打包,多版本,多渠道,多环境,多功能,多模块随心所欲


    应用定制

    除了上面的这些场景外,还有很多需要修改App名称,改包名,已经去除某些代码逻辑的,那么我看依旧可以借助gradle轻松实现。

    修改包名

    productFlavors {    c91mobile{        applicationId "com.tamic.apkdemo91"    }
    
        c360{        applicationId "com.tamic.apkdemo360"    }
    
         crelease{     }

    }

    Gradle敏捷打包,多版本,多渠道,多环境,多功能,多模块随心所欲


    修改app名称

    开发中可以在代码架构层次中创建对应的资源名称和代码逻辑,修改名称和app图标的方式都一样。

    Gradle敏捷打包,多版本,多渠道,多环境,多功能,多模块随心所欲


    这样输出的apk名字也不一样,具体的详细步骤不再多说。

    修改代码

    还有很多时候时候我们本地的很多模块,但我们不想打不相关的sdk到我们的app中,或则不想要这个功能的app中,简单可以这样:

    dependencies {
    
      compile 'com.android.support:appcompat-v7:23.2.1'
     compile 'com.android.support:design:23.2.1'
     cdevCompile 'com.tamic.novate:novate:1.2.4'
     testCompile 'junit:junit:4.12'

    }

    比如我的开发环境,需要Novate这个sdk,其他渠道需要去除的情况,可以建立一个对应于dev的依赖,这样其他打出包就没这个sdk,java代码可以这么处理。

    if (BuildConfig.FLAVOR.equals("cdev")) {               
     try {                    
     Class.forName("com.tamic.novate.Novate");                    
     Novate novate = new Novate.Builder(MainActivity.this).build(); }

    如果不想这么判断,也可以在对应的dev文件加下建立java,来处理有这个sdk的逻辑,其他渠道就默认main里面代码了。

    Gradle敏捷打包,多版本,多渠道,多环境,多功能,多模块随心所欲


    延伸


    通过上面我们发现输出的apk都是同样的一个名字,难以分辨出,可以将打包类型,渠道,版本号,日期等加入进去,例如输出:tamic_release_360_ver1.0.0_build20160921.apk  。

    //定制output的apk文件名
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def outputFile = output.outputFile
            // 打包类型
            def buildTypeName = variant.buildType.name
            if (outputFile != null && outputFile.name.endsWith('.apk')) {
                // 打包名称
                def flavorName = variant.productFlavors[0].name
                // 版本名称
                def versionName = defaultConfig.versionName
                // 开发环境
                buildConfigField "String", "APP_ENV", "\"${flavorName} \""            // url            buildConfigField "String", "HOST_URL", "\" ${hostUrl}\""
                // tamic_release_360_ver1.0.0_build20160921.apk
                def fileName = "${PRODUCT_NAME}_${buildTypeName}_${flavorName}_V${versionName}_build${BUILD_TIME_FORMAT}.apk"            output.outputFile = new File(outputFile.parent, fileName)        }    }}

    Gradle敏捷打包,多版本,多渠道,多环境,多功能,多模块随心所欲


    以上就是批量打包结果,其他还有配置不同版本配置的签名,全局配置变量很多技巧,不在今天的话题之内,这里不再介绍,读者可以看看我以前的文章。

    这里有人就要提问题了,你的这种打包方式有效率吗,为何不用拆分包方式进行打包,我看很多人是直接用apkTool反编译修改dex再合成签名apk,其实不用担心,Android studio 的Instant Run已经帮我们做了部分工作。


    结尾

    今天常规的打包技巧已全部介绍完毕,通过以上模式,大致满足企业对第三方厂商OEM需求,如果有兴趣的朋友可以再深入研究,下期介绍搭建自己打包服务器。


    Tamic开发社区

    To You,For Me

    Android &  ios


    长按二维码关注

    以上是关于Gradle敏捷打包,多版本,多渠道,多环境,多功能,多模块随心所欲的主要内容,如果未能解决你的问题,请参考以下文章

    Android手把手带你进入android打包的多版本,多环境,多渠道的踩坑之旅

    Gradle多渠道打包(动态设定App名称,应用图标,替换常量,更改包名,变更渠道)

    Android 通过python实现自动化构建打包上传加固

    AndroidStudio 升级3.4后gradle引发的多渠道多任务异常

    gradle多渠道打包及友盟统计-eclipse版本

    手把手教你使用gradle mulchannel插件,进行多渠道打包