《Gradle权威指南》--Android Gradle高级自定义
No1:
指定共享库
<uses-library android:name="com.google.android.maps" android:required="true"/>
No2:
android除了标准的sdk,还存在两种库
1)add-on库:位于add-ons目录下,大部分是第三方厂商或者公司开发的
2)optional可选库:位于platforms/android-xx/optional目录下,一般是为了兼容旧版本的API,比如HttpClient的org.apache.http.legacy
第一种Android Gradle会自动解析并添加到classpath里,第二种就不会
No3:
android{ useLibrary 'org.apache.http.legacy' }
No4:
批量修改生成的apk文件名
android{ compileSdkVersion 23 buildToolsVersion "23.0.1" useLibrary 'org.apache.http.legacy' defaultConfig{ applicationId "org.flysnow.app.example92" minSdkVersion 14 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes{ release{ minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro' zipAlignEnabled true } } productFlavors{ google{ } } applicationVariants.all{ variant->variant.outputs.each{ 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 apkFile = new File(output.outputFile.getParent(),"Example92_${flavorName}_v${variant.versionName}_${buildTime()}.apk") output.outputFile = apkFile } } } } def buildTime{ def date = new Date() def formattedDate = date.format('yyyyMMdd') return formattedDate }
每一个ApplicationVariant至少有一个或多个outputs输出
No5:
Android支持基于文件的模块化,就是apply from
version.gradle文件
ext{ appVersionCode = 1 appVersionName = "1.0.0" }
build.gradle文件中引用
apply from: 'version.gradle'
No6:
获取当前的tag名称
git describe --abbrev=0 --tags
No7:
动态从git tag中获取应用的版本名称
def getAppVersionName(){ def stdout = new ByteArrayOutputStream() exec{ commandLine 'git','describe','--abbrev=0','-tags' standardOutput = stdout } return stdout.toString() }
调用
android{ compileSdkVersion 23 buildToolsVersion "23.0.1" defaultConfig{ applicationId "org.flysnow.app.example93" minSdkVersion 14 targetSdkVersion 23 versionCode appVersionCode versionName getAppVersionName() } }
No8:
以git tag的数量作为其版本号
def getAppVersionCode(){ def stdout = new ByteArrayOutputStream() exec{ commandLine 'git','tag','--list' standardOutput = stdout } return stdout.toString().split("\n").size() }
调用
android{ compileSdkVersion 23 buildToolsVersion "23.0.1" defaultConfig{ applicationId "org.flysnow.app.example93" minSdkVersion 14 targetSdkVersion 23 versionCode getAppVersionCode() versionName getAppVersionName() } }
No9:
隐藏签名文件信息(签名文件放在服务器,兼容debug打包)
android{ compileSdkVersion 23 buildToolsVersion "23.0.1" signingConfigs{ def appStoreFile = System.getenv("STORE_FILE") def appStorePassword = System.getenv("STORE_PASSWORD") def appKeyAlias = System.getenv("KEY_ALIAS") def appKeyPassword = System.getenv("KEY_PASSWORD") //当不能从环境变量里获取到签名信息的时候,就使用项目中带的debug签名 if(!appStoreFile||!appStorePassword||!appKeyAlias||!appKeyPassword){ appStoreFile = "debug.keystore" appStorePassword = "android" appKeyAlias = "androiddebugkey" appKeyPassword = "android" } release{ storeFile file(appStoreFile) storePassword appStorePassword keyAlias appKeyAlias keyPassword appKeyPassword } } }
No10:
动态配置AndroidManifest文件
ManifestPlaceholders是ProductFlavor的一个属性,是一个Map类型,所以我们可以同时配置很多个占位符
<meta-data android:value="${UMENG_CHANNEL}" android:name="UMENG_CHANNEL"> android{ compileSdkVersion 23 buildToolsVersion "23.0.1" productFlavors{ google{ manifestPlaceholders.put("UMENG_CHANNEL","google") } baidu{ manifestPlaceholders.put("UMENG_CHANNEL","baidu") } } }
它会把AndroidManifest文件中所有占位符变量为UMENG_CHANNEL的内容替换为manifestPlaceholders中对应的value的值
缩写
android{ compileSdkVersion 23 buildToolsVersion "23.0.1" productFlavors{ google{} baidu{} } productFlavors.all{ flavor->manifestPlaceholders.put("UMENG_CHANNEL",name) } }
No11:
自定义你的BuildConfig
public final class BuildConfig{ public static final boolean DEBUG = Boolean.parseBoolean("true"); public static final String APPLICATION_ID = "org.flysnow.app.example96"; public static final String BUILD_TYPE = "debug"; public static final String FLAVOR = "baidu"; public static final int VERSION_CODE = 1; public static final String VERSION_NAME = "1.0.0"; }
获取当前包名
context.getPackageName() //更方便,性能更快 BuildConfig.APPLICATION_ID
注:DEBUG常量在debug模式下的值是true,在release模式下自动变为false
新增一些常量
public void buildConfigField(@NonNull String type,@NonNull String name,@NonNull String value){}
第一个参数type是要生成字段的类型,第二个参数name是要生成字段的常量名字,第三个参数value是要生成字段的常量值。最终生成字段格式:<type> <name> = <value>
例:安装渠道包的时候打开相应的网页
android{ compileSdkVersion 23 buildToolsVersion "23.0.1" defaultConfig{ applicationId "org.flysnow.app.example96" minSdkVersion 14 targetSdkVersion 23 versionCode 1 versionName '1.0.0' } productFlavors{ google{ buildConfigField 'String','WEB_URL','"http://www.google.com"' } baidu{ buildConfigField 'String','WEB_URL','"http://www.baidu.com"' } } }
BuildConfig类就会生成相应的常量
public static final String WEB_URL = "http://www.baidu.com";
我们通过BuildConfig.WEB_URL使用即可,在打包的时候,Android Gradle会帮我们自动生成不同的值
No12:
动态添加自定义的资源
res/Values文件夹里可以使用xml方式定义,还可以在我们的Android Gradle中定义,实现这一功能的正是resValue方法
public void resValue(@NonNull String type,@NonNull String name,@NonNull String name,@NonNull String value){ ClassField alreadyPresent = getResValues().get(name); if(alreadyPresent != null){ String flavorName = getName(); if(BuilderConstants.MAIN.equals(flavorName)){ logger.info("DefaultConfig:resValue '{}' value is being replaced: {} -> {}",name,alreadyPresent.getValue(),value); }else{ logger.info("ProductFlavor({}):resValue'{}' value is being replaced:{} -> {}",flavorName,name,alreadyPresent.getValue(),value); } } addResValue(AndroidBuilder.createClassField(type,name,value)); }
例:
android{ compileSdkVersion 23 buildToolsVersion "23.0.1" productFlavors{ google{ resValue 'string','channel_tips','google渠道欢迎你' } baidu{ resValue 'string','channel_tips','baidu渠道欢迎你' } } }
生成
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="channel_tips" translatable="false">baidu渠道欢迎你</string> </resources>
No13:
Java编译选项
compileOptions来对Java编译选项进行配置
android{
compileSdkVersion 23
buildToolsVersion "23.0.1"
compileOptions{
encoding = 'utf-8'
sourceCompatibility = JavaVersion.VERSION_1_6
targetCompatibility = JavaVersion.VERSION_1_6
}
}
注:scourceCompatibility是配置Java源代码的编译级别,比如1.5/1.6这样
targetCompatibility是配置生成的Java字节码的版本
No14:
adb操作选项配置--adbOptions
public class AdbOptions implements com.android.builder.model.AdbOptions{ int timeOutInMs; List<String> installOptions; @Override public int getTimeOutInMs(){ return timeOutInMs; } public void timeOutInMs(int timeOutInMs){ this.timeOutInMs(timeOutInMs); } public void timeOutInMs(int timeOutInMs){ setTimeOutInMs(timeOutInMs); } @Override public Collection<String> getInstallOptions(){ return installOptions; } public void setInstallOptions(String option){ installOptions = ImmutableList.of(option); } public void setInstallOptions(String... options){ installOptions = ImmutableList.copyOf(options); } public void installOptions(String option){ installOptions = ImmutableList.of(option); } public void installOptions(String... options){ installOptions = ImmutableList.copyOf(options); } }
使用
android{ compileSdkVersion 23 buildToolsVersion "23.0.1" adbOptions{ timeOutInMs = 5*1000//秒 installOptions '-r','-s' } }
adb install有六个选项:
-l:锁定该应用程序
-r:替换已存在的应用程序,就是强制安装
-t:允许测试包
-s:把应用程序安装到SD卡上
-d:允许进行降级安装,就是安装的程序比手机上带的版本低
-g:为该应用授予所有运行时的权限
No15:
Dex选项配置--dexOptions{}
package com.android.builder.core; import com.android.annotation.Nullable; public interface DexOptions{ boolean getIncremental(); boolean getPreDexLibraries(); boolean getJumboMode(); @Nullable String getJavaMaxHeapSize(); @Nullable Integer getThreadCount(); }
incremental用来配置是否启用dx的增量模式
android{ compileSdkVersion 23 buildToolsVersion "23.0.1" dexOptions{ incremental true } }
javaMaxHeapSize配置执行dx命令时为其分配的最大堆内存,主要用来解决执行dx时内存不够用的情况
android{ compileSdkVersion 23 buildToolsVersion "23.0.1" dexOptions{ javaMaxHeapSize '4g' } }
jumboMode用来配置是否开启jumbo模式,解决函数数量超过65535个的问题
android{ compileSdkVersion 23 buildToolsVersion "23.0.1" dexOptions{ jumboMode true } }
preDexLibraries用来配置是否预执行dex Libraries库工程,开启后会大大提高增量构建的速度,不过可能会影响clean构建的速度
threadCount用来配置Android Gradle运行dx命令时使用的线程数量,适当提高dx的效率
android{ compileSdkVersion 23 buildToolsVersion "23.0.1" dexOptions{ threadCount 2 } }
No16:
app方法数为什么有65535这个限制?
Dalvik虚拟机在执行DEX文件的时候,它使用了short这个类型来索引DEX文件中的方法,所以单个DEX文件可以被定义的方法最多只能是65535个。
No17:
Android 5.0之后,使用了ART的运行时方式,可以天然支持app有多个DEX文件。ART在安装app的时候执行预编译,把多个DEX文件合并成一个oat文件执行。
No18:
突破65535方法限制
android{ compileSdkVersion 23 buildToolsVersion "23.0.1" defaultConfig{ applicationId "org.flysnow.app.example911" minSdkVersion 14 targetSdkVersion 23 versionCode 1 versionName '1.0.0' //启用multidex multiDexEnabled true } } dependencies{ compile fileTree(dir: 'libs',include:['*.jar']) compile 'com.android.support:multidex:1.0.1' }
1)如果没有自定义Application,直接使用MultiDexApplication
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.flysnow.app.example911"> <application ... android:name="android.support.multidex.MultiDexApplication"> ... </application> </manifest>
2)如果有自定义Application,直接继承MultiDexApplication即可
3)如果自定义Application继承了其他第三方提供的Application,就需要重写attachBaseContext方法
public class Example911Application extends Application{ @Override protected void attachBaseContext(Context base){ super.attachBaseContext(base); MultiDex.install(this); } }
因为继承MultiDexApplication也是通过重写attachBaseContext方法然后MultiDex.install(this)方法实现的
No19:
自动清理未使用的资源--Resource Shrinking
android{ compileSdkVersion 23 buildToolsVersion "23.0.1" buildTypes{ release{ minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro' } } }
避免误删--keep(文件位置:res/raw/keep.xml)
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"/>
只用中文的语言资源
android{ compileSdkVersion 23 buildToolsRevision "23.0.1" defaultConfig{ applicationId "org.flysnow.app.example912" minSdkVersion 14 targetSdkVersion 23 versionCode 1 versionName '1.0.0' resConfigs 'zh' } }
动清理资源只是在打包的时候,不打包到apk中,并没有删除工程中的资源