我们可以新建一个项目,然后生成一个新的签名文件。
一般来说,经过这些操作之后,我们便可以通过点击左上角的Build->Generate Signed APK->Next,然后选择对应的Build Type进行打包。那对一些人来说,这样也太麻烦了,每次都得输入相关信息,还得进行选择,这怎么能忍!
那么有更简单快捷的方法吗?答案是有的。我们可以在项目的app目录下的build.gradle中进行签名的配置。
首先我们找到build.gradle下的android{},默认配置如下所示接下来呢,我们可以把项目的预览模式调成Project,不调也行,只需把刚才生成的签名文件放至到对应的目录下即可。我的做法是直接放至到项目下,如图所示
然后,回到app下的build.gradle,在里面增加这段代码
signingConfigs { debug { storeFile file('../test.jks')//签名文件路径 storePassword "123456" keyAlias "test" keyPassword "123456" //签名密码 println("====== signingConfigs.debug ======") } release { storeFile file('../test.jks')//签名文件路径 storePassword "123456" keyAlias "test" keyPassword "123456" //签名密码 println("====== signingConfigs.release ======") } }
我来解释一下,storeFile file对应的就是签名文件key的路径,我们的签名放在项目的直接子路径下,而build.gradle在test/app下,所以我们要用 .. 找到项目的根路径,再通过 / 找到对应的签名文件所在路径。当然,你也可以自己配置签名文件的地址,只要能找到即可。
此时,我们在项目路径下的app/build/outputs可以看到我们打包的apk,从名字可以看出,该APK是已经签名的。那么就是说,用代码配置,可以达到和使用图形界面一样的效果。
对了,这里有一点需要注意,就是signingConfigs代码块一定要写在buildTypes前面,否则会报下面这种错: Could not find property ‘debugConfig’ on SigningConfig .
至此,我们的buildTypes的配置可以这样:
buildTypes { debug { println("====== buildTypes.debug ======") signingConfig signingConfigs.debug } release { //是否混淆 minifyEnabled false //是否移除无用资源 zipAlignEnabled true println("====== buildTypes.release ======") signingConfig signingConfigs.release //混淆的配置文件 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
大家现在便可以通过命令行进行任意操纵,或者进行多渠道打包。
不过,上述的配置虽然配置简单,但是存在不安全性,假如你的项目是开源的,你把签名文件的配置密码之类的信息用明文写在build.gradle里面,那是不是很不安全呢?所以,我们可以这么做。在项目路径下新建一个.properties的文件,或者直接local.properties下直接添加相关信息即可。
我们可以直接在该文件下添加:(填写相关信息)
keystore.path=../test.jks //替换成自己的签名路径keystore.password=123456 keystore.alias=testkeystore.alias_password=123456
那么,在build.gradle下面,为了不用明文显示,我们首先要获得key的相关配置,所以我们可以在app的build.gradle下面新增该代码
def keystoreFilepath = ''def keystorePSW = ''def keystoreAlias = ''def keystoreAliasPSW = ''// default keystore file, PLZ config file path in local.propertiesdef keyfile = file('s.keystore.temp')Properties properties = new Properties()// local.properties file in the root directorproperties.load(project.rootProject.file('local.properties').newDataInputStream())keystoreFilepath = properties.getProperty("keystore.path")if (keystoreFilepath) { keystorePSW = properties.getProperty("keystore.password") keystoreAlias = properties.getProperty("keystore.alias") keystoreAliasPSW = properties.getProperty("keystore.alias_password") keyfile = file(keystoreFilepath)}
首先,便是给key赋默认值,然后根据Properties的配置文件,然后根据我们在local.properties下的配置参数keystore.password等获取签名文件的配置信息。
这里有一点需要强调,在的项目中,我们可以看到我们项目下有一个.gitignore的文件,里面的配置大概如下所示
我们可以看到/local.properties,意思就是说local.properties默认是不添加到版本控制里面的,因为local.properties存储的是我们环境资源的一些相关信息,如sdk的路径。故我们可以在local.properties下配置签名信息而不用担心密钥外泄。对于开源项目来说,是非常好的。
那么,此时此刻,我们app/build.gradle下的signingConfigs可以改为:
signingConfigs { debug { storeFile keyfile storePassword keystorePSW keyAlias keystoreAlias keyPassword keystoreAliasPSW println("====== signingConfigs.debug ======") } myConfig { storeFile keyfile storePassword keystorePSW keyAlias keystoreAlias keyPassword keystoreAliasPSW println("====== signingConfigs.release ======") } }
相应的,buildTypes也可以配置成这样
buildTypes { debug { println("====== buildTypes.debug ======") signingConfig signingConfigs.debug } release { //是否混淆 minifyEnabled false //是否移除无用资源 zipAlignEnabled true //混淆配置 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' //签名文件存在,则签名 if (keyfile.exists()) { println("WITH -> buildTypes -> release: using jks key") signingConfig signingConfigs.myConfig }else { println("WITH -> buildTypes -> release: using default key") } applicationVariants.all { variant -> variant.outputs.each { output -> def outputFile = output.outputFile if (outputFile != null && outputFile.name.endsWith('.apk')) { // 输出apk名称为ruijie_v1.0_wandoujia.apk def fileName = "ruijie_v${defaultConfig.versionName}_${variant.productFlavors[0].name}.apk" output.outputFile = new File(outputFile.parent, fileName) } } } }
这样的话,当我们的项目是开源的,那么我们不会上传签名文件,我们通过上述代码keyfile.exists()进行判断是否有签名,那么其他人打包出来的apk是未签名的。所以能够保证apk签名的安全性。
附加一下:build.gradle的相关配置:
有时候我们打包会显示lint报错,那么我们可以添加
lintOptions { abortOnError false }
dx工具(android将jar包转成dex格式二进制jar包工具)
这将应用所有使用dex的task。解决65535的配置dexOptions { incremental true javaMaxHeapSize "4g" //关闭预编译 preDexLibraries = false }
或者指定JDK的编译版本
compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 }
打包忽略掉第三方jar相同的文件
packagingOptions { exclude 'META-INF/DEPENDENCIES.txt' exclude 'META-INF/LICENSE.txt' exclude 'META-INF/NOTICE.txt' exclude 'META-INF/NOTICE' exclude 'META-INF/LICENSE' exclude 'META-INF/DEPENDENCIES' exclude 'META-INF/notice.txt' exclude 'META-INF/license.txt' exclude 'META-INF/dependencies.txt' exclude 'META-INF/LGPL2.1' }
最后,附上build.gradle部分配置的相关代码:
apply plugin: 'com.android.application'apply plugin: 'me.tatarka.retrolambda'apply plugin: 'android-apt'def keystoreFilepath = ''def keystorePSW = ''def keystoreAlias = ''def keystoreAliasPSW = ''// default keystore file, PLZ config file path in local.propertiesdef keyfile = file('s.keystore.temp')Properties properties = new Properties()// local.properties file in the root directorproperties.load(project.rootProject.file('local.properties').newDataInputStream())keystoreFilepath = properties.getProperty("keystore.path")if (keystoreFilepath) { keystorePSW = properties.getProperty("keystore.password") keystoreAlias = properties.getProperty("keystore.alias") keystoreAliasPSW = properties.getProperty("keystore.alias_password") keyfile = file(keystoreFilepath)}android { compileSdkVersion 24 buildToolsVersion "24.0.2" defaultConfig { applicationId "com.example.vizax.with" minSdkVersion 15 targetSdkVersion 24 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" multiDexEnabled true } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } // 这个是解决lint报错的代码 lintOptions { abortOnError false } dexOptions { incremental true javaMaxHeapSize "4g" //关闭预编译 preDexLibraries = false } /*android { productFlavors { kuan { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "kuan"] } xiaomi { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi"] } qh360 { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "qh360"] } baidu { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"] } wandoujia { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"] } } }*/ android { productFlavors { kuan {} xiaomi {} qh360 {} baidu {} wandoujia {} yingyongbao {} } productFlavors.all { flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] } } signingConfigs { debug { storeFile keyfile storePassword keystorePSW keyAlias keystoreAlias keyPassword keystoreAliasPSW println("====== signingConfigs.debug ======") } myConfig { storeFile keyfile storePassword keystorePSW keyAlias keystoreAlias keyPassword keystoreAliasPSW println("====== signingConfigs.release ======") } /*release { storeFile file('../with.jks') //签名文件路径 storePassword "with123456" keyAlias "with" keyPassword "with123456" //签名密码 }*/ } //打包忽略掉第三方jar相同的文件 packagingOptions { exclude 'META-INF/DEPENDENCIES.txt' exclude 'META-INF/LICENSE.txt' exclude 'META-INF/NOTICE.txt' exclude 'META-INF/NOTICE' exclude 'META-INF/LICENSE' exclude 'META-INF/DEPENDENCIES' exclude 'META-INF/notice.txt' exclude 'META-INF/license.txt' exclude 'META-INF/dependencies.txt' exclude 'META-INF/LGPL2.1' } /*signingConfigs代码块一定要写在buildTypes前面,否则会报下面这种错: Could not find property 'debugConfig' on SigningConfig container. 签名密码写在gradle中不安全,故最好在打包上线的时候将相关代码注释掉。*/ buildTypes { debug { println("====== buildTypes.debug ======") signingConfig signingConfigs.debug } release { //是否混淆 minifyEnabled false zipAlignEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' if (keyfile.exists()) { println("WITH -> buildTypes -> release: using jks key") signingConfig signingConfigs.myConfig }else { println("WITH -> buildTypes -> release: using default key") } applicationVariants.all { variant -> variant.outputs.each { output -> def outputFile = output.outputFile if (outputFile != null && outputFile.name.endsWith('.apk')) { // 输出apk名称为ruijie_v1.0_wandoujia.apk def fileName = "ruijie_v${defaultConfig.versionName}_${variant.productFlavors[0].name}.apk" output.outputFile = new File(outputFile.parent, fileName) } } } } } /* 左下角打开Terminal,输入命令行 如果我们想打包wandoujia渠道的release版本,执行如下命令就好了: gradlew assembleWandoujiaRelease 如果我们想打包wandoujia渠道的debug版本,执行如下命令就好了: gradlew assembleWandoujiaDebug 如果我们只打wandoujia渠道版本,则: gradlew assembleWandoujia 此命令会生成wandoujia渠道的Release和Debug版本 同理我想打全部Release版本: gradlew assembleRelease*/}
至于混淆的相关配置,稍后我在下一篇博客进行详细讲解。
如果有任何疑问,欢迎留言。谢谢。