Android gradle打包基础
gradle目录
[代码]java代码:
12345 | MyApp ├── build.gradle ( 可以查看Gradle的版本 ) ├── settings.gradle( include不同的模块) └── app └── build.gradle( 配置App模块的参数 ) |
Gradle signingConfigs基本配置
archiveNameFormat - Groovy格式字符串, 指定渠道打包输出的APK文件名格式,默认文件名格式是: ${appPkg}-${flavorName}-${buildType}-v${versionName}-${versionCode},可使用以下变量:
projectName - 项目名字
appName - App模块名字
appPkg - applicationId (App包名packageName)
buildType - buildType (release/debug/beta等)
flavorName - flavorName (对应渠道打包中的渠道名字)
versionName - versionName (显示用的版本号)
versionCode - versionCode (内部版本号)
buildTime - buildTime (编译构建日期时间)
语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | android { compileSdkVersion 22 //编译版本 buildToolsVersion "23.0.0" //buildtool版本 defaultConfig { //默认配置,会同时应用到debug和release版本上 applicationId "io.yimsamtam.***" //包名 minSdkVersion 14 targetSdkVersion 22 versionCode 2 versionName "1.2.0" } signingConfigs { release {//获取build.properties里的密码常量,需要在buildeTypes引用 storeFile file(STORE_FILE) storePassword STORE_PASSWORD keyAlias KEY_ALIAS keyPassword KEY_PASSWORD } debug { storeFile file(STORE_FILE) storePassword STORE_PASSWORD keyAlias KEY_ALIAS keyPassword KEY_PASSWORD } } buildTypes { //这里面可以配置不同版本的一些参数,比如混淆、签名配置等 release { //正式服 正式签名 signingConfig signingConfigs.release //用keystore打包 minifyEnabled true //是否开启混淆 zipAlignEnabled true //压缩 shrinkResources true //去掉无用的resourse文件 debuggable true //使该版本可调试 buildConfigField "boolean", "LOG_DEBUG", "false" //加载混淆文件,开启混淆没有这句会报错 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } develop {//开发服 正式签名 signingConfig signingConfigs.release minifyEnabled true zipAlignEnabled true shrinkResources true debuggable true //使该版本可调式 buildConfigField "boolean", "LOG_DEBUG", "true" proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { //测试服 测试签名 signingConfig signingConfigs.debug minifyEnabled false zipAlignEnabled false // 移除无用的resource文件 shrinkResources false buildConfigField "boolean", "LOG_DEBUG", "true" proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } //修改生成的最终文件名 applicationVariants.all { variant -> variant.outputs.each { output -> def outputFile = output.outputFile if (outputFile != null && outputFile.name.endsWith('.apk')) { File outputDirectory = new File(outputFile.parent); def fileName String channelName = "homepage"; if (variant.buildType.name == "release") { //输出:bnj_t_2015.12.12_4886faf_c360.apk if (variant.productFlavors[0] != null) { channelName = variant.productFlavors[0].name; } //版本+时间+git版本+渠道的格式 fileName = "Movie_${channelName}_${packageTime()}_${revision()}.apk" } else if (variant.buildType.name == "develop") { //输出:bnj_t_2015.12.12_4886faf.apk fileName = "Movie_${packageTime()}_${revision()}.apk" //${defaultConfig.versionname} } else { //输出:bnj_t_2015.12.12_4886faf_debug.apk fileName = "Movie_debug.apk" } output.outputFile = new File(outputDirectory, fileName) } } } productFlavors { offcial { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "offcial"] } wandoujia { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"] } baidu { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"] } } //修改生成的最终文件名 applicationVariants.all { variant -> variant.outputs.each { output -> def outputFile = output.outputFile if (outputFile != null && outputFile.name.endsWith('.apk')) { File outputDirectory = new File(outputFile.parent); def fileName String channelName = "offcial"; if (variant.buildType.name == "release") { //输出:Movie_2015.12.12_4886faf_c360.apk if (variant.productFlavors[0] != null) { channelName = variant.productFlavors[0].name; } //版本+时间+git版本+渠道的格式 fileName = "Movie_${channelName}_${packageTime()}_${revision()}.apk" } else if (variant.buildType.name == "develop") { //输出:bnj_t_2015.12.12_4886faf.apk fileName = "Movie_Dev${packageTime()}_${revision()}.apk" //${defaultConfig.versionname} } else { //输出:bnj_t_2015.12.12_4886faf_debug.apk fileName = "Movie_debug.apk" } output.outputFile = new File(outputDirectory, fileName) } } } lintOptions {//移除lint检查的error abortOnError false } } //找到文件的签名 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 } dependencies { //模块依赖 compile group: '包名' , name: '模块名' , version: '版本号' } // 打包时间格式 def packageTime() { return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC")) } |
Mapping(/project/app/build/outputs/mapping/)
dump.txt 描述apk文件中所有类文件间的内部结构。
mapping.txt 列出了原始的类,方法,和字段名与混淆后代码之间的映射。
seeds.txt 列出了未被混淆的类和成员
usage.txt 列出了从apk中删除的代码
settings.gradle (声明加入工程的module)
include ‘:app’,’:module-b’ //示例
gradle.properties (保存应用签名密码等常量)
STORE_FILE = keystore(jks)的路径
STORE_PASSWORD = KEYSTORE密码
KEY_ALIAS = 别名
KEY_PASSWORD = KEYSTORE密码会自动生成local.properties文件
在build.gradle的android标签下引用
1 2 3 4 5 6 7 8 | signingConfigs { //获取应用签名的密码常量,需要在buildTypes里调用 releaseConfig { storeFile file(STORE_FILE) storePassword STORE_PASSWORD keyAlias KEY_ALIAS keyPassword KEY_PASSWORD } } |
常见错误
1 | If you are using Gradle, make sure that your current variant is debuggable. |
原因及解决:Build Variant里该版本未开启调试功能。在Gradle里添加debugable true代码即可。
1 | Error:Execution failed for task ':demo:packageRelease'.> Unable to compute hash of D:\testProject\demo\build\intermediates\classes-proguard\release\classes.jar |
原因及解决:混淆了第三方jar包。注释掉 -libraryjars相关代码即可。
1 2 | The APK file build\outputs\apk\******.apk does not exist on disk. Error while Installing APK |
原因及解决: 刷新一下Gradle或者Clean Project即可。
BuildConfig详解
当Android Studio编译的时候,会自动生成一个BuildConfig类。该类保存了一些Gradle里的常量。在Java代码中,可以通过该类来获取这些常量的值。
1 2 3 4 5 6 7 8 9 10 | public final class BuildConfig { public static final boolean DEBUG = Boolean.parseBoolean("true"); public static final String APPLICATION_ID = "io.movies"; //包名 public static final String BUILD_TYPE = "develop";//发布类型 public static final String FLAVOR = "offcial"; //渠道版本 public static final int VERSION_CODE = 2;//版本号 public static final String VERSION_NAME = "1.2.0";//版本名 // Fields from build type: develop public static final boolean LOG_DEBUG = true; //自定义的Log关闭 } |
当然我们还可以根据需要来自定义这些常量。例如根据发布类型来控制LOG是否输出。
首先,我们需要在BuildTypes不同发布类型里定义一个字段 ,从而控制其LOG输出。
1 2 3 4 5 6 7 8 9 10 11 12 | buildtypes{ release{ buildConfigField "boolean","LOG_DEBUG","false"//关闭LOG输出 } develop{ buildConfigField "boolean","LOG_DEBUG","true" //打开LOG输出 } } |
接着,在Log工具类里,通过BuildConfig类来获取LOG_DEBUG的值即可。
1 2 3 4 5 | public class LogUtil { public static boolean ENABLE_LOG = BuildConfig.LOG_DEBUG; } |
Meta-data详解
Androidmanifest文件里meta-data标签主要用于提供组件额外的数据,本身是一个键值对,可以自定义键和值。(在打包的时候,可根据需要设置不同的值)
在Java中使用
首先判断Meta-data标签在哪个标签内,如果在application标签里,则需要用Applicationinfo来获取该值。同理,在Activity标签里,用ActivityInfo。
1 2 3 4 5 6 7 8 9 | <application . . . <meta-data android:name="yimsamtam.github.io.test" android:value="YimSamTam" /> </application> |
在Java代码用ApplicationInfo获取。
1 2 3 | ApplicationInfo info = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA); String value = info.metaData.getString("yimsamtam.github.io.test"); Functions.toast(this, value); //此处打印YimSamTam字符串 |
在Gradle中使用
Androidmanifest文件里,其值通过${KEY}的方式来引用。在Gradle里赋值。
1 2 3 4 5 6 7 8 9 | <application . . . <meta-data android:name="yimsamtam.github.io.test" android:value="${TEST_VALUE}" /> </application> |
在Gradle里结合manifestPlaceholders赋值
1 2 3 4 5 6 7 | productFlavors { offcial { manifestPlaceholders = [TEST_VALUE: "YimSamTam"] } } //当程序编译offcial版本时,便会赋值YimSamTam到Androidmanifest文件的meta-data标签里。 |
实际开发需求-不同发布版本使用不同的友盟UMENG_APPKEY
Manifest代码如下
1 2 3 4 5 6 | <application <meta-data android:name="UMENG_APPKEY" android:value="${UMENG_APPKEY}" /> </application> |
Gradle代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | buildTypes { release { //正式服 manifestPlaceholders = [UMENG_APPKEY: "570104fde0f55aedba00200d-REL"] } develop {//开发服 manifestPlaceholders = [UMENG_APPKEY: "570104fde0f55aedba00200d-DEV"] } debug { //测试服 manifestPlaceholders = [UMENG_APPKEY: "570104fde0f55aedba00200d-DEB"] } } |
推荐