Gradle系列第(三)篇---Android Studio与Gradle那些事儿

来源:互联网 发布:我的世界方块数据值 编辑:程序博客网 时间:2024/06/05 19:21


Android中的gradle.jpg


本来这篇要写Android性能优化的,个人时间比较少,每天加班到很晚,写博客的时间就很少了,但是Gradle系列的文章还没有写完,所以补一篇,在Gradle系列第(二)篇---Gradle编程主要对象主要写了Gradle中的几个对象(Project,Settings,Gradle,Task、Action),现在聊一聊Android Studio中的gradle常见的功能需求。如果你還沒有阅读过我的前两篇博客Gradle系列第(一)篇---Groovy语法初探和Gradle系列2---Gradle编程主要对象,可以先看一下,有助于本文的理解,好啦,各位看官准备好瓜子花生,接下来一大篇文章哗啦啦的来了。不过不用担心,这篇博客仍然是面向基础。

读完这篇博客,你会了解到这些内容

  • 1、Android的构建文件
  • 2、全局参数配置
  • 3、用脚本更改项目结构
  • 4、多种apk的生成
  • 5、签名的配置与使用
  • 6、项目混淆(Proguard)
  • 7、gradle多渠道打包
  • 8、APK需求定制的案例
  • 9、动态参数配置
  • 10、gradle依赖管理
  • 11、gradle.properties文件配置
  • 12、jar文件输出
一、AS项目构建文件的简单解释

一个AS项目结构大概像下面这样子


项目构建文件.jpg

如蓝色条所示,项目中总共包含了6个构建文件(不算Library中的gradle),我们先从宏观的方面了解一下,每个构建文件的作用是啥?

  • 1、这个文件是app文件夹下这个Module的gradle配置文件,也可以算是整个项目最主要的gradle配置文件,比如自动打包debug,release,beta等环境,签名,多渠道打包,混淆等操作都可以在这里面写。每一个Module都需要有一个gradle配置文件。
  • 2、我们主要看下gradle-wrapper.properties这个文件的内容
    #Mon Dec 28 10:00:20 PST 2015distributionBase=GRADLE_USER_HOMEdistributionPath=wrapper/distszipStoreBase=GRADLE_USER_HOMEzipStorePath=wrapper/distsdistributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
    可以看到里面声明了gradle的目录与下载路径以及当前项目使用的gradle版本,这些默认的路径我们一般不会更改的,有時候导入一个新项目,gradle版本不对,可以在这里修改。
  • 3、这个文件是整个项目的gradle基础(全局)配置文件,内容主要包含了两个方面:一个是声明仓库的源,这里可以看到是指明的jcenter(), 之前版本则是mavenCentral(), jcenter可以理解成是一个新的中央远程仓库,兼容maven中心仓库,而且性能更优。另一个是声明了android gradle plugin的版本。allprojects:中定义的属性会被应用到所有 moudle 中,但是为了保证每个项目的独立性,我们一般不会在这里面操作太多共有的东西。
