美文网首页gradleAndroid开发Android开发经验谈
Android进阶修炼;Gradle骚操作合集

Android进阶修炼;Gradle骚操作合集

作者: 飞鱼_9d08 | 来源:发表于2020-02-26 21:41 被阅读0次

    Gradle骚操作

    Gradle是谷歌钦定的android项目构建工具。熟练使用gradle可以实现很多骚操作,比如多渠道打包,指定打包文件路径和文件名等,而且实现方式不止一种。Gradle工具的编程语言叫做 Groovy, Groovy的语法相对宽松,有点类似javascript,怎么写的人都有,但是最终目标可能是一致的。

    本文收录一些Gradle骚操作供大家分享,经本人验证可行,会提供完整Demo(Gradle会存在新旧版本兼容问题,运行demo不要改动gradle版本配置):Demo地址

    配置buildTypes{ xxx } 自定义"构建类型“

    ...
    android {
        ...
        // 配置签名文件
        signingConfigs {
            debug {
                // 这里会使用默认签名
            }
            release {
                storeFile file("mykey.jks")
                storePassword "android"
                keyAlias "android"
                keyPassword "android"
                v2SigningEnabled true
            }
        }
        //level 1: 打包方式,默认有debug和release,当然可以自己加喜欢的
        buildTypes {
    
            //内置 debug 和 release,但是我们可以忽略不计
            uat {
                // 测试环境可调试
                debuggable true
                signingConfig signingConfigs.release
            }
            prd {
                // 正式环境 可调式
                debuggable true
                signingConfig signingConfigs.release
            }
            online {
                // 正式发布包 不可调试
                debuggable false
                signingConfig signingConfigs.release
            }
    
        }
    
    }
    
    dependencies {
        ...
    }
    
    

    关键点:

    • 配置buildTypes之前,必须 配signingConfigs,把签名文件配好,否则,就会出现 install Task缺失的情况

      image
    • buildTypes里面默认就有 debug和release,这一点在as里面可以看到,下图中另外3个是我们自己加的type:

    image
    • 通过指定的buildType打包出来的apk,可以读到 当前包的buildType,并且用于app程序内部,比如配置app的网络环境设置(测试环境和正式环境),还有 是否支持日志打印屏蔽所有日志和放开所有日志),获取的方式为BuildConfig.BUILD_TYPE

      class MainActivity : AppCompatActivity() {
      
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
              setContentView(R.layout.activity_main)
              tvBuildType.text = BuildConfig.BUILD_TYPE
          }
      }
      
      

    配置productFlavors{ xxx } 自定义"多种打包风味“

    Demo地址:https://github.com/18598925736/EnjoyGradleHank/commits/master(切换到时间点:78dc2ca

    打包风味 flavor 是 谷歌提供的 多渠道打包的官方手段。

    app module 的 build.gradle

    ...
    
    android {
        ...
    
        //level 2: 通过productFlavors打包配置,实现多维度风味打包
        flavorDimensions "zone", "themeColor"
        // 定义多风味
        productFlavors {
            /**
             * 越南版  每一个风味选项都必须指定独立的风味值
             */
            vn {
                applicationId "com.global.vn.ftint"
                dimension "zone"
                manifestPlaceholders = [zone: "vn"]
            }
    
            /**
             * 国内版
             */
            cn {
                applicationId "com.global.cn.ftint"
                dimension "zone"
                manifestPlaceholders = [zone: "cn"]
            }
    
            /**
             * 主题风格为红色
             */
            red {
                dimension "themeColor"
                manifestPlaceholders = [themeColor: "red"]
            }
    
            /**
             * 主题风格为蓝色
             */
            blue {
                dimension "themeColor"
                manifestPlaceholders = [themeColor: "blue"]
            }
        }
    }
    
    dependencies {
        ...
    }
    
    

    同时必须与 manifest配合:

    app module 的 AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.zhou.enjoygradle2">
    
        <application
            ...>
            ...
            <meta-data
                android:name="ZONE"
                android:value="${zone}" />
            <meta-data
                android:name="themeColor"
                android:value="${themeColor}" />
        </application>
    
    </manifest>
    
    

    关键点:

    • 多风味打包支持多维度,定义维度是在 flavorDimensions "zone", "themeColor" 这样代码中 , 此处定义了两个维度:“地区”,“主题颜色”

    • 配置 风味 productFlavor的时候必须指定维度:以下代码就是指定维度为 zone ,维度的值,则是 cn,同时还指定了包名applicationId.

         cn {
                  applicationId "com.global.cn.ftint"
                  dimension "zone"
                  manifestPlaceholders = [zone: "cn"]
              }
      
      
    • 多种维度的笛卡尔积的数值,就是 打包方式的总个数,比如上方,zone维度2个风味,themeColor维度2个风味,所以风味的总数是 2x2=4,在as中也能看到对应的task,但是,此处的 维度总数 和 之前的buildType又形成了一个新的笛卡儿积,之前有5个(uat,prd,online,还有自带的 debug和release ),所以能看到的task一共是 4x5=20

    改变apk输出路径和文件名

    目前查到有三种写法:

    • 重写applicationVariants.all{} 参数闭包,支持绝对路径的写法

      ...
      
      android {
          ...
      
          //level3
          applicationVariants.all { variant ->
              //这个修改输出的APK路径
              if (variant.buildType.name != "debug") {//防止AS无法安装debug包(apk)
                  variant.getPackageApplication().outputDirectory = new File(myPackageDir())
              }
              variant.getPackageApplication().outputScope.apkDatas.forEach { apkData ->
                  //这个修改输出APK的文件名
                  apkData.outputFileName = "${appNamePrefix()}" +
                          variant.versionName + "_" +
                          "${releaseTime()}" + "_" +
                          variant.flavorName + "_" +
                          variant.buildType.name + "_" +
                          ".apk"
              }
          }
      }
      
      def myPackageDir(){
          return "C:\\Users\\adminstrator\\Desktop\\myPackage"
      }
      
      def appNamePrefix() {
          return "HankZhou"
      }
      
      String releaseTime() {
          Date date = new Date()
          String dates = date.format("yyyyMMdd_hhmmss", TimeZone.getTimeZone("UTC"))
          return dates
      }
      
      dependencies {
          ...
      }
      
      

      执行 gradlew assembleCnBlueOnline 之后在 电脑桌面上的 myPackage 文件夹中会存在一个apk:

    • 重写applicationVariants.all{} 参数闭包,支持相对路径的写法

      ...
      
      android {
          ...
      
          // 或者,使用这种方式,只支持相对路径
          applicationVariants.all { variant ->
              String time = new Date().format('yyyyMMdd_HHmmss')
              String pkgName = "enjoy_${time}_v${defaultConfig.versionName}_${buildType.name}_${flavorName}.apk"
              outputs.first().outputFileName = "../../../${pkgName}" // 它不能使用绝对路径,也就是说可以使用项目的相对路径
          }
      }
      
      ...
      
      dependencies {
          ...
      }
      
      

      执行 gradlew assembleCnBLueUat 之后,app module的build目录内会出现 apk:

    • 通过干涉task的执行过程

      ...
      
      android {
          ...
      }
      
      /**
       * 当有序任务图中加进去一个任务的时候,对它进行处理,doLast重新定义完成之后的行为
       */
      tasks.whenTaskAdded { task ->
          if (task.name.equalsIgnoreCase("assembleCnBlueUat")) {
              println("=============发现任务 ${task.name}")
      
              // 如果是assembleRelease任务,在最后执行导出apk以及mapping目录到指定目录
              task.doFirst {
                  println("=============doFirst")
              }
              task.doLast {
                  outputReleaseFile()
              }
          }
      }
      
      void outputReleaseFile() {
          println("=============outputReleaseFile")
          android.applicationVariants.all { variant ->
              // 如果是正式版打包
              if (variant.name.contains("cnBlueUat")) {
                  println("=============准备拷贝${variant.name}")
                  File outputPath = new File(myPackageDir())
                  println("==========把文件${variant.outputs[0].outputFile} \n 拷贝到 目标位置:$outputPath")
                  outputPath.mkdir()
                  // 但是很明显,这里的copy并没有成功
                  copy {// 拷贝apk文件
                      println("========开始拷贝...")
                      from variant.outputs[0].outputFile
                      into outputPath
                      // 重命名导出名称
                      rename {
                          appNamePrefix() + variant.name + '_' + android.defaultConfig.versionName + '-' + releaseTime() + ".apk"
                      }
                  }
              }
          }
      }
      
      def myPackageDir(){
          return "C:\\Users\\adminstrator\\Desktop\\myPackage"
      }
      
      def appNamePrefix() {
          return "HankZhou"
      }
      
      String releaseTime() {
          Date date = new Date()
          String dates = date.format("yyyyMMdd_hhmmss", TimeZone.getTimeZone("UTC"))
          return dates
      }
      
      dependencies {
          ...
      }
      
      

      执行 gradlew assembleCnBlueUat之后,电脑桌面上会出现 mypackage目录,内部有一个apk

    方式对比

    • 重写applicationVariants.all{} 参数闭包的方式,修改输出的绝对路径,或者相对路径,分别对应两个api:
      • 相对 outputs.first().outputFileName
      • 绝对 variant.getPackageApplication().outputScope.apkDatas 和 apkData.outputFileName
    • 使用 干涉task任务的方式,和 上面的方式比起来,写法更加复杂,但是可控制的精度也更高,比如:它除了可以改变输出路径之外,还可以直接指定拦截哪些task,而不是像 重写applicationVariants.all闭包 一样,一律变更输出路径和文件名。

    先写这么多。后续还有继续补上

    作者:波澜步惊
    链接:https://www.jianshu.com/p/94933c63ecb2

    相关文章

      网友评论

        本文标题:Android进阶修炼;Gradle骚操作合集

        本文链接:https://www.haomeiwen.com/subject/ovirqhtx.html