- 12
resConfigs : 过滤 drawable文件夹的资源
一般情况下,我们打完包,res 下面的资源如图所示:
现在加上资源过滤规则:
android { defaultConfig { applicationId "com.wifi.analytics" minSdkVersion 9 targetSdkVersion 25 versionCode 1 versionName "1.0" resConfigs "hdpi" //打包的时候只保留 drawable-xhdpi 文件夹里面的资源 } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
这次我们打包效果如下:
buildTypes 详解
buildTypes{}对应的是 BuildType 类
继承关系
BuildType 继承 DefaultBuildType ; DefaultBuildType 继承 BaseConfigImpl ;
BaseConfigImpl --- DefaultBuildType --- BuildType
-
1
-
2
-
3
buildTypes的属性:
name:build type的名字 applicationIdSuffix:应用id后缀 versionNameSuffix:版本名称后缀 debuggable:是否生成一个debug的apk minifyEnabled:是否混淆 proguardFiles:混淆文件 signingConfig:签名配置 manifestPlaceholders:清单占位符 shrinkResources:是否去除未利用的资源,默认false,表示不去除。 zipAlignEnable:是否使用zipalign工具压缩。 multiDexEnabled:是否拆成多个Dex multiDexKeepFile:指定文本文件编译进主Dex文件中 multiDexKeepProguard:指定混淆文件编译进主Dex文件中
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
buildType的方法:
1.buildConfigField(type,name,value):添加一个变量生成BuildConfig类。 2.consumeProguardFile(proguardFile):添加一个混淆文件进arr包。 3.consumeProguardFile(proguardFiles):添加混淆文件进arr包。 4.externalNativeBuild(action):配置本地的build选项。 5.initWith:复制这个build类型的所有属性。 6.proguardFile(proguardFile):添加一个新的混淆配置文件。 7.proguradFiles(files):添加新的混淆文件 8.resValue(type,name,value):添加一个新的生成资源 9.setProguardFiles(proguardFileIterable):设置一个混淆配置文件。
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
initWith :复制属性
android { compileSdkVersion 25 buildToolsVersion "25.0.2" defaultConfig { applicationId "com.wifi.analytics" minSdkVersion 9 targetSdkVersion 25 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } myType { initWith debug //完全复制 debug 的所有属性‘ minifyEnabled true //自定义打开混淆 } } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
applicationIdSuffix 、versionNameSuffix :添加后缀
android { compileSdkVersion 25 buildToolsVersion "25.0.2" defaultConfig { applicationId "com.wifi.analytics" minSdkVersion 9 targetSdkVersion 25 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' applicationIdSuffix "zhao" //applicationId 追加后缀名 zhao versionNameSuffix "debug" //versionName 追加后缀名 debug1.0 } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
效果图,如下:
buildConfigField: 自定义属性
在 build.gradle 文件中定义 buildConfigField 属性
android { buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' buildConfigField "String", "API_ENV", "\"http://yiba.com\"" //自定义String属性 } debug { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' buildConfigField "String", "API_ENV", "\"http://yiba.com\"" //自定义String属性 } } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
然后点击同步按钮,然后就可以在 build 目录看到 debug 和 release 信息。
debug 环境下的 BuildConfig 如下:
release 环境下的 BuildConfig 如下:
当然我们也可以在代码中获取自定义的值:
//获取变量值 String API = BuildConfig.API_ENV ;
-
1
-
2
-
3
上面演示了自定义 String 变量,也可以 自定义 int 、boolean
android { buildTypes { debug { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' buildConfigField "String", "API_ENV", "\"http://www.baidu.com\"" //自定义 String 值 buildConfigField "Boolean", "openLog", "true" //自定义 boolean 值 buildConfigField "int", "age", "10" //自定义 int 值 } } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
Gradle 实现差异化构建
情景1
LeakCanary 是 square 公司出品的一个检测内存泄漏的开源库。
GitHub : https://github.com/square/leakcanary
我们一般这样集成
dependencies { compile 'com.squareup.leakcanary:leakcanary-android:1.5.2' }
-
1
-
2
-
3
然后我们在 Application 类中初始化:
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); LeakCanary.install(this); } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
但是这样集成有一个弊端,就是 debug 和 release 包都会把 LeakCanary 的源码打进去,如果我们在 release 包中不把 LeakCanary 源码打进去,怎么办? 还好 LeakCanary 给我们提供了一个方法,方法如下:
dependencies { //打 debug 包 debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1' //打 release 包 releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1' }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
leakcanary-android-no-op 是一个空壳,里面有2个空类,所以就可以避免把 LeakCanary 源码打进 release 包。但是这种方式有个缺陷,如果一些开源库没有提供 releaseCompile 库,那我们改怎么办了?下面的情景2 就会讲到解决方案。
情景2
Stetho 是 Faceboo k开源的Andorid调试工具。当你的应用集成Stetho时,开发者可以访问Chrome,在Chrome Developer Tools中查看应用布局,网络请求,sqlite,preference 等等。
官网:http://facebook.github.io/stetho/
从官网可以看到 stetho 没有提供 releaseCompile 包 , 情景1 的方案就不能用了。新的思路集成方案如下:
dependencies { debugCompile 'com.facebook.stetho:stetho:1.5.0' }
-
1
-
2
-
3
在 src 目录下创建 debug 目录、release 目录 ,然后分别在 debug 目录 和 release 目录 创建 java 目录 , 在 java 目录中创建包名,比如: com.app
, 如下图所示:
debug 目录下创建 SDKManage 类 ,如下 :
public class SDKManager { public static void init(Context context) { //初始化 Stetho Stetho.initializeWithDefaults(context); } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
release 目录下创建 SDKManage 类 ,如下 :
public class SDKManager { public static void init(Context context) { //这是一个空方法,目的是不引入 Stetho 源码 } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
在住项目中的 MyApplication 类,并且完成 Stetho 的初始化,如下:
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); SDKManager.init(this); } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
这样我们便完成了简单的差异化构建, 打出来的 release 包就没有 Stetho 源码。
SourceSet
SourceSet 简介
SourceSet 可以定义项目结构,也可以修改项目结构。Java插件默认实现了两个SourceSet,main 和 test。每个 SourceSet 都提供了一系列的属性,通过这些属性,可以定义该 SourceSet 所包含的源文件。比如,java.srcDirs,resources.srcDirs 。Java 插件中定义的其他任务,就根据 main 和 test 的这两个 SourceSet 的定义来寻找产品代码和测试代码等。
SourceSet 定义源码目录
在 Android 项目中,我们可以在 src/main/java
目录新建 Java 文件,如下图所示:
现在我们在 src 目录下,新建 test1 目录 ,发现不能在 test1 目录中新建 Java 文件,如下图所示:
为什么在 test1 目录不能新建 Java 文件,因为 Gradle 中 SourceSet 默认定义的源码文件路径是src/main/java
, 其他的文件下下面的源码我们自然无法访问。解决这个问题也很简单,我们需要在 SourceSet 中增加一个源码路径即可。如下所示:
android { sourceSets { main { java { srcDir 'src/test1' //指定源码目录 } } } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
然后同步一下,就可以在 test1 目录中新建 Java 文件了。如下图所示:
当然我们也可以同时指定多个源码目录,比如同时指定 test1 , test2 , test3 为源码目录。
android { sourceSets { main { java { srcDir 'src/test1' //指定 test1 为源码目录 srcDir 'src/test2' //指定 test2 为源码目录 srcDir 'src/test3' //指定 test3 为源码目录 } } } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
或者 这样写 :
android { sourceSets { main { java.srcDirs( 'src/test1' , 'src/test2' ,'src/test3' ) } } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
效果如下图所示:
SourceSet 定义资源目录
定义 test1 目录 Java 源代码路径、res 资源目录。目录结构如下图所示:
android { sourceSets { main { java.srcDirs('src/test1/java') //定义java 源代码 res.srcDirs('src/test1/res') //定义资源目录(layout , drawable,values) } } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
SourceSet 实现 layout 分包
对于一个大项目来说,页面太多,布局文件就很多,有时在众多布局文件中找某个模块的布局文件,很是痛苦,为了解决这个问题,我们可以在创建多个 layout 目录,不同模块的布局文件放在不同的 layout 目录中,这样查找起来,就容易很多。
例子:
比如我们的项目中,有两个模块分别为:登录、注册。
-
第一步:把项目中 layout 文件夹改名字为 layouts
-
第二步:在 layouts 目录下,分别创建 login 、register 目录 。
-
第三步:分别在 login 、register 目录下创建 layout 目录。注意这一步是必须的,否则会报错。
-
第四步:把 登录布局文件、注册布局文件 分别放在 第三步创建的对应的 layout 目录下。
效果图如下:
SourceSet 实现如下:
android { sourceSets { main { res.srcDirs 'src/main/res/layouts/login' //定义登录布局目录 res.srcDirs 'src/main/res/layouts/register' //定义注册布局目录 } } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
SourceSet 定义 AndroidManifest 文件
指定 test1 目录下的 AndroidManifest 文件。项目结构如下图所示:
代码如下:
android { sourceSets { main { manifest.srcFile 'src/test1/AndroidManifest.xml' } } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
在组件化开发中, 我们需要针对 debug 与 release 模式下, 指定不同的 Manifest 文件, 代码如下:
android { def appDebug = false; buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' appDebug = false; } debug { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' appDebug = false; } } sourceSets { main { if (appDebug) { manifest.srcFile 'src/test1/AndroidManifest.xml' } else { manifest.srcFile 'src/main/AndroidManifest.xml' } } } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
SourceSet 定义 assets 目录
Android Studio 项目目录中,assets 默认目录如下:
如何重新定义 assets 目录 。在项目的根目录下创建 assets 目录,如下图所示:
sourceSets 定义代码如下:
android { sourceSets { main { assets.srcDirs = ['assets'] } } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
SourceSet 定义其他资源
android { sourceSets { main { jniLibs.srcDirs //定义 jni 目录 aidl.srcDirs //定义 aidl 目录 } } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
applicationVariants
定义 versionName 、VersionCode
在打包的时候分 debug 、release 版本 , 需要控制 versionName
android { applicationVariants.all { variant -> def flavor = variant.mergedFlavor def versionName = flavor.versionName if (variant.buildType.isDebuggable()) { versionName += "_debug" //debug 名字 } else { versionName += "_release" //release 名字 } flavor.versionName = versionName } }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
定义 APK 包的名字
apply plugin: 'com.android.application' android { defaultConfig { applicationId "android.plugin" minSdkVersion 14 targetSdkVersion 25 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } //定义渠道 productFlavors { xiaomi { //小米 } wandoujia { // 豌豆荚 } } //打包命名 applicationVariants.all { variant -> variant.outputs.each { output -> //定义一个新的apk 名字。 // 新名字 = app 名字+ 渠道号 + 构建类型 + 版本号 + 当前构建时间 def apkName = "appName_${variant.flavorName}_${buildType.name}_v${variant.versionName}_${getTime()}.apk"; output.outputFile = new File(output.outputFile.parent, apkName); } } } //获取当前时间 def getTime() { String today = new Date().format('YY年MM月dd日HH时mm分') return today }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
效果图如下:
Task
定义 task
//定义任务1 task task1<<{ println 'task1' } //定义任务2 task task2<<{ println 'task2' }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
mustRunAfter 定义 task 执行顺序
//task2 的执行顺讯在 task1 之后 task2.mustRunAfter task1
-
1
-
2
-
测试1 : gradlew task1
效果如下:
:app:task1 task1
-
1
-
2
-
3
-
测试2 : gradlew task2
效果如下:
:app:task2 task2
-
1
-
2
-
3
-
测试3 : gradlew task1 task2
效果如下:
:app:task1 task1 :app:task2 task2
-
1
-
2
-
3
-
4
-
5
-
测试4 : gradlew task2 task1
效果如下:
:app:task1 task1 :app:task2 task2
-
1
-
2
-
3
-
4
-
5
结论
如果单独执行 task1 就只会执行 task1 的任务;单独执行 task2 就只会执行 task2 的任务;
如果同时执行 task1、task2 , 一定会先执行 task1 , 等 task1 执行完后,就会执行 task2 内容。
扩展
上面 mustRunAfter 我们还有一种写法,如下图所示:
task2 {}.mustRunAfter task1
- 1
这个写法的效果和 mustRunAfter 是一样的,当然我们还可以在 花括号里面写一些任务,比如 :
task2 { println '我最先执行' }.mustRunAfter task1
-
1
-
2
-
3
下面做个测试,测试命令如下:
gradlew task2 task1
-
1
-
2
效果如下:
我最先执行 :app:task1 task1 :app:task2 task2
-
1
-
2
-
3
-
4
-
5
-
6
-
7
dependsOn 定义 task 依赖
task2 任务依赖于 task1 ,执行 task2 就会先执行 task1
task2.dependsOn task1
- 1
测试:
gradlew task2
-
1
-
2
效果如下:
:app:task1 task1 :app:task2 task2
-
1
-
2
-
3
-
4
-
5
常用 Gradlew 命令
- 1、gradlew -v : 查看版本号
------------------------------------------------------------ Gradle 3.3 ------------------------------------------------------------ Build time: 2017-01-03 15:31:04 UTC Revision: 075893a3d0798c0c1f322899b41ceca82e4e134b Groovy: 2.4.7 Ant: Apache Ant(TM) version 1.9.6 compiled on June 29 2015 JVM: 1.8.0_112 (Oracle Corporation 25.112-b15) OS: Windows 10 10.0 amd64
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
2、gradlew task : 查看所有的 task
Gradle 日志管理
Log 等级
除了常用的 debug , info , warning , error ,gradle 自己特有的 quiet 和l ifecycle 。
| 级别 | 用途 |
| — | — |
| ERROR | 错误信息 |
| QUIET | 重要信息 |
| WARNING | 警告信息 |
| LIFECYCLE | 警告信息 |
| INFO | 警告信息 |
| DEBUG | 调试信息 |
- 小例子
build.gradle
task hello << { logger.quiet("重要信息") logger.info("信息") logger.debug("调试信息") logger.lifecycle("过程信息") //自定义信息等级 logger.log(LogLevel.ERROR, "错误信息") }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
改变默认输出
println 后跟信息,gradle 会将其重定向到日志系统中,默认为 quiet 等级。
最后
写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
task2.dependsOn task1`
- 1
测试:
gradlew task2
-
1
-
2
效果如下:
:app:task1 task1 :app:task2 task2
-
1
-
2
-
3
-
4
-
5
常用 Gradlew 命令
- 1、gradlew -v : 查看版本号
------------------------------------------------------------ Gradle 3.3 ------------------------------------------------------------ Build time: 2017-01-03 15:31:04 UTC Revision: 075893a3d0798c0c1f322899b41ceca82e4e134b Groovy: 2.4.7 Ant: Apache Ant(TM) version 1.9.6 compiled on June 29 2015 JVM: 1.8.0_112 (Oracle Corporation 25.112-b15) OS: Windows 10 10.0 amd64
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
2、gradlew task : 查看所有的 task
Gradle 日志管理
Log 等级
除了常用的 debug , info , warning , error ,gradle 自己特有的 quiet 和l ifecycle 。
| 级别 | 用途 |
| — | — |
| ERROR | 错误信息 |
| QUIET | 重要信息 |
| WARNING | 警告信息 |
| LIFECYCLE | 警告信息 |
| INFO | 警告信息 |
| DEBUG | 调试信息 |
- 小例子
build.gradle
task hello << { logger.quiet("重要信息") logger.info("信息") logger.debug("调试信息") logger.lifecycle("过程信息") //自定义信息等级 logger.log(LogLevel.ERROR, "错误信息") }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
改变默认输出
println 后跟信息,gradle 会将其重定向到日志系统中,默认为 quiet 等级。
最后
写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。
[外链图片转存中…(img-RffwdmBg-1714279246469)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!