buildscript {    repositories {        jcenter()    }    dependencies {        classpath 'com.android.tools.build:gradle:2.2.0'        // NOTE: Do not place your application dependencies here; they belong        // in the individual module build.gradle files    }}allprojects {    repositories {        jcenter()    }}task clean(type: Delete) {    delete rootProject.buildDir}

所有通过gradle导入的jar包都是从http://bintray.com/bintray/jcenter这个中央仓库上扒下来的。如果你需要的jar包在这个网站上没有,那就无法通过gradle的方式来导入哦。

  • 4、这个里面可以配置参数,然后在其他build.gradle中引用,后面会讲例子,如何动态配置参数。
  • 5、这里主要指定了ndk和SDK的路径
    ndk.dir=G\:\\Users\\wangjing\\AppData\\Local\\Android\\sdk\\ndk-bundlesdk.dir=G\:\\Users\\wangjing\\AppData\\Local\\Android\\sdk
  • 6、setting.gradle最关键的内容就是告诉Gradle这个multiprojects包含哪些子projects,当你的app只有一个模块的时候,你的setting.gradle将会是这样子的:
    include ':app'
    当你的app有多个模块的时候,你的setting.gradle将会是这样子的
    include ':app', ':library',。。。。
    setting.gradle文件将会在gradle初始化时期执行,关于初始化时期,可以查看上一篇博客,并且定义了哪一个模块将会被构建。举个例子,上述setting.gradle包含了app模块,setting.gradle是针对多模块操作的,所以单独的模块工程完全可以删除掉该文件。在这之后,Gradle会为我们创建一个Setting对象,每一个settings.gradle都会转换成一个Settings对象,并为其包含必要的方法,你不必知道Settings类的详细细节,但是你最好能够知道这个概念。另外可以在settings做一些初始化的工作,后面介绍。

读到这里做个总结

  • build.gradle:控制每个Module的构建过程
  • gradle.properties:设置gradle脚本中的参数
  • local.properties:gradle的SDK和NDK环境变量配置
  • gradle.properties:用于配置参数信息
  • setting.gradle :配置gradle的多项目管理
二、实用技能精讲
1、全局参数配置

通常我们的项目都有很多的Module,像我现在公司的项目就有十几个,那么每个Module里面的gradle文件通常都有类似这样的配置。

android {    compileSdkVersion 24    buildToolsVersion "24.0.0"    defaultConfig {        applicationId "com.zhangwan.www.gradle"        minSdkVersion 15        targetSdkVersion 24        versionCode 1        versionName "1.0"        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"    }}

这些配置对于每个Module来说,最好统一,我把它定义在项目根目录的gradle文件中,如下。

//全局配置ext {    minSdkVersion =15    targetSdkVersion =24    compileSdkVersion =24    buildToolsVersion ="24.0.0"    versionCode =1    versionName="1.0"}

定义好了,我们可以在各个Module的gradle文件文件中引用,如下:

android {    compileSdkVersion rootProject.ext.compileSdkVersion    buildToolsVersion  rootProject.ext.buildToolsVersion    defaultConfig {        minSdkVersion rootProject.ext.minSdkVersion        targetSdkVersion rootProject.ext.targetSdkVersion        versionCode rootProject.ext.versionCode        versionName rootProject.ext.versionName        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"    }}

利用Gradle全局变量,对于多Module有很大的好处,方便统一,除了上面的列子,在举个例子。上面全部按照单个的属性配置的,对于相关的属性,可以将他们写到一个列表中,下面定义了一个dependencies_config的列表。

ext{    dependencies_config=[supportv7:"com.android.support:appcompat-v7:25.0.0"]}

在Module中,这样引用

dependencies {    compile rootProject.ext.dependencies_config.supportv7     .....}
2、项目结构更改

sourceSets 的作用是重新定义资源文件位置,比如

android {    compileSdkVersion 24    buildToolsVersion "24.0.0"    sourceSets{        main{            res.srcDirs=['src/main/res','src/main/res/layout/activity','src/main/res/layout/fragment']        }    }}

在你Sync Now之后,会出现activity和fragment两个文件夹


更改项目目录.png

最常见的是下面这块代码,当Eclipse项目转到Studio的时候,需要重新指定一些文件的位置。

sourceSets {      main {          manifest.srcFile 'AndroidManifest.xml'          java.srcDirs = ['src']          resources.srcDirs = ['src']          aidl.srcDirs = ['src']          renderscript.srcDirs = ['src']          res.srcDirs = ['res']          assets.srcDirs = ['assets']          jniLibs.srcDirs = ['libs']      }  }

sourceSets的用法就是这样,可以重新指定文件目录,但是读到这,可能有些人心中有个问题,为什么sourceSets ,defaultConfig这样的东东要写在android的大括号中。换言之,android这个大括号里面还能写什么东西,我来列举一下。

android {    defaultConfig {        //默认配置项,defaultConfig就是程序的默认配置,注意,如果在   AndroidMainfest.xml里面定义了与这里相同的属性,会以这里的为主。    }    buildTypes {      // 编译配置,release或debug版本的内容    }    compileOptions {      // Java 的版本配置    }    sourceSets {        //源码设置(项目目录结构的设置)    }    packagingOptions {       //打包时的相关配置      }    lintOptions {        //编译的 lint 开关,程序在buid的时候,会执行lint检查,有任何的错误或者警告提示,都会终止构建,我们可以将其关掉。        //abortOnError false      }    productFlavors {        //产品发布的一些东西,比如渠道、包名等        flavor1 {        }        flavor2 {        }    }    signingConfigs {        //签名的配置        release {        }    }    testOptions{        //测试配置,TestOptions类型    }     aaptOptions{      //aapt配置,AaptOptions类型     }      lintOptions{       //lint配置,LintOptions类型    }     dexOptions{       //dex配置,DexOptions类型    }     compileOptions{     // 编译配置,CompileOptions类型    }     packagingOptions{       // PackagingOptions类型    }     jacoco{        //JacocoExtension类型。 用于设定 jacoco版本    }     splits{       //Splits类型。    } }

在DSL文档中,以上每个类型都有它的详细配置选项,一般常见的设置就是上面啦,如果你觉得有的不太了解,看下面之后就了解了。

3、多种apk的生成

默认studio生成的buildTypes是像下面这样的,但是呢,我还想要其他的变种类型。

 buildTypes {        release {            minifyEnabled false// 不混淆            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }    }

我们可以这样添加

 buildTypes {        release {            minifyEnabled false            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }        r1{            applicationIdSuffix ".r1"        }        r2{            applicationIdSuffix ".r2"        }        r3{            applicationIdSuffix ".r3"        }    }

通过这样就可以得到多种变种app,执行assemble这个task,打出所有apk。


build_types.png

总共得到系统默认有的release和debug两个apk,额外还有r1,r2,r3三个不同的apk。
那么applicationIdSuffix是什么呢,逆向r1包看看。


r1包逆向结果.png


系统通过包名来区分应用,这种方式无非就是在包名后面加上了一个后缀r1。

4、签名的配置与使用

上面打出的包都是没有指定签名的,我们要配置一个签名,首先需要生成签名文件。我生成的签名文件是1.jks

 signingConfigs{        signR1{            storeFile file("build/1.jks");            storePassword "123456"            keyAlias "xxx"            keyPassword "123456"        }        signR2{            storeFile file("build/2.jks");            storePassword "123456"            keyAlias "xxx"            keyPassword "123456"        }    }

签名在signingConfigs中配置,signR1,signR2是签名的名字,在buildTypes中使用。

 buildTypes {        release {            minifyEnabled false            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }        r1{            signingConfig signingConfigs.signR1            applicationIdSuffix ".r1"        }        r2{            signingConfig signingConfigs.signR2            applicationIdSuffix ".r2"        }    }

加上签名后打的包是这样,跟未加签名相比较,多了app-r1.apk,app-r2.apk。


签名apk生成.png
5、项目混淆(Proguard)

面对众多的渠道,打包也有很多不同的需求。 比如 debug版,release版,dev版等等。 有时候不同的版本中使用到的不同的服务端api域名也不相同。 比如 debug_api.com,release_api.com,dev_api.com等等。不同的版本对应了不同的 api 域名,还可能对应不同的 icon 等。渠道首发包通常需要要求在欢迎页添加渠道的logo等。下面我们开始进行打包。首先进行混淆设置,混淆需要buildTypes中配置,在上面说过,默认生成的buildTypes是这样子的

  buildTypes {        release {            minifyEnabled false            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }    }

其中,proguard-android.txt是在你的sdk\tools\proguard目录下。minifyEnabled:表示是否开启混淆,默认为false;proguardFiles:混淆配置文件,一般就采用项目中默认的proguard-rules.pro文件。在这个文件中写我们的混淆规则,比如:

-keepclasseswithmembernames class * {                                           # 保持 native 方法不被混淆    native <methods>;}-keepclassmembers enum * {                                                      # 保持枚举 enum 类不被混淆    public static **[] values();    public static ** valueOf(java.lang.String);}-keep class * implements android.os.Parcelable {                                # 保持 Parcelable 不被混淆  public static final android.os.Parcelable$Creator *;}

有这些还不够,还需要在gradle中开启混淆

   buildTypes {        release {           // 不显示 Log             buildConfigField "boolean", "LOG_DEBUG", "false"            shrinkResources true            signingConfig signingConfigs.release            minifyEnabled true            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard.cfg'        }        debug {          // 显示 Log             buildConfigField "boolean", "LOG_DEBUG", "true"            signingConfig signingConfigs.debug        }    }

我们设置minifyEnabled true,就会在打包的时候进行代码混淆处理. 其中proguard-android.txt不用管,在sdk目录里面,我们主要是配置了proguard.cfg文件。可能大家直接在android studio创建项目不会有这个文件,而是proguard-rules.pro文件,其实一样的,我这里是因为项目是从eclipse迁移过来的,之前在eclipse上混淆是proguard.cfg文件.

6、gradle多渠道打包
  • 1、第一步 在AndroidManifest.xml里配置PlaceHolder
    <meta-data          android:name="MY_CHANNEL"          android:value="${MY_CHANNEL}" />
  • 2、第二步 在build.gradle设置productFlavors

     productFlavors{      xiaomi {          //用gradle修改AndroidManifest.xml中的meta-data元素值          manifestPlaceholders = [MY_CHANNEL: "xiaomi"]      }      _360 {          manifestPlaceholders = [MY_CHANNEL: "_360"]      }      baidu {          manifestPlaceholders = [MY_CHANNEL: "baidu"]      }      huawei{          manifestPlaceholders = [MY_CHANNEL: "huawei"]      }  }

    或者批量修改

    productFlavors{      xiaomi {}      _360 {}      baidu {}      huawei{}  }  productFlavors.all {      flavor -> flavor.manifestPlaceholders = [MY_CHANNEL: name]  }

    最后,最好在defaultConfig中定义一个默认的渠道

    defaultConfig{      manifestPlaceholders = [ MY_CHANNEL:"xiaomi" ]}

    到此配置完成,可以执行命令了。

  • 3、去工程的根目录,也就是有gradlew文件的目录,打开命令行,输入命令:
    ./gradlew assemble
    这时候你去app/build/outputs/apk中就能看到自动打好的渠道包了。
    ./gradlew assembleRelease
    只打Release包
    ./gradlew assembleDebug
    只打Debug包
    ./gradlew assemblebaidu
    只打360的渠道包
    ./gradlew assemblebaiduRelease

不想敲命令行的,调起下面这个面板打包


打包Task.png
7、APK需求定制

上面说了一下打包,在打包的時候,一些特殊化的操作,比如修改指定apk Logo,apk重命名,这些怎么搞?

  • 渠道包重命名
    android.applicationVariants.all { variant ->      variant.outputs.each { output ->          def outputFile = output.outputFile          if (outputFile != null && outputFile.name.endsWith('.apk')) {              File outputDirectory = new File(outputFile.parent);              def fileName              if (variant.buildType.name == "release") {                  fileName = "wangjing_${variant.productFlavors[0].name}.apk"              } else {                  fileName = "wangjing_${variant.productFlavors[0].name}_beta.apk"              }              output.outputFile = new File(outputDirectory, fileName)          }      }  }
  • 根据渠道修改APP名称

    buildTypes {      debug {          // 显示Log          buildConfigField "boolean", "LOG_DEBUG", "true"          //重命名          resValue("string","app_name","DEBUG")          versionNameSuffix "-debug"          minifyEnabled false          zipAlignEnabled false          shrinkResources false          signingConfig signingConfigs.debug      }      release {          // 不显示Log          buildConfigField "boolean", "LOG_DEBUG", "false"          //重命名          resValue("string","app_name","DEBUG")          //混淆          minifyEnabled true          //加载默认混淆配置文件          proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'          //签名          signingConfig signingConfigs.release      }  }

    其中 resValue("string","app_name","DEBUG") 表示一个string 类型的变量app_name的值是DEBUG,做了上面的配置之后,需要将string.xml的app_name删掉,因为gradle编译的时候,会将脚本中的配置跟string.xml的合并。

8、动态参数配置
signingConfigs{        release{            storeFile file("build/mykey.jks")            storePassword   "123456"            keyAlias "123456"            keyPassword   "123456"        }    }

上面的这段配置,有个缺点,就是值直接写死了,我们可以动态配置参数。在哪里配置呢,一开始就说了,在gradle.properties中配置参数。如下:

 systemPro.keyAliasPassword=123456systemPro.keyAlias=123456systemPro.keyStorePassword=123456systemPro.keyStore=mykey.jks

配置好了,就可以“到处”使用了

 signingConfigs{        release{            storeFile       System.properties["keyStore"]            storePassword   System.properties["keyStorePassword"]            keyAlias        System.properties["keyAlias"]            keyPassword     System.properties["keyAliasPassword"]        }        debug{            storeFile file("mykey.jks")            storePassword  "123456"            keyAlias"123456"            keyPassword  "123456"        }    }
9、gradle依赖管理

比如我们想依赖个support-v4包,直接一句话:

compile 'com.android.support:support-v4:23.1.1'

一个依赖需要定义三个元素:group,name和version。group意味着创建该library的组织名,通常这会是包名,name是该library的唯一标示。
上述的代码是基于groovy语法的,所以其完整的表述应该是这样的:

compile group: 'com.android.support:', name: 'support-v4', version:'23.1.1'

有些时候,你可能需要和sdk协调工作。为了能顺利编译你的代码,你需要添加SDK到你的编译环境。你不需要将sdk包含在你的APK中,因为它早已经存在于设备中,不需要在compile,我们总共有5个不同的配置:

  • compile是默认的那个,其含义是包含所有的依赖包,即在APK里,compile的依赖会存在。

  • apk的意思是apk中存在,但是不会加入编译中,这个貌似用的比较少。

  • provided的意思是提供编译支持,但是不会写入apk。

  • testCompileandroidTestCompile会添加额外的library支持针对测试。

通常项目的Module很多,依赖也非常多,为了方便管理,我们应该将这些依赖写到一个全局的地方,可以供其他module使用。这种思想也是第一小节所提的全局参数的配置。依赖管理可以参考:http://stormzhang.com/android/2016/03/13/gradle-config/

10、gradle.properties文件配置
  • gradle.properties常见配置比如有:
    开启并行编译:加快gradle 的编译
    org.gradle.parallel=true

  • 开启编译守护进程:该进程在第一次启动后回一直存在,当你进行二次编译的时候,可以重用该进程。
    org.gradle.daemon=true

  • 加大可用编译内存:
    org.gradle.jvmargs=-Xms256m -Xmx1024m

11、jar文件输出

android Studio常常有输出jar包的需求,只要下面这段代码即可:

task makeJar(type: Copy) {    delete 'build/libs/my.jar'    from('build/intermediates/bundles/release/')    into('build/libs/')    include('classes.jar')    rename ('classes.jar', 'my.jar')}

OK,Gradle研究了一个多星期,这篇博客耗时两个晚上,终于结束,另外如果时间来的急,在写一篇Gradle系列4或5,因为感觉自己还没有讲清楚,侧重多个Module中gradle的使用与Gradle常用命令的使用,下篇博客继续性能优化系列的更新,每一次写博客都花费很多的时间和精力也是一次锻炼,跟他人分享自己的学习成果,最后附上参考资料,比我写的好。

  • Gradle for Android: http://www.codeceo.com/article/gradle-for-android-1.html

  • 深入理解Gradle: http://blog.csdn.net/luohai859/article/details/48319129



作者:LooperJing
链接:http://www.jianshu.com/p/ac02cebd279e
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
阅读全文
0 0
原创粉丝点击