1、获取properties配置文件的值
def getLocalProperties(String key, Object defValue) {
try {
Properties properties = new Properties()
properties.load(new File(rootDir.absolutePath + "/local.properties").newDataInputStream())
def value = properties.getProperty(key, defValue)
return value
} catch (Exception e) {
return defValue
}
}
task hello << {
def sdkPath = getLocalProperties('sdk.dir','~~')
println "sdk 的路径为 ${sdkPath}"
}
执行./gradlew hello 命令,运行结果如下:
Task :hello
sdk 的路径为 /Users/lianjia/Library/Android/sdk
2、gradle执行脚本,并获取脚本的返回值
- 通过设置CommondLine参数的方式执行脚本
task getGitVersion << {
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git','--version'
standardOutput = stdout
}
def version = stdout.toString().substring(stdout.toString().indexOf('n')+1)
println "git版本号为:${version}"
}
运行./gradlew getGitVersion,结果如下:
Task :getGitVersion
git版本号为: 2.18.0
- 通过设置args参数的方式执行脚本
插件化项目中用到这个task,用adb命令把插件拷贝到plugins目录下,以便debug的时候,直接这条命令便可debug我们插件开发的代码
task pushPlugin() {
doFirst {
def apkExistPath = transferPath(
new File(project.getBuildDir().absolutePath + "/outputs/apk", pluginExistName).absolutePath)
def command = "adb shell mkdir sdcard/plugins; adb push -p $apkExistPath sdcard/plugins/$pluginPushName; adb shell am force-stop $packageName"
exec {
try {
executable 'bash'
args "-c", "$command"
} catch (Exception e) {
println e.message
println("=====================push plugin failed with exception.=========================")
}
}
println("=====================push plugin successfull.=========================")
}
doLast {
restart.execute()
}
}
这是gradle特别牛的地方,我们不仅可以执行各种bash命令,例如git、adb、python等
3、自定义你的BuildConfig
Android打包完之后,在build文件夹下会生成一个BuildConfig类,字段包含了项目构建的一些基本信息,比如:ApplicationId,buildType,versionName和versionCode等
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.lianjia.link.linkplugin";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
}
我们可以通过buildConfigField(String type,String name,String value)让我们可以添加自己的常量到BuildConfig
如果我们需要在不同的渠道中添加不同的一些参数,那么我们便可以使用buildConfigField这个方法实现,如下所示项目中配置了两个渠道,google和baidu,如果是google渠道,那么我们配置了WEB_URL的值分别为http://www.google.com和http://www.baidu.com
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.lianjia.link.linkplugin"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
buildConfigField 'String', 'WEB_URL', '"http://www.baidu.com"'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
productFlavors{
google {
buildConfigField 'String','WEB_URL','"http://www.google.com"'
}
baidu {
buildConfigField 'String','WEB_URL','"http://www.baidu.com"'
}
}
}
build项目之后查看一下BuildConfig这个类已经添加了WEB_URL的常量
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.lianjia.link.linkplugin";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
// Fields from default config.
public static final String ALL_MODEUL = "app,router";
public static final String WEB_URL = "http://www.baidu.com";
}
4、动态配置AndroidManifest文件
动态配置AndroidManifest文件,顾名思义是可以在构建的过程中,动态修改AndroidManifest文件中的一些内容。这样的例子非常多,比如使用友盟等第三方分析统计的时候,会要求我们在AndroidManifest文件中指定渠道名称:
<meta-data android:name="UMENG_CHANNEL" android:value="${UMENG_CHANNEL}"/>
对于这种情况我们不可能定义很多个AndroidManifest文件,因为这种工作太繁琐,而且维护麻烦。所以就需要在构建的时候,根据正在生成的不同渠道包来为其制定不同的渠道名称。
对于这种情况,AndroidGradle提供了非常便捷的方法让我们来替换AndroidManifest文件中的内容,它就是ManifestPlaceHolder、Manifest占位符。
ManifestPlaceholders是ProductFlavor的一个属性,是一个Map类型,所以我们同时可以配置多个占位符。下面我们在google和baidu两个渠道中配置了google和baidu的渠道名,如下:
productFlavors{
google {
manifestPlaceholders.put("UMENG_CHANNEL","google")
}
baidu {
manifestPlaceholders.put("UMENG_CHANNEL","baidu")
}
}
build项目,打开反编译apk,我们发现baidu渠道中的渠道名称已经修改成baidu了,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0"
package="com.lianjia.link.linkplugin">
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="28" />
<application
android:theme="@ref/0x7f0c0005"
android:label="@ref/0x7f0b0027"
android:icon="@ref/0x7f0a0000"
android:debuggable="true"
android:allowBackup="true"
android:supportsRtl="true"
android:roundIcon="@ref/0x7f0a0001"
android:appComponentFactory="android.support.v4.app.CoreComponentFactory">
<activity
android:name="com.lianjia.link.linkplugin.MainActivity">
<intent-filter>
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="UMENG_CHANNEL"
android:value="baidu" />
</application>
</manifest>
5、使用resValue 动态添加自定义资源
在我们开发Android的过程中,我们会使用很多资源,有图片、动画、字符串等,这些资源我们可以在res文件夹里定义,然后在工程里引用即可使用。这里我们讲的自定义资源是专门针对res/values类型资源的,它们不光可以在res/values文件夹里使用xml的方式定义,还可以在我们的Android Gradle中定义,这大大增加了构建的灵活性。
实现这一功能的正式resValue方法,它在BuildType和ProductFlavor这两个对象都存在,也就是我们可以分别针对不同的渠道,或者不同构建类型来自定义其特有的资源。我们可以先看看ProductFlavor中的resValue方法为例,可以先看看它的源码实现:
/**
* Adds a new generated resource.
*
* <p>This is equivalent to specifying a resource in res/values.
*
* <p>See <a
* href="http://developer.android.com/guide/topics/resources/available-resources.html">Resource
* Types</a>.
*
* @param type the type of the resource
* @param name the name of the resource
* @param value the value of the resource
*/
public void resValue(@NonNull String type, @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(new ClassFieldImpl(type, name, value));
}
从其文档注释中可以看到,它会添加生成一个资源,其效果和在res/values文件夹中定义一个资源是等价的。
resValue方法有3个参数:
type:要定义的资源类型,比如string、id、boolean等
name:自定义资源的名称,以便我们在工程中引用它。
value:定义的资源值
下面开始使用了,分别在google和baidu两个渠道中定义了一个channel_tips的string类型,它们的值各不相同。如下所示:
productFlavors{
google {
resValue 'string','channel_tips','google渠道欢迎你'
}
baidu {
resValue ' ','channel_tips','baidu渠道欢迎你'
}
}
写好了配置之后,reuild一下项目,然后在/build/generated/res/resValues/baidu/debug/values下面会生成一个generated.xml文件,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Automatically generated file. DO NOT MODIFY -->
<!-- Values from product flavor: baidu -->
<string name="channel_tips" translatable="false">baidu渠道欢迎你</string>
</resources>
上面演示的事string类型,也可以使用id、boolean、integer、color等这些类型来自定义values资源。总之这个reValue方法和BuildConfigField方法非常类似。
6、批量修改生成的apk文件名
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.lianjia.link.linkplugin"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
flavorDimensions "versionCode"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
productFlavors{
google {
manifestPlaceholders.put("UMENG_CHANNEL","google")
buildConfigField 'String','WEB_URL','"http://www.google.com"'
resValue 'string','channel_tips','google渠道欢迎你'
}
baidu {
manifestPlaceholders.put("UMENG_CHANNEL","baidu")
buildConfigField 'String','WEB_URL','"http://www.google.com"'
resValue 'string','channel_tips','baidu渠道欢迎你'
}
}
applicationVariants.all { variant ->
variant.outputs.all { output ->
if(output.outputFile != null && output.outputFile.name.endsWith('.apk')){
output.outputileName = "link_${variant.flavorName}_${variant.buildType.name}_${buildTime()}.apk"
}
}
}
}
applicationVariants 是一个DomainObjectCollection集合,我们可以通过all方法进行遍历,遍历的每一个variant都是一个生成的产物。
application Variants中的variant都是Application Variant,通过查看源码,可以看到它有一个outputs作为它的输出。每一个Application Variant至少有一个输出,也可以有多个,所以这里的outputs属性是一个List集合、我们再遍历它,如果它的名字是以.apk结尾的话,我们再去修改apk的名字就好了。我们发现output的实际上是ApkVariantOutputImpl类,它实现了ApkVariantOutput接口,我们在ApkVariantOutput接口中找到了一个setOutputFileName方法,那就好办了,直接通过调用该方法便可修改生成的apk名称。
7、grale文件模块化
我们都知道项目中如果有很多依赖库的话,gradle文件会非常臃肿,不易于维护,那么此时我们可以把那些依赖库放在单独的一个gradle文件中,用ext来放这些依赖,最后通过apply from引用该文件。
config.gradle
ext {
isPlugin = true
//全局环境配置
android = [compileSdkVersion : 25,
minSdkVersion : 19,
targetSdkVersion : 22, // 不要写成23,写成23会在6.0手机上进行权限隐私校验,如果没有判断就会crash
renderscriptTargetApi: 19,
versionName : "3.26.0",
lianjiasticker : "LianjiaSticker",]
//依赖配置
dependencies = [
/*Google Library*/
libFlexbox : "com.google.android:flexbox:0.2.5",
...
]
debugDependencies = [
/*Square Library*/
libLeakCanary : "com.squareup.leakcanary:leakcanary-android:1.5.1",
/*Facebook Library*/
libStethoOkhttp3: "com.facebook.stetho:stetho-okhttp3:1.4.1",
libStetho : "com.facebook.stetho:stetho:1.3.0",]
releaseDependencies = [
/*Square Library*/
libLeakCanary: "com.squareup.leakcanary:leakcanary-android-no-op:1.5.1"]
}
[project] build.gradle
apply from: rootProject.file('lianjia.gradle')
网友评论