Gradle 多渠道打包实践

公司现在用的是 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 clean
gradlew 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.jks
STORE_PASSWORD=XXX
KEY_ALIAS=XXX
KEY_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 --stacktrace
gradlew 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值