gradle学习-gradle实战总结

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.comhttp://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')
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351