Gradle 多渠道打包实践

来源:互联网 发布:mac怎么安装第三方软件 编辑:程序博客网 时间:2024/06/05 00:19

公司现在用的是 ANT 来打包 apk。昨晚学习了一下用命令行使用 Gradle 打包,先总结,以后用得上。
这篇文章,只讲“是什么”、“怎么做”,至于“为什么”这里先不弄清楚,先把打包这个任务完成以后再来说这个。

配置友盟环境


在 AndroidManifest.xml 的 <application> 标签中添加相关信息:APP_KEY渠道号。在这里,渠道号只是一个占位符,在打包的阶段可以动态地替换占位符里的信息。

<application    ... >    <meta-data        android:name="UMENG_APPKEY"        android:value="@string/UMENG_APPKEY"/>    <meta-data        android:name="UMENG_CHANNEL"        android:value="${UMENG_CHANNEL_ID}"/>    ...</application>

配置签名信息


app 的 module 中的 build.gradle 添加下列信息:别名、别名密码、签名文件地址和签名密码

signingConfigs {    release {        keyAlias 'xxx'        keyPassword 'xxx'        storeFile file('C:/Users/Administrator/Desktop/xxx.jks')        storePassword 'xxx'    }}

设置 buildTypes 的签名配置


因为要打 release 包,所以需要设置 buildTypes 为 release 的签名配置,如下

