使用共享库
Android 的包有本身就包含在Android SDK中的,系统会自动帮我们链接它们,不会出现找不到相关类的情况。此外,还有一些库,比如 com.google.andorid.maps,android.test.tunner 等独立的库,它们并不会被系统自动链接,所以我们如果要使用它们的话,就需要单独进行生成使用,这些库被称为共享库
。
我们可以在AndroidManifest 文件中,指定要使用的库。如,我们要声明需要使用 maps 这个共享库:
<uses-library
android:name = "com.google.android.maps"
android:required = "true"/>
声明之后,在安装生成的APK包的时候,系统就会根据我们的定义,帮助检测手机系统是否有我们需要的共享库,如果系统不满足,将不能安装该应用。
在Android中,除了标准的SDK,还存在两种库。:一种是add-ons
库,位于add-ons 目录下,大都为第三方厂商开发;一种是optional
可选库,它们位于 platforms/android-xx/optional 目录下,一般是为了兼容旧版本的 API ,比如 org.apache.http.legacy ,这是一个 HttpCilent 库,从API 23开始,标准SDK中就不再包含 HttpClient 库,如果还想使用该库,就必须使用这个可选库。
对于第一种库来说,Android Gradle 会帮我们自动解析,帮我们添加到 classpath 里,但第二种库就不会了,我们需要使用useLibrary
方法手动添加。
android {
useLibrary 'org.apache.http.legacy '
}
动态配置 AndroidManifest 文件
动态配置 AndroidManifest 文件,顾名思义,就是在构建过程中修改 AndroidManifest 文件中的一些内容。如:在生成不同渠道包的时候为其指定不同的渠道名。对于这种情况,Android Gradle 提供了非常便携的方法让我们来替换 AndroidManifest 文件中的内容,即manifestPlaceholder
,Manifest 占位符。
manifestPlaceholder
是ProductFlavors
的一个属性,是一个map类型,因此我们可以同时配置多个占位符。
下面我们以定义 Google 和 Baidu 两个渠道举例:
android{
complieSdkVersion 23
buldToolsVersion "23.0.1"
productFlavors {
google {
manifestPlaceholders.put("UMENG_CHANNEL", "google")
}
baidu{
manifestPlaceholders.put("UMENG_CHANNEL", "baidu")
}
}
}
我们使用的key是一样的,它就是在 AndroidManifest 文件中的占位符变量。在构建的时候,它会把 AndroidManifest 文件中所有占位符变量为 UMNENG_CHANNEL 的内容替换为 manifestPlaceholders 中对应的 value值,我们只需直接引用对应key即可。
<meta-data android:value = "${UMENG_CHANNEL}" android:name="UMENG_CHANNEL"/>
如果渠道过多,我们可以通过迭代productFlavors批量的方式进行修改:
android{
complieSdkVersion 23
buildToolsVersion "23.0.1"
productFlavors{
google{}
baidu{}
}
productFlavors.all { flavors ->
manifestPlaceholders.put("UMENG_CHANNEL", name)
}
}
我们通过all 函数遍历每一个 productFlavor ,然后把它们的name作为渠道名。当然,manifestPlaceholders
的使用场景绝不限于渠道名这一个,比如还有ContentProvider 的 auth 的授权,或者其他动态配置meta信息等,灵活运用它能帮助我们做很多事情。
自定义 BuildConfig
BuildConfig 这个类是由 Android Gradle 构建脚本在编译后自动生成的,默认情况下一般是这样的:
pubilc final class BuildConfig {
//打包模式
pubilc static final boolean DEBUG = Boolean.parseBoolean("true");
//包名
pubilc static final Sting APPLICATION_ID = "..."
//构建类型
pubilc static final Sting BUILD_TYPE = "debug"
//构建的渠道
pubilc static final Sting FLAVOR = "baidu"
//版本号
pubilc static final int VERSION_CODE = 1;
//版本名称
pubilc static final Sting VERSION_NAME = "1.0.0"
}
DEBUG 这个常量需要介绍一下,一般在开发过程中都会输出日志进行调试,一般只有在我们自己开发中才会打印出日志,当我们发布后就不能打印日志了,我们就需要一个标记是debug模式还是release模式的开关,这就是 BuildConfig.DEBUG。在debug模式下它的值为true,而在release模式下会变为false,Gradle 会自动生成、修改。
既然这个BuildConfig这么好用,我们可不可以自己定义、新增些常量呢?答案当然是可以的。对此, Android Gradle 提供了 buildConfigField(String type, String name, String value),让我们可以添加自己的常量到 BuildConfig 中。
最终生成的字段格式如下:
<type> <name> = <value>;
举个例子:
android {
buildTypes {
debug {
buildConfigField 'String','NAME','"value"'
}
}
}
自定义BuildConfig非常灵活,可以根据不同的渠道,不同的构建类型来灵活配置APP。
动态添加自定义的资源
Android 开发会用到在res文件夹定义的资源,在工程里引用即可使用。本节我们讲的自定义资源,是专门针对 res/values 类型资源的,它们不止能在 res/values 文件夹里使用 xml 的方式定义,还可以在 Android Gradle 中定义。
实现此功能的是 resValue 方法,它在 BuildType 和 ProdctFlavor 这两个对象中都存在,也就是说我们可以分别针对不同的渠道,或者不同的构建类型来自定义其特有的资源。以 resValue 方法为例。
resValue 方法会添加生成一个资源,效果与在 res/values 文件中定义一个资源是等价的。
该方法有 3 个参数,第一个是 type ,即定义的资源类型,如:string , id , bool 等;第二个是 name,也就是你要定义资源的名称,以便我们在工程中引用它;第三个是 value ,即定义资源的值。
android {
...
//该方法当然也可以在 buildType 中使用
productFlavors {
google {
resValue 'string','channel_tips','google 渠道欢迎你'
}
baidu {
resValue 'string','channel_tips','baidu渠道欢迎你'
}
}
}
当使用该方法时, Android Gradle 帮我们生成的资源在 build/gengerated/res/resValues/baidu/debug/values/generated.xml
中。
DEX 选项配置
Android 中的Java源代码被编译成 class 字节码后,在打包成 apk 的时候又被 dx 命令优化成 Android 虚拟机可执行的 dex 文件。dx 命令的处理,Android Gradle 已经帮我们处理好了,但有时可能会遇到 OOM 错误。为什么会提示内存不足呢?因为 dx 命令只是一个脚本,它调用的还是 Java 编写的 dx.jar 库,是Java 程序处理的。dx 的默认内存是1Gb,即1024Mb。
android {
...
dexOptions{
//增量模式
incremental true
// 最大内存
javaMaxHeapSize = '4g'
// jumbo 模式
jumboMode true
// 是否预执行dex Libraries 库工具
preDexLibrary true
// 运行 dx 命令的线程数
threadCount 2
}
}
突破 65535 方法限制
由于Dalvik虚拟机
在执行DEX文件
的时候,使用了short
这个类型来索引DEX文件中的方法,导致单个DEX文件可以被定义的方法最多只能是65535
个,当我们定义的方法超过这个数量时,就会出现错误提示信息。
Android 5.0给出了解决方案,即 Multidex
。ART 虚拟机
可以支持多个DEX文件,在安装APP的时候执行预编译,将多个DEX文件合并成一个oat
文件执行。
android {
...
defaultConfig{
//启用multidex
multiDexEnabled true
}
}
dependencies {
compile fileTree(dir:'libs, include:['*.jar'])
compile 'com.android.support:multidex:1.0.1'
}
其他方法:插件化
自动清理未使用的资源
方法一:自行寻找,手动删除
方法二:使用Android Lint 检测,手动删除
方法三:Resource Shrinking(打包成APK前,检测所有资源是否被引用,如果没有将不打进APK包中) + Code Shrinking(清理无用代码)
keep 方法配置XX资源不被清理
resConfig 只要需要的资源
其他
批量修改生成的 APK 文件名
如要修改生成的 APK 文件名,就要了解这几个属性:
applicationVariants
, libraryVariants
, testVariants
动态生成版本信息
四种方法:配置在build中,分模块配置,从git tag中获取,从属性文件(version.gradle)中动态获取和递增
隐藏签名信息
将签名信息和密钥放在服务器中
Java 编译选项
android {
...
compileOptions {
encoding = 'utf-8'
// Java 源代码的编译级别
sourceCompatibility = JavaVersion.VERSION_1_8
// 生成Java 字节码的版本
targetCompatibility = JavaVersion.VERSION_1_8
}
}
adb 操作选项配置
android {
...
adbOptions{
//设置超时时间 -- CommondRejectException
timeOutInMs = 5*1000 //秒
// -l : 锁定该应用程序 lock
// -r : 替换已存在的应用程序 replace
// -t : 允许测试包 test
// -s : 将APP安装到 SD 卡上 sdcard
// -d : 允许APP降级安装 degrade
// -g : 为该应用授予所有运行时权限 god
intallOptions '-r','-s'
}
}
参考文章:
《Android Gradle 权威指南》