一、生成签名apk
之前我们都是通过Android Studio来将程序安装到手机上的,而它背后实际的工作流程是Android Studio会将程序代码打包成一个APK文件,然后将这个文件传送到手机上,最后再执行安装操作。Android系统会将所有的APK文件识别为应用程序的安装包,但并不是所有的APK文件都能成功安装到手机上,Android系统要求只有签名后的APK文件才可以安装,那么为什么通过Android Studio运行程序的时候没有进行过签名操作还能将程序安装到手机上?这是因为Android Studio使用了一个默认的keystore文件帮我们自动进行了签名。下面介绍两种生成签名文件的方法:
1、使用Android Studio生成
点击Android Studio导航栏上的Build->Generate Signed APK,会弹出如下的创建签名APK对话框:
由于目前我们还没有一个正式的keystore文件,所以点击Create new按钮,然后会弹出一个新的对话框来让我们填写keystore文件所必要的信息,如下图:
key store path一般创建在该项目下,自定义名称即可:
填写完以上所有的信息后点击OK,这时我们刚才填写的信息会自动填充到创建签名APK对话框中,如下图:
然后点击Next,弹出如下对话框:
这时就要选择APK文件的输出地址了,这里默认将APK文件生成到项目的根目录下,点击Finish,然后稍等一段时间,APK文件就会生成好了,并且会在右上角弹出一个如下图所示的提示:
点击提示上的Show in Explorer可以立刻查看生成的APK文件。
2、使用Gradle生成
使用Gradle生成APK文件就要在app/build.gradle文件中配置些信息,在android闭包下添加如下信息:
signingConfigs{
config{
storeFile file('C:\\Users\\Administrator\\Desktop\\Personal\\TestRelease\\test.jks')
storePassword '123456'
keyAlias 'test'
keyPassword '123456'
}
}
继续编辑该文件,在buildTypes下面的release闭包中添加如下信息:
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//添加
signingConfig signingConfigs.config
}
}
即在buildTypes下面的release闭包中应用了刚才添加的签名配置,这样当生成正式版APK文件的时候就会自动使用刚才配置的签名信息来进行签名了,现在build.gradle文件已经配置完成,那么如何生成APK文件呢?其实Android Studio中内置了很多的Gradle Tasks,其中就包含了生成APK文件的Task。点击右侧工具栏的Gradle->项目名->:app->Tasks->build,如下图:
其中assembleDebug用于生成测试版的APK文件,assembleRelease用于生成正式版的APK文件。在生成APK之前,先要双击clean这个Task来清理一下当前项目,然后双击assembleRelease,结果如下图:
可以看到assembleRelease执行成功了,APK文件会自动生成在app/build/outputs/apk目录下,如下图:
虽然现在APK文件已经成功生成了,不过还有一个小细节需要注意一下。目前这种方式keystore文件的所有信息都是以明文的形式直接配置在build.gradle中,注意不太安全。Android推荐的做法是将这类敏感数据配置在一个独立的文件里面,然后在build.gradle中去读取这些数据。
下面按照这种方式来实现。Android Studio项目的根目录下有一个gradle.properties,它是专门用来配置全局键值对数据的,我们在gradle.properties文件中添加如下信息:
KEY_PATH=C:/Users/Administrator/Desktop/Personal/TestRelease/test.jks
KEY_PASS=123456
ALIAS_NAME=test
ALIAS_PASS=123456
可以看到,这里将keystore文件的各种信息以键值对的形式进行了配置,然后我们在build.gradle中去读取这些数据就可以了。编辑app/build.gradle的signingConfigs闭包,如下:
signingConfigs{
config{
storeFile file(KEY_PATH)
storePassword KEY_PASS
keyAlias ALIAS_NAME
keyPassword ALIAS_PASS
}
}
这样直接查看build.gradle文件是无法看到keystore文件的各种信息,只有查看gradle.properties文件才能看得到,然后我们只需要将gradle.properties文件保护好就行了,比如说将它从Git版本控制中排除。这样gradle.properties文件就只会保留在本地,从而就不用担心keystore文件的信息会泄露了。
二、生成多渠道APK文件
目前Android领域的应用商店非常多,不像苹果只有一个App Store。当然我们完全可以使用同一个APK文件来上架不同的应用商店,但是如果你有一些特殊需求的话,比如说针对不同的应用商店渠道来定制不同的界面,或者为了统计每个渠道的下载及其他数据,这就比较头疼了。传统情况下,开发这种差异性需求非常痛苦,通常需要维护多份代码版本,然后逐个打成相应渠道的APK文件。一旦有任何功能变更就苦不堪言,因为每份代码版本里面都需要逐个修改一遍。幸运的是,现在Android Studio提供了一种非常方便的方法来应对这种差异性需求,极大程度地解决了之前版本维护困难的问题。下面来进行学习。
1、普通多渠道APK
比如说我们准备生成360和伴读两个渠道的APK文件,那么修改app/build.gradle文件,如下:
productFlavors{
c360{
applicationId "com.testrelease.android.c360"
}
baidu{
applicationId "com.testrelease.android.baidu"
}
}
可以看到,这里添加了一个productFlavors的闭包,然后在该闭包中添加所有的渠道配置就可以了。注意Gradle中的配置规定不能以数字开头,因此这里将360的渠道名配置成了c360。渠道名的闭包中可以覆写defaultConfig中的任何属性,比如说这里将applicationId属性进行了覆写,那么最终生成的各渠道APK文件的包名也将各不相同。
接下来我们开始针对不同渠道编写差异性需求。在app/src目录下新建一个baidu目录,然后在baidu目录下再新建java和res这两个目录,如下图:
这样我们就可以在这里编写百度渠道特有的功能了,java目录用于存放代码,res目录用于存放资源,如果需要覆写AndroidManifest文件中的内容,还可以在baidu目录下再新建一个AndroidManifest.xml文件。当然,实际上我们并没有什么渠道差异性的需求,在这里只是为了演示,就给不同渠道的APK起一个不同的应用名吧。
应用名之前是定义在main/res/values/string.xml文件中的,那么我们在baidu目录下也建立一个相同的目录结构,然后将baidu/res/vaules/string.xml中的内容进行如下修改:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">测试百度版</string>
</resources>
这样百度渠道的APK就会使用baidu/res/values/string.xml中定义的应用名来覆盖原有的应用名。同样的道理,新建qihoo目录,进行类似的操作。这样我们就以一个简单的示例实现渠道差异性需求了,下面开始来生成多渠道的APK文件,用以上的两种方式都可以,个人偏好使用Android Studio提供的可视化工具来生成,如下图:
到这里可以选择生成的渠道包,如果想生成多个,按住CTRL键进行多选就可以了。这里我们全选,生成结果如下:
下面就是安装到手机了,结果如下:
可以看到,3个版本成功安装到手机上,这是由于之前我们在productFlavors中覆写了各渠道的applicationId属性,保证每个APK文件的包名都不想同,因而它们才能安装到同一个设备上。
2、友盟多渠道APK
由于国内Android市场众多渠道,为了统计每个渠道的下载及其它数据统计,就需要我们针对每个渠道单独打包,如果让你打几十个市场的包岂不烦死了,不过有了Gradle,这再也不是事了。
第一步:在AndroidManifest.xml里配置PlaceHolder
<meta-data第二步:在build.gradle设置productFlavors
android:name="UMENG_CHANNEL"
android:value="${UMENG_CHANNEL_VALUE}" />
productFlavors{或者批量设置:
c360{
manifestPlaceholders=[UMENG_CHANNEL_VALUE:"c360"]
}
baidu{
manifestPlaceholders=[UMENG_CHANNEL_VALUE:"baidu"]
}
wandoujia{
manifestPlaceholders=[UMENG_CHANNEL_VALUE:"wandoujia"]
}
xiaomi{
manifestPlaceholders=[UMENG_CHANNEL_VALUE:"xiaomi"]
}
}
productFlavors{很简单清晰有没有,后面就是打包了,按照之前的两种方式,任选一种即可,结果如下:
c360{}
baidu{}
wandoujia{}
xiaomi{}
}
productFlavors.all{
flavor->flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
}