buildTypes {    release {        zipAlignEnabled true        minifyEnabled true        shrinkResources true        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        signingConfig signingConfigs.release // 加上这句话即可    }}

配置渠道信息


渠道号是自己定的,比如:wandoujia(豌豆荚)、baidu(百度)、google(Play Store)等等,想取什么名字就取什么名字。不过不要使用数字开头的命名方式。也不要使用unknow 这样的名字,因为友盟不允许取这个名字。

还记不记得前面讲的渠道号占位符:${UMENG_CHANNEL_ID},这里要做的就是动态的替换占位符的内容。配置信息如下:

productFlavors {    baidu {        manifestPlaceholders = [UMENG_CHANNEL_ID: "baidu"]    }    google {        manifestPlaceholders = [UMENG_CHANNEL_ID: "google"]    }    wandoujia {        manifestPlaceholders = [UMENG_CHANNEL_ID: "wandoujia"]    }    xiaomi {        manifestPlaceholders = [UMENG_CHANNEL_ID: "xiaomi"]    }    owen {        manifestPlaceholders = [UMENG_CHANNEL_ID: "owen"]    }}

执行打包命令


到这里,相关的配置已经全部完成了,可以执行打包命令了,在控制台输入下面这两条命令

gradlew cleangradlew assembleRelease

打包好的 apk 文件就放在 app module 的 build\outputs\apk 文件夹下


APK文件的位置

完整的 build.gradle 配置


apply plugin: 'com.android.application'android {    signingConfigs {  // 签名配置        release {            keyAlias 'xxx'            keyPassword 'xxx'            storeFile file('C:/Users/Administrator/Desktop/xxx.jks')            storePassword 'xxx'        }    }    compileSdkVersion 23    buildToolsVersion "23.0.3"    defaultConfig {        applicationId "com.owen.packapk"        minSdkVersion 14        targetSdkVersion 23        versionCode 1        versionName "1.0"    }    buildTypes {        release {  // buildTypes 配置            zipAlignEnabled true            minifyEnabled true            shrinkResources true            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'            signingConfig signingConfigs.release        }    }    productFlavors {  // 渠道配置        baidu {            manifestPlaceholders = [UMENG_CHANNEL_ID: "baidu"]        }        google {            manifestPlaceholders = [UMENG_CHANNEL_ID: "google"]        }        wandoujia {            manifestPlaceholders = [UMENG_CHANNEL_ID: "wandoujia"]        }        xiaomi {            manifestPlaceholders = [UMENG_CHANNEL_ID: "xiaomi"]        }        owen {            manifestPlaceholders = [UMENG_CHANNEL_ID: "owen"]        }    }}dependencies {    compile fileTree(include: ['*.jar'], dir: 'libs')    testCompile 'junit:junit:4.12'    compile 'com.android.support:appcompat-v7:23.3.0'}

以上就打打包的全部内容了,做到这里就可以完成打包任务了。不过还有一些地方可以优化。

隐藏签名信息


签名信息这种东西属于私密的信息,应该保护起来,最好不要明文写到 build.gradle 里,更不要上传到代码仓库中去。应该放在单独的文件中,在 build.gradle 里去引用这个文件读取签名信息。

清空 signingConfigs

signingConfigs {    release {    }}

在 app module 中新建一个文件 signing.properties,填入签名信息

STORE_FILE=C:/Users/Administrator/Desktop/XXX.jksSTORE_PASSWORD=XXXKEY_ALIAS=XXXKEY_PASSWORD=XXX

在 build.gradle 里添加下列代码去读取这个文件中的签名信息。

File propFile = file('signing.properties');if (propFile.exists()) {    def Properties props = new Properties()    props.load(new FileInputStream(propFile))    if (props.containsKey('STORE_FILE') && props.containsKey('STORE_PASSWORD') &&            props.containsKey('KEY_ALIAS') && props.containsKey('KEY_PASSWORD')) {        android.signingConfigs.release.storeFile = file(props['STORE_FILE'])        android.signingConfigs.release.storePassword = props['STORE_PASSWORD']        android.signingConfigs.release.keyAlias = props['KEY_ALIAS']        android.signingConfigs.release.keyPassword = props['KEY_PASSWORD']    } else {        android.buildTypes.release.signingConfig = null    }} else {    android.buildTypes.release.signingConfig = null}

signing.properties 这个文件不能上传到代码仓库里去,所以要在 .gitignore 文件中添加一句话

signing.properties

经过这几个步骤,就实现了保护签名信息的需求。

简化渠道号配置信息的写法


之前的渠道信息是这么写的

productFlavors {    baidu {        manifestPlaceholders = [UMENG_CHANNEL_ID: "baidu"]    }    google {        manifestPlaceholders = [UMENG_CHANNEL_ID: "google"]    }    wandoujia {        manifestPlaceholders = [UMENG_CHANNEL_ID: "wandoujia"]    }    xiaomi {        manifestPlaceholders = [UMENG_CHANNEL_ID: "xiaomi"]    }    owen {        manifestPlaceholders = [UMENG_CHANNEL_ID: "owen"]    }}

可以发现这些配置信息的写法都差不多,有一定规律。所以,还可以简写成这样

productFlavors {    baidu {}    google {}    wandoujia {}    xiaomi {}    owen {}}productFlavors.all { flavor ->    flavor.manifestPlaceholders = [UMENG_CHANNEL_ID: flavor.name]}

跟踪打包过程


建议在命令后面加上 --stacktrace 参数,这样万一命令执行失败,可以看到详细的出错信息

gradlew clean --stacktracegradlew assembleRelease --stacktrace

定制 APK 的文件名


默认情况下,打包好的 APK 的文件名是这样的


APK 的文件名

我想要的这样的文件名:app_版本名称_版本号_日期_渠道号.apk,可以添加如下信息

def packageTime() {    return new Date().format("yyyyMMdd", TimeZone.getTimeZone("UTC"))}android {    applicationVariants.all { variant ->        variant.outputs.each { output ->            // 重命名产出的apk文件            def outputFile = output.outputFile            if (outputFile != null && outputFile.name.endsWith('.apk')) {                File outputDirectory = new File(outputFile.parent);                def fileName                if (variant.buildType.name == "release") {                    fileName = "app_v${defaultConfig.versionName}_${defaultConfig.versionCode}_${packageTime()}_${variant.productFlavors[0].name}.apk"                } else {                    fileName = "app_v${defaultConfig.versionName}_${defaultConfig.versionCode}_${packageTime()}_debug.apk"                }                output.outputFile = new File(outputDirectory, fileName)            }        }    }}

打包结果如图:


定制APK文件名

删除冗余的 unaligned apk 文件


在 app/build/outputs/apk 文件夹中,可以看到一些文件名末尾带有 unaligned 文字的冗余文件,这些文件没有用,可以删掉,只需要在 build.gradle 中添加下列命令就好了

applicationVariants.all { variant ->    variant.outputs.each { output ->        // 重命名产出的apk文件        def outputFile = output.outputFile        if (outputFile != null && outputFile.name.endsWith('.apk')) {            File outputDirectory = new File(outputFile.parent);            def fileName            if (variant.buildType.name == "release") {                fileName = "app_v${defaultConfig.versionName}_${defaultConfig.versionCode}_${packageTime()}_${variant.productFlavors[0].name}.apk"            } else {                fileName = "app_v${defaultConfig.versionName}_${defaultConfig.versionCode}_${packageTime()}_debug.apk"            }            output.outputFile = new File(outputDirectory, fileName)        }        // 删除unaligned apk        if (output.zipAlign != null) {            output.zipAlign.doLast {                output.zipAlign.inputFile.delete()            }        }    }}

打包之后,就没有这些冗余文件了


去除冗余文件

Github


完整项目地址在这里:https://github.com/owenmike/packApk

总结


多渠道打包的步骤:

  1. 配置 AndroidManifest.xml
  2. 配置签名信息
  3. 配置渠道号信息
  4. 定制与优化


文/程序猴(简书作者)
原文链接:http://www.jianshu.com/p/7236ceca2630

这种打包其实很耗时,当你的渠道超过10个的时候估计很麻烦,其实有更快的方法:
http://www.jianshu.com/p/e0783783d26d

程序猴: @cyning 美团方案确实不错,不过这个方案还需要注意一点,需要重新执行一下zipAligned才比较合适







0 0