个人博客:

http://www.milovetingting.cn

Android Gradle 高级自定义

使用共享库

Android的包,如android.app,android.content,android.view,android.widget等,是默认包含在Android SDK库里的,所有应用都可以直接使用它们。还有一些库,如com.google.android.maps,android.test.runner等,这些库是独立的,并不会被系统自动链接,所以如果要使用的话,就需要单独进行生成使用,这类库我们称为共享库。

在AndroidManifest.xml中,我们可以指定要使用的库:

<uses-library
android:name="com.google.android.maps"
android:required="true"
/>

这样我们就声明了需要使用maps这个共享库。声明之后,在安装生成的apk包的时候,系统会根据我们的定义,帮助检测手机系统是否有我们需要的共享库。因为我们设置的android:required="true",如果手机系统不满足,将不能安装该应用。

在Android中,除了标准的SDK,还存在两种库:一种是add-ons库,它们位于add-ons目录下,这些库大部分是第三方厂商或者公司开发的,一般是为了开发者使用,但又不想暴露具体标准实现;第二类是optional可选库,它们位于platforms/androi-xx/optional目录下,一般是为了兼容旧版本的API,比如org.apache.http.legacy。

对于第一类add-ons附件库来说,Android Gradle会自动解析,帮我们添加到classpath里。第二类optional可选库就不会,需要自己将这个可选库添加到classpath中。Android Gradle提供了useLibrary方法,让我们把一个库添加到classpath中。

android{
useLibrary 'org.apache.http.legacy'
}

以上的配置已经可以生成APK,并能安装运行。但最好也要在AndroidManifest文件中配置一下uses-library标签,以防出现问题。

批量修改生成的apk文件名

Android对象为我们提供了3个属性:applicationVariants(仅仅适用于Android应用Gradle插件),libraryVariants(仅仅适用于Android库Grdle插件),testVariants(以上两种Gradle插件都适用)。

以上3个属性返回的都是DomainObjectSet对象集合,访问它们都会触发创建所有的任务。

以下为批量修改apk名称的示例:

