Tamic: http://blog.csdn.net/sk719887916/article/details/53411771

开始

上篇Gradle发布Module(Maven)到jcenter, 并构建自己的企业Maven私服介绍了怎样从本地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_24
rem jdk的版本
set JDK_Version=1.6
rem sdk的路径
set AndroidHome=D:/Android/android-sdk-windows
rem 编译的android版本路径
set AndroidVersion=/platforms/android-8
rem 编译的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.keystore

for %%x in ("%AndroidProject%") do set AndroidProject=%%~sx
for %%x in ("%JAVA_HOME%") do set JAVA_HOME=%%~sx
for %%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打包过程:点击查看Ant打包,本次我就不在介绍 > Tamic: http://blog.csdn.net/sk719887916/article/details/53224544

Gradle

渠道包 ##


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

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

productFlavors {

    c91mobile{

    }

    c360{

    }

     crelese{
     }

    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{

    }

     crelese{
     }

    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;

应用定制

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

修改包名

productFlavors {

    c91mobile{
        applicationId "com.tamic.apkdemo91"

    }

    c360{
        applicationId "com.tamic.apkdemo360"
    }

     crelese{
     }

}

修改app名称

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



这样输出的apk也不一样,具体不在详细多说。

* 修改代码*

还有很多时候时候我们本地哟很多模块,但我们不想打和不想关的sdk到我们不想要这个功能的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里面代码了。

延伸


通过上面我们发现输出的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)
        }
    }
}

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

结尾

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

Tamic: http://blog.csdn.net/sk719887916/article/details/53411771

[Gradle系列]Gradle打包apk多版本,多渠道,多环境,多功能,多模块随心所欲的更多相关文章

  1. [Gradle系列]Gradle发布module库到jCenter, 并构建自己的企业Maven私服

    Tamic 作者: http://blog.csdn.net/sk719887916/article/details/53224544 前言 andorid开发者经常会看到xx公司发布了xx项目,xx ...

  2. centos 下 gradle 编译打包 apk

    由于Jenkins 装在centos环境下,想实现Android程序的编译,只能通过gradle 命令去打包版本apk,以下记录了如何在centos下使用gradle 打包apk 一.安装 gradl ...

  3. Unity自动打包Apk

    unity打包apk相对来说比较容易,相信出过的人都明白,出包过程,没有大的难度,一步一操作,一步一等待,繁琐耗时,不懂的人又代替不了.这时候需求就来了,如何简单的一键打包搞定,这个就稍微有点难度,当 ...

  4. Gradle实现的两种简单的多渠道打包方法

    本来计划今天发Android的官方技术文档的翻译--<Gradle插件用户指南>的第五章的,不过由于昨天晚上没译完,还差几段落,所以只好推后了. 今天就说一下使用Gradle进行类似友盟这 ...

  5. Gradle实现自动打包,签名,自定义apk文件名

    Gradle实现自动打包,签名,自定义apk文件名 什么是签名,签名有什么用 Android APP都需要我们用一个证书对应用进行数字签名,不然的话是无法安装到Android手机上的,平时我们调试运行 ...

  6. 打包APK出现org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:lintVitalRelease'.

    AndroidS Studio打包APK时出现问题:org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':a ...

  7. Gradle系列之Android Gradle插件

    原文发于微信公众号 jzman-blog,欢迎关注交流. 通过前面几篇文章学习了 Gradle 基础知识以及 Gradle 插件相关的知识,关于 Gradle 及其插件相关知识请先阅读下面几篇文章: ...

  8. Android Studio 多个编译环境配置 多渠道打包 APK输出配置

    看完这篇你学到什么: 熟悉gradle的构建配置 熟悉代码构建环境的目录结构,你知道的不仅仅是只有src/main 开发.生成环境等等环境可以任意切换打包 多渠道打包 APK输出文件配置 需求 一般我 ...

  9. Gradle系列之Android Gradle基础配置

    原文发于微信公众号 jzman-blog,欢迎关注交流. 通过前面几篇文章学习了 Gradle 基础知识以及 Gradle 插件相关的知识,关于 Gradle 及其插件相关知识请先阅读下面几篇文章: ...

随机推荐

  1. C# 6.0中你不知道的新特性

    为什么写? 今天去上班的公交上,有朋友在张队(张善友)的微信群里,发了一个介绍C# 6.0新特性的视频,视频7分钟,加上本人英语实在太low,整体看下来是一脸懵逼的. 下班回到家里,打开这个视频,把视 ...

  2. 前端之旅HTML与CSS篇之IE6常见BUG

    1.IE6怪异解析之padding与border算入宽高原因:未加文档声明造成非盒模型解析解决方法:加入文档声明<!doctype html> 2.IE6在块元素.左右浮动.设定marin ...

  3. 在容器中运行 Jenkins pipeline 任务

    持续集成中的 pipeline 技术和 docker 都是当前正在发展的主流方向,当然把它们结合起来在 CI/CD 过程中发挥出更强大的威力也是大家共同的目标.本文将介绍如何在 Jenkins pip ...

  4. [ Java学习基础 ] String、StringBuffer、StringBuilder比较学习

    首先讲获得字符串对象的方式有两种,一种是直接使用字符串常量,一种是使用new关键字创建,但它们之间是有一些区别,如下运行实例: String s1 = new String("Hello&q ...

  5. codevs 搜索题汇总(钻石+大师级)

    1043 方格取数 2000年NOIP全国联赛提高组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond 题目描述 Description 设有N*N的方格图 ...

  6. [IOI 2011]ricehub

    Description 乡间有一条笔直而长的路称为“米道”.沿着这条米道上 R 块稻田,每块稻田的坐标均为一个 1 到 L 之间(含 1 和 L)的整数.这些稻田按照坐标以不减的顺序给出,即对于 0 ...

  7. [HNOI2010]MATRIX 矩阵

    Description Input 第一行包含三个正整数N M P表示矩阵的行数列数以及每个数的范围,接下来N行每行包含M个非负整数,其中第i行第j个数表示以格子(i,j)为右下角的2*2子矩阵中的数 ...

  8. [HNOI2003]消防局的设立

    题目描述 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状 ...

  9. [SDOI2009]HH的项链

    题目描述 HH 有一串由各种漂亮的贝壳组成的项链.HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义.HH 不断地收集新的贝壳,因此,他的项链变得越来越长. ...

  10. 【tyvj】刷题记录(1001~1099)(64/99)

    1001:排序完按照题意做即可. #include<cstdio> #include<iostream> #include<cmath> #include<a ...