工具使用指南Android开发技巧

Gradle for Android 问题总结

2016-04-17  本文已影响5751人  Jinwong

Gradle是什么?

Gradle 是以Groovy为基础,面向java应用,基于DSL语法的自动化构建工具。是google引入,替换ant和maven的新工具,其依赖兼容maven和ivy。

使用gradle的目的:
更容易重用资源和代码;
可以更容易创建不同的版本的程序,多个类型的apk包;
更容易配置,扩展;
更好的IDE集成;

首先明确gradle跟maven一样,也有一个配置文件,maven里面是叫pom.xml,而在gradle中是叫build.gradle。Android Studio中的android项目通常至少包含两个build.gradle文件,一个是project范围的,另一个是module范围的,由于一个project可以有多个module,所以每个module下都会对应一个build.gradle。这么说有点抽象,看下面这个图:


1. Project中build.gradle

project下的build.gradle是基于整个project的配置,主要配置gradle 版本及 全局依赖仓库、库或者其他全部参数。
android studio 现在重要仓库采用jcenter(),之前版本放在mavenCentral。
另外有时还没有加入jcenter()仓库的第三方库,也需要在这里配置他们的库地址。
需要在这里配置,才能将第三方库拉下来

buildscript {
   //构建过程依赖的仓库
  repositories {
      jcenter()
  }
  
  //构建过程需要依赖的库
  dependencies {
      //声明的是gradle插件的版本
      classpath 'com.android.tools.build:gradle:2.0.0'

      // NOTE: Do not place your application dependencies here; they belong
      // in the individual module build.gradle files
  }
}

allprojects {
  //这里面配置整个项目依赖的仓库,这样每个module就不用配置仓库了
  repositories {
      jcenter()

      maven {
          // LeanCloud 的包仓库
          url "http://mvn.leancloud.cn/nexus/content/repositories/releases"
      }
  }
}

//配置全局变量
ext {
  // module依赖库公共版本号
  SupportXVersion = '23.2.0'
  GsonVersion = '2.6.2'
  LeanCloudVersion = 'v3.13.4'
  JunitVersion = '4.12'
  
  compileSdkVersion = 22
  buildToolsVersion = "23.0.1"
  minSdkVersion = 10
  targetSdkVersion = 22
  versionCode = 34
  versionName = "v2.6.1"
}

注:大家可能很奇怪,为什么仓库repositories需要声明两次,这其实是由于它们作用不同,buildscript中的仓库是gradle脚本自身需要的资源,而allprojects下的仓库是项目所有模块需要的资源

2. module中build.gradle

//申明使用插件,表明要编译的内容和产物,
//com.android.application 表明该module 为android 应用
//com.android.library 表明为library库
//java 表名是java库
apply plugin: 'com.android.application'

//安卓构建过程需要配置的参数
android {
    //编译SDK的版本
    compileSdkVersion COMPILE_SDK_VERSION as int
    //buildtool 的版本
    buildToolsVersion BUILD_TOOLS_VERSION
    
    /默认配置,会同时应用到debug和release版本上
    defaultConfig {
        //应用包名
        applicationId APPLICATION_ID
        //支持最小android sdk 版本
        minSdkVersion MIN_SDK_VERSION as int
        // 目标版本
        targetSdkVersion TARGET_SDK_VERSION as int
        //应用版本号
        versionCode VERSION_CODE as int
        //应用版本名称
        versionName VERSION_NAME
        
        /// 配置生成的 BuildConfig 文件中的常量,代码引用直接 
        buildConfigField "String", "LOG_TAG", LOG_TAG // 日志tag
        buildConfigField "String", "LOG_HTTP_TAG", LOG_TAG_HTTP // http日志tag
        buildConfigField "String", "LOG_WEB_TAG", LOG_TAG_WEB // web日志tag

      // 默认是UMENG_CHANNEL_VALUE为umeng
        manifestPlaceholders = [UMENG_CHANNEL_VALUE: "umeng"]
        
      // dex突破65535的限制
        multiDexEnabled true
    }
    
    //java版本号
    compileOptions{
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
    
    //签名
    signingConfigs {
        release {
            //签名文件
            storeFile file(STORE_FILE)
            storePassword STORE_PASSWORD
            keyAlias KEY_ALIAS
            keyPassword KEY_PASSWORD
        }
    }
    // 为了解决部分第三方库重复打包了META-INF的问题
    packagingOptions {
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/NOTICE.txt'
    }
    
    //移除lint检测的error
    lintOptions {
        abortOnError false
    }
    
    //编译类型
    //其中debug, release是gradle默认自带的两个build type, 当然你可以定义其他类型。
    //可以针对不停编译的版本中配置不同的参数,比如混淆、签名等。preview
    buildTypes {
        debug {
            minifyEnabled false
            zipAlignEnabled false
            shrinkResources false
        }
        
         preview {
            debuggable false // 是否保留调试信息
            minifyEnabled true  //是否混淆
            zipAlignEnabled true // 包优化
            shrinkResources true // 移除不必要的资源

            // 签名
            signingConfig signingConfigs.release
            // 代码混淆规则文件
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        
        release {
            //加上后缀
            applicationIdSuffix ".release"
            minifyEnabled true //是否混淆
            zipAlignEnabled true // zip对齐优化
            shrinkResources true // 移除不必要的资源
            
            // 不显示Log
            buildConfigField "boolean", "LOG_DEBUG", "false"

            // 签名
            signingConfig signingConfigs.release
            //混淆文件的位置
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    // 多渠道
    productFlavors {
        //可以设置不同渠道渠道号,应用名称
        dev { // 开发
            buildConfigField "String", "CHANNEL_NUMBER", '"11111"'
        }
        '360' {
            buildConfigField "String", "CHANNEL_NUMBER", '"11112"'
        }
        GooglePlay {
            buildConfigField "String", "CHANNEL_NUMBER", "11113"'

    }
    // 多渠道批量替换
    productFlavors.all { flavor ->
         //批量修改Manifest占位符替换
        //在Manifest使用`${UMENG_CHANNEL_VALUE}`,`LEANCLOUD_CHANNEL_VALUE`,打包时将替换成渠道名,例如UMENG_CHANNEL_VALUE="dev";
        flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name, LEANCLOUD_CHANNEL_VALUE: name]
        // Project Properties->_myAPPBuildVersionName,用于程序集成下命令行修改
        if (project.hasProperty('_myAPPBuildVersionName')) {
            defaultConfig.versionName = _myAPPBuildVersionName
        }
    }
     
     //定义变量
     //gradle 可以用def定义一些值例如:def KeyPassword = "123123" 
     def releaseTime() {
         return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
     }

    
    // 批量打包
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def outputFile = output.outputFile
            if (outputFile != null && outputFile.name.endsWith('.apk')) {
                def fileName
                if (variant.buildType.name.equals('release')) {
                    // myAPP_v版本号_渠道名.apk
                    fileName = "myAPP_v${variant.versionName}_${variant.productFlavors[0].name}.apk"
                } else {
                    // myAPP_v版本号_渠道名_时间_编译类型名.apk
                    fileName = "myAPP_v${variant.versionName}_${variant.productFlavors[0].name}_${releaseTime()}_${variant.buildType.name}.apk"
                }
                output.outputFile = new File(outputFile.parent + "/${variant.buildType.name}", fileName)
            }
        }
    }


}

//依赖第三方库
dependencies {
   //编译libs目录下所以jar包
   //compile files('libs/xxx.jar')   导入一个特定jar包
    compile fileTree(dir: 'libs', include: ['*.jar'])//导入所有的jar包
    compile project(':core')
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile 'com.android.support:design:23.1.1'
    compile 'com.android.support:recyclerview-v7:23.1.1'
    compile 'com.android.support:cardview-v7:23.1.1'
}

注意:

3. Project中setting.gradle

这个文件是全局的项目配置文件,里面主要声明Project中所包括的所有module,

//一个Project中所包括的所有module
include ':app', ':model',':lib', ':core'

4. Project中gradle.properties

gradle.properties为gradle的配置文件,里面可以定义一些常量供build.gradle使用,比如可以配置签名相关信息如keystore位置,密码,keyalias等,build.gradle就可以直接引用
gradle 中的一些配置参数建议写到gradle.properties

//编译版本信息
APPLICATION_ID = com.jin.myAPP
COMPILE_SDK_VERSION = 23
BUILD_TOOLS_VERSION = 23.0.1
MIN_SDK_VERSION = 15
TARGET_SDK_VERSION = 1
VERSION_CODE = 1
VERSION_NAME = 1.0.0.0

//keystore信息
STORE_FILE = ../app/mykey.keystore
STORE_PASSWORD = your password
KEY_ALIAS = your alias
KEY_PASSWORD = your password

5. ext配置全局参数

project的build.gradle中的ext可以为各位module进行全局配置参数,防止各个module之间的不统一,不可控。而且当我们升级sdk、build tool、target sdk等,几个module都要更改,非常的麻烦。

ext {
   compileSdkVersion = 22
   buildToolsVersion = "23.0.1"
   minSdkVersion = 10
   targetSdkVersion = 22
   versionCode = 34
   versionName = "v2.6.1"
}

然后在各自module的build.gradle中引用:

android {

  compileSdkVersion rootProject.ext.compileSdkVersion

  buildToolsVersion rootProject.ext.buildToolsVersion

 defaultConfig {

    applicationId "com.xxx.xxx"

    minSdkVersion rootProject.ext.minSdkVersion

    targetSdkVersion rootProject.ext.targetSdkVersion

    versionCode rootProject.ext.versionCode

    versionName rootProject.ext.versionName
  }
}

6. resValue 定义资源。

例如resValue "string" 就是字符串资源,可以用R.String 来引用对应的字符串资源

android {
   defaultConfig {
       resValue "string", "build_time", buildTime()
       resValue "string", "build_host", hostName()
       resValue "string", "build_revision", revision()
    }
}

def buildTime() {
   return new Date().format("yyyy-MM-dd HH:mm:ss")
}

def hostName() {
   return System.getProperty("user.name") + "@" +InetAddress.localHost.hostName
 }

def revision() {
  def code = new ByteArrayOutputStream()
     exec {
        commandLine 'git', 'rev-parse', '--short', 'HEAD'
        standardOutput = code
      }
  return code.toString()
  }

上述代码实现了动态的添加了3个字符串资源: build_time、build_host、build_revision, 然后在其他地方可像如引用字符串一样使用如下:

// 在Activity里调用
getString(R.string.build_time) // 输出2015-11-07 17:01
getString(R.string.build_host) // 输出jay@deepin,这是我的电脑的用户名和PC名
getString(R.string.build_revision) // 输出3dd5823, 这是最后一次commit的sha值

7. PlaceHolder

manifest的一些值我们可以用PlaceHolder处理。例如

<meta-data
    android:name="UMENG_CHANNEL"
    android:value="${UMENG_CHANNEL_VALUE}" />

在gradle中改成

 manifestPlaceholders = [UMENG_CHANNEL_VALUE: "360"]

可以放在defaultConfig中设置默认值,或者放在productFlavors中根据不同渠道修改成不同值。或者批量修改

  productFlavors.all { 
        flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] 
    }

8. 导入某个project

如果你的app是多模块的,假设有两个模块app和lib,并且app模块是依赖lib的,这时候我们就需要在app模块的build.gradle中的dependencies结点下配置依赖:

compile project(':lib')

并且你需要在settings.gradle中把lib模块包含进来:

include ':lib',':app'

此外,这种情况下lib模块是作为库存在的,因而它的build.gradle中的插件声明通常应该是这样的:

apply plugin: 'com.android.library'

而且,作为library的模块lib的build.gradle文件的defaultConfig中是不允许声明applicationId的,这点需要注意。

9. 引用本地aar:

repositories{
     flatDir {
        dirs 'libs'
    }
}
dependencies{
    compile (name:'xxx',ext:'aar')
}

10. BuildConfig

在build.gradle中配置buildConfigField参数,编译后会在..\app\build\generated\source\buildConfig文件夹下会自动生成对应版本对应module的BuildConfig.java。BuildConfig就会包含对应版本的配置信息。程序中可以直接引用这些数据。例如BuildConfig.DEBUG。

public final class BuildConfig {
 public static final boolean DEBUG = Boolean.parseBoolean("true");
 public static final String BUILD_TYPE = "debug";
 public static final String FLAVOR = "360";
 public static final int VERSION_CODE = 45;
 public static final String VERSION_NAME = "3.2.0.0";
 // Fields from build type: debug
 public static final String HOST_IMG_SERVER = "img";
 public static final String HOST_SERVER = "test";
 // Fields from product flavor: 360
 public static final String CHANNEL_NUMBER = "232100";
}

11. module 调整目录结构sourceSets

默认情况下,java文件和resource文件分别在src/main/java和src/main/res目录下,在build.gradle文件,andorid{}里面添加下面的代码,便可以将java文件和resource文件放到src/java和src/resources目录下。

sourceSets {
   main {
      java {
          srcDir 'src/java'
      }
      resources {
        srcDir 'src/resources'
     }
   }
}

更简便的写法是:

sourceSets {
   min.java.srcDirs = ['src/java']
   min.resources.srcDirs = ['src/resources']
}

12. Gradle常用命令

上面大家接触了一些命令如 ./gradlew -v ./gradlew clean ./gradlew build, 这里注意是./gradlew, ./代表当前目录,gradlew代表 gradle wrapper,意思是gradle的一层包装,大家可以理解为在这个项目本地就封装了gradle,即gradle wrapper, myAPP/gradle/wrapper/gralde-wrapper.properties**文件中声明了它指向的目录和版本。只要下载成功即可用grdlew wrapper的命令代替全局的gradle命令。

理解了gradle wrapper的概念,下面一些常用命令也就容易理解了。

上一篇下一篇

猜你喜欢

热点阅读