android {
compileSdkVersion 28
defaultConfig {
applicationId "com.wangyz.gradle"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
flavorDimensions "default"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
productFlavors {
baidu { }
huawei { }
}
applicationVariants.all {
variant ->
variant.outputs.all {
output ->
if (output.outputFile != null && output.outputFile.name.endsWith('.apk') && 'release'.equals(variant.buildType.name)) {
def flavorName = variant.flavorName.startsWith("_") ? variant.flavorName.substring(1) : variant.flavorName
def fileName = "channel_${flavorName}_${variant.versionName}.apk"
outputFileName = fileName
}
}
}
}

动态生成版本信息

一般的版本由3部分组成:major.minor.patch,第一个是主版本号,第二个是副版本号,第三个是补丁号。

最原始的方式

android {
compileSdkVersion 28
defaultConfig {
applicationId "com.wangyz.channel"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}

分模块的方式

可以把版本号的配置单独抽取出来,放在单独的文件里,供build引用。

新建一个vesion.gradle文件:

ext{
appVersionCode = 1
appVersionName = "1.0.0"
}

在build.gradle中引用它:


apply from:'version.gradle' android {
compileSdkVersion 28
defaultConfig {
applicationId "com.wangyz.channel"
minSdkVersion 21
targetSdkVersion 28
versionCode appVersionCode
versionName appVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}

先使用apply from加载version.gradle脚本文件,这样它里面定义的扩展属性就可以使用了。

从git的tag中读取

想获取当前的tag名称,在git下非常简单,使用以下命令即可:

git describe --abbrev=0 --tags

知道了命令,那么如何在Gradle中动态获取呢?这就需要exec。Gradle提供了执行shell命令非常简便的方法,即exec。它是一个Task任务。

def getAppVersionName(){
def stdout = new ByteArrayOutputStream()
exec{
commandLine 'git','describe','--abbrev=0','--tags'
standardOutput = stdout
}
return stdout.toString()
}

以上定义了一个获取版本名称的方法,通过该方法获取了git tag的名称后,就可以把它作为应用的版本名称,只要把versionName配置成这个方法就好了。

android {
compileSdkVersion 28
defaultConfig {
applicationId "com.wangyz.channel"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName getAppVersionName()
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}

从属性文件中动态获取和递增

1、在项目目录下新建一个version.properties的属性文件

2、把版本名称分为3部分major.minor.patch,版本号分为一部分number,然后在properties新增4个KV键值对

3、在build.gradle新建一个方法用于读取该属性文件

隐藏签名文件信息

定义一个文件,用来保存签名的相关信息,如sign.properties,这个文件加入.gitignore,不上传到git中。通过读取这个文件来获取配置信息。

android{
signingConfigs{
release{
def signPropertiesFile = 'sign.properties'
storeFile file(readSignProperties(signPropertiesFile,'storeFile'))
storePassword readSignProperties(signPropertiesFile,'storePassword')
keyAlias readSignProperties(signPropertiesFile,'keyAlias')
keyPassword readSignProperties(signPropertiesFile,'keyPassword')
}
}
} buildTypes{
release{
signingConfig signingConfigs.release
}
} def readSignProperties(String filePath,String key){
File file = file(filePath)
if(file.exists())
{
def Properties properties = new Properties()
properties.load(new FileInputStream(file))
return properties[key]
}
return "file not exist!"
}

sign.properties内容如下:

storeFile=android.keystore
storePassword=android
keyAlias=android
keyPassword=android

动态配置AndroidManifest文件

Android Gradle 提供了非常便捷的方法让我们来替换AndroidManifest文件中的内容,它就是manifestPlaceholder,Manifest占位符。

android{
productFlavors{
google{
manifestPlaceholders = [APP_CHANNEL:"google"]
}
baidu{
manifestPlaceholders = [APP_CHANNEL:"baidu"]
}
}
}

在AndroidManifest文件中使用

<application>
<meta-data
android:name="APP_CHANNEL"
android:value="${APP_CHANNEL}"
/>
</application>

如果需要批量修改(假设需要将名称改为和渠道名一样),可以通过productFlavors迭代方法:

android{
productFlavors{
google{ }
baidu{ }
} productFlavors.all{
flavor->
manifestPlaceholder = [APP_CHANNEL:name]
}
}

自定义BuildConfig

Android Gradle 提供了buildConfigField(String type,String name,String value)让我们添加自己的常量到BuildConfig中。使用示例:

android{
productFlavors{
google{
buildConfigField 'String','URL','"http://www.google.com"'
}
baidu{
buildConfigField 'String','URL','"http://www.baidu.com"'
}
}
}

需要注意,value这个参数,是单引号中间的部分,尤其对于String类型的值,里面的双引号不能省略。value是什么就写什么,原封不动地放在单引号里。

上面是渠道,productFlavor,其实不光渠道可以配置自定义字段,构建类型BuildType也可以配置。如:

android{
buildTypes{
debug{
buildConfigField 'String','NAME','"zhangsan"'
}
}
}

动态添加自定义的资源

实现这一功能的方法是resValue方法。它在BuildType和ProductFlavor这两个对象中存在。它会生成一个资源,效果和在res/values文件中定义一个资源是等价的。

resValue方法有三个参数,第一个是type,也就是你要定义资源的类型,比如有string,id,bool等;第二个是name,也就是定义资源的名称,以便在工程中引用;第三个是value,就是定义资源的值。

android{
productFlavors{
google{
resValue 'string','tip','hello'
}
baidu{
resValue 'string','tip','hi'
}
}
}

Java编译选项

Android对象提供了一个compileOptions方法,接受一个CompileOptions类型的闭包作为参数,来对Java编译选项进行配置:

android{
compileOptions{
encoding 'utf-8'
sourceCompatibility JavaVersion.VERSION_1_6
targetCompatibility JavaVersion.VERSION_1_6
}
}

CompileOptions是编译配置,它提供三个属性,分别是encoding,sourceCompatibility,targetCompatibility,通过对它们进行设置来配置Java相关的编译选项。

sourceCompatibility是配置Java源代码的编译级别

targetCompatibility是配置生成的Java字节码的版本

adb操作选项配置

在Android Gradle 中,为我们预留了对adb的一些选项的控制配置,它就是adbOptions{}闭包。

android{
adbOptions{
timeOutInMs 5*1000
installOptions '-r','-s'
}
}

DEX选项配置

Android Gradle 提供了dexOptions{}闭包,让我们可以对dx操作进行一些配置。

android{
dexOptions{
incremental true
}
}

突破65535方法限制

Java源文件被打包成一个DEX文件,这个文件就是优化过的、Dalvik虚拟机可执行的文件,Dalvik虚拟机在执行DEX文件时,使用了short类型来索引DEX文件中的方法,这就意味着单个DEX文件可以被定义的方法最多只有是65535,当定义的方法数量超过时,就会出错。

Android官方给出的解决方案:Multidex。对于Android5.0以后的版本,使用了ART的运行方式,可以天然支持App有多个DEX文件,ART在安装App的时候执行预编译,把多个DEX文件合并成一个oat文件执行。对于Android5.0之前的版本,Dalvik虚拟机限制每个App只能有一个class.dex,要使用它们,就得使用Android提供的Multidex库。

要在项目中使用Multidex,首先要修改Gradle build配置文件,启用Multidex,并同时配置Multidex需要的jar依赖。

android{
defaultConfig{
multiDexEnabled true
}
} dependencies{
implementation 'com.android.support:multidex:1.0.1'
}

配置好之后,开启了Multidex,会让我们的方法多于65535个的时候生成多个DEX文件,名字为classes.dex,classes(...n)这样的形式。但是对于Android5.0以前的系统虚拟机,它只认识一个DEX,名字还是classes.dex,所以想达到程序可以正常运行的目的,也要让虚拟机把其它几个生成的classes加载进来。要做到这步,就必须在App程序启动的入口控制,这个入口就是Application。

Multidex提供现成的Application,名字是MultiDexApplication,如果我们没有自定义Applicaiton的话,直接使用MultiDexApplication即可,在Manifest清单文件中配置:

<application
...
android:name="android.support.multidex.MultiDexApplication"
>
...
</application>

如果有自定义的Application,并且是直接继承自Application,那么只需要把继承改为我们的MultiDexApplication即可。

如果自定义的Application是继承自第三方提供的Application,就不能改继承了,这个时候可以重写attachBaseContext方法来实现:

@Override
protected void attachBaseContext(Context base){
super.attachBaseContext(base);
MultiDex.install(this);
}

虽然有了解决65535问题的方法,但还是要尽量避免我们工程中的方法超过65535.首先不能滥用第三方库,如果引用,最好也要自己精简。精简后,还要使用ProGuard减小DEX的大小。还有因为Dalvik linearAlloc的限制,尤其在2.2和2.3版本上,只有5MB,到Android 4.0的时候升级到8MB,所以低于4.0的系统上dexopt的时候可能会崩溃。

自动清理未使用的资源

Android Gradle 为我们提供了在构建打包时自动清理未使用的资源的方法,这个就是Resource Shrinking。

Resource Shringking要结合Code Shringking一起使用,即我们开发中经常使用的ProGuard,也就是我们要启用minifyEnabled,是为了减缩代码。

android{
buildTypes{
release{
minifyEnabled true
shringkResources true
...
}
}
}

自动清理未使用的资源这个功能虽然好用,但是有时候会误删有用的程序,因为我们在代码编写的时候,可能会使用反射去引用资源,尤其很多的第三方库会这么做,这个时候Android Gradle就区分不出来,可能会误认为这些资源不有被使用。针对这种情况,Android Gradle提供了keep方法来让我们配置哪些资源不被清理。

keep方法使用非常简单,我们要新建一个xml文件来配置,这个文件是res/raw/keep.xml,然后通过tools:keep属性来设置。这个tools:keep接受一个以逗号分隔的配置资源列表,并且支持星号*通配符。

<?xml version="1.0" encoding="utf-8">
<resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@layout/layout_a,@layout/layout_b" />

keep.xml还有一个属性是tools:shrinkMode,用于配置自动清理资源的模式,默认是false,是安全的。

除了shrinkResources之外,Android Gradle 还提供了一个resConfigs,它属于ProductFlavor的一个方法,可以让我们配置哪些类型的资源才会被打包进APK中。

android{
defaultConfig{
resConfigs 'zh'
}
}

上面代码表示,只保留zh资源。

【Gradle】Android Gradle 高级自定义的更多相关文章

  1. The Android Gradle Plugin and Gradle version-compatibility

    http://tools.android.com/tech-docs/new-build-system/version-compatibility Version Compatibility Post ...

  2. Android Gradle defaultConfig详解及实用技巧

    实际项目中,都会应用Android Gradle Plugin,根据实际中的项目模块的职责,可以具体应用如下四种插件类型. 1,apply plugin: 'com.android.applicati ...

  3. 【Gradle】 Gradle 综合

    Gradle User Guide:http://www.gradle.org/docs/current/userguide/userguide.html 针对它的中文翻译:http://ask.an ...

  4. 《Gradle权威指南》--Android Gradle高级自定义

    No1: 指定共享库 <uses-library android:name="com.google.android.maps" android:required=" ...

  5. Android Gradle Plugin指南(六)——高级构建定制

    原文地址:http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Advanced-Build-Customization ...

  6. Android Gradle 自定义Task 详解

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/76408024 本文出自[赵彦军的博客] 一:Gradle 是什么 Gradle是一 ...

  7. 【Gradle】自定义Android Gradle工程

    自定义Android Gradle工程 defaultConfig默认配置 defaultConfig是Android对象中的一个配置项,负责定义所有的默认配置.一个基本的defaultConfig配 ...

  8. Android gradle 自定义插件

    Gradle 的插件有三种打包方式: 构建脚本:插件逻辑写在 build.gradle 中,适用于逻辑简单的任务,但是该方式实现的插件在该构建脚本之外是不可见的,只能用于当前脚本. buildSrc项 ...

  9. Gradle系列之Android Gradle高级配置

    本篇文章主要在之前学习的基础上,从实际开发的角度学习如何对 Android Gradle 来进行自定义以满足不同的开发需求,下面是 Gradle 系列的几篇文章: Gradle系列之初识Gradle ...

随机推荐

  1. Linux ipv6 无状态 设置为 eui64

    Linux ipv6 无状态 设置为 eui64 转载注明来源: 本文链接 来自osnosn的博客,写于 2019-08-22. 无状态的ipv6有eui64和stable-privacy模式, 在家 ...

  2. 关于MFC与OpenGL结合绘图区域用鼠标来控制图形的移动的坑

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11773171.html 之前开发的导入多个模型,旋转,分别移动什么什么的,都是在纯OpenGL ...

  3. tf.nn.in_top_k的用法

    tf.nn.in_top_k组要是用于计算预测的结果和实际结果的是否相等,返回一个bool类型的张量,tf.nn.in_top_k(prediction, target, K):prediction就 ...

  4. Java学习 1.1——(JVM介绍)Java为什么能够跨平台?

    首先介绍一下Java的各个层级,先放一张图: 硬件,操作系统和操作系统接口:这三级不说大家都知道,操作系统有很多种,比如Windows,Linux.Windows又分为win7,win10,win x ...

  5. SSH框架之Spring第四篇

    1.1 JdbcTemplate概述 : 它是spring框架中提供的一个对象,是对原始JdbcAPI对象的简单封装.spring框架为我们提供了很多的操作模板类. ORM持久化技术 模板类 JDBC ...

  6. 【Puppeteer】puppeteer安装/常用的方法以及一个小栗子(Youtube油管自动评论)

    这里介绍的是Win平台的安装方法,其他平台请至Github>Puppeteer. 首先要安装node.js 可以看我这篇的开头>[Angular]学习笔记-环境部署.项目建立相关 1.新建 ...

  7. 利用zabbix API 统计一段时间内监控项的均值和峰值

    #coding:utf-8 #给定主机IP获取一段时间内cpu的均值和峰值并写入文件(txt,可以跑完后直接把后缀名改为csv以表格形式打开):需要指定IP文件 import requests imp ...

  8. Android 程序分析环境搭建-静态分析环境搭建

    1.2 静态分析环境搭建 这里主要讲一些用的比较顺手的工具,一并列出来,Uaa,等环境配置, 2,notepad++ ,everything , Jdgui ,idea, ida ,Fiddler , ...

  9. 2019 DevOps 必备面试题——容器化和虚拟化

    原文地址:https://medium.com/edureka/devops-interview-questions-e91a4e6ecbf3 原文作者:Saurabh Kulshrestha 翻译君 ...

  10. VS2017无法打开Razor视图文件提示:引发类型"System.Exception"异常

    背景介绍 由于电脑装了R#(吃内存大户),导致VS2017打开项目慢以及卡顿,因此在扩展和更新这个功能里面将没用的插件关闭了,导致.NET CORE的Razor视图文件打不开(真心是一脸懵逼,关个插件 ...