90%不知道的Android Build Variant的使用

applicationId ‘com.example.android.paid’

resValue “string”,‘tag’,‘paid’

dimension ‘isFree’

}

domestic{

dimension ‘area’

}

overseas {

dimension ‘area’

}

}

通过上面的定义,我们就拥有了四种组合,分别是

  • freeDomestic 免费国内版

  • freeOverseas 免费国际版

  • paidDomestic 付费国内版

  • paidOverseas 付费国际版

这里有个注意的地方是,flavor组合的顺序是根据flavorDimensions的元素排序决定的。假如我们将

isFreearea的顺序颠倒一下

flavorDimensions “area”,‘isFree’

那么原先的freeDomestic将变成domesticFree。这会造成什么影响呢?

实际上第一个flavor是具有高优先级的。 假如free和domestic都定义了各自的包名

productFlavors{

flavorDimensions “area”,‘isFree’

free {

applicationId ‘com.example.android.free’

dimension ‘isFree’

}

domestic{

dimension ‘area’

applicationId ‘com.example.android.domestic’

}

}

那么最终的包名将会是com.example.android.domestic

matchingFallbacks


在某些情况下,app模块包含了某些flavors而library模块却没有,在这种情况下,app无法和library的flavor相匹配,通过指定matchingFallbacks来兜底。例如下面这个例子,app依赖了library

//app build.gradle

productFlavors{

flavorDimensions ‘isFree’

free {

dimension ‘isFree’

matchingFallbacks = [‘demo’]

}

paid {

dimension ‘isFree’

}

}

//library build.gradle

productFlavors{

flavorDimensions ‘isFree’

demo {

dimension ‘isFree’

}

paid {

dimension ‘isFree’

}

}

当执行assembleFreeRelease时,由于library不存在freeflavor,那么会使用demo进行替代。

如果app不指定matchingFallbacks的话,是无法通过编译的,会报如下错误

Could not resolve all artifacts for configuration ‘:app:freeDebugCompileClasspath’.

Could not resolve project :library.

Required by:

project :app

所以,如果library和app都定义了ProductFlavor,那么需要对齐,否则需要指定matchingFallbacks进行兜底。注意,library和app需要定义在同个dimension下。

SourceSet

========================================================================

SourceSet即源代码集,我们可以使用 SourceSet 代码块更改 Gradle 为源代码集的每个组件收集文件的位置。这样我们就无需改变文件的位置。换句话说,有了SourceSet,我们可以按照自己的偏好指定代码和资源的路径

基本使用


属性

以下是AndroidSourceSets提供的属性

| Property | Description |

| — | — |

| aidl | Android AIDL目录 |

| assets | Assets目录 |

| java | Java目录 |

| jni | JNI目录 |

| jniLibs | JNI libs目录 |

| manifest | AndroidManifest路径 |

| name | source set的名称 |

| renderscript | RenderScript目录 |

| res | res资源目录 |

| resources | Java resources目录 |

以上的配置除了manifest对应的是AndroidSourceFile 对象,即为单一文件,其余的都是AndroidSourceDirectorySet对象,我们来看下AndroidSourceDirectorySet接口提供了哪些方法。

public interface AndroidSourceDirectorySet extends PatternFilterable {

@NonNull

String getName();

//添加资源路径到集合中,最终AGP会从集合里取出所有的文件

@NonNull

AndroidSourceDirectorySet srcDir(Object srcDir);

//添加多个资源路径到集合中

@NonNull

AndroidSourceDirectorySet srcDirs(Object… srcDirs);

//指定资源的路径,与上面两个方法不同的时候,该方法会覆盖原有的集合

@NonNull

AndroidSourceDirectorySet setSrcDirs(Iterable<?> srcDirs);

//以FileTree形式返回资源

@NonNull

FileTree getSourceFiles();

//返回过滤规则

@NonNull

PatternFilterable getFilter();

//将源文件夹作为一个列表返回

@NonNull

List getSourceDirectoryTrees();

//返回资源文件列表

@NonNull

Set getSrcDirs();

/** Returns the [FileCollection] that represents this source sets. */

@Incubating

FileCollection getBuildableArtifact();

}

因此我们可以修改源集的位置,我们来看一个简单配置

def basePath = projectDir.parentFile.absolutePath

def resPath = new File(basePath, “res”)

def manifestPath = new File(basePath, “AndroidManifest.xml”)

sourceSets {

main {

res.srcDir(resPath)

manifest.srcFile(manifestPath)

}

}

我们可以通过sourceSets任务来打印具体的配置

:app:sourceSets

//输出

main


Compile configuration: compile

build.gradle name: android.sourceSets.main

Java sources: [app/src/main/java]

//AndroidMnaifest路径被改到app根目录下

Manifest file: AndroidManifest.xml

//可以看刚才添加的res目录

Android resources: [app/src/main/res, res]

Assets: [app/src/main/assets]

AIDL sources: [app/src/main/aidl]

RenderScript sources: [app/src/main/rs]

JNI sources: [app/src/main/jni]

JNI libraries: [app/src/main/jniLibs]

Java-style resources: [app/src/main/resources]

paid


Compile configuration: paidCompile

build.gradle name: android.sourceSets.paid

Java sources: [app/src/paid/java]

Manifest file: app/src/paid/AndroidManifest.xml

Android resources: [app/src/paid/res]

Assets: [app/src/paid/assets]

AIDL sources: [app/src/paid/aidl]

RenderScript sources: [app/src/paid/rs]

JNI sources: [app/src/paid/jni]

JNI libraries: [app/src/paid/jniLibs]

Java-style resources: [app/src/paid/resources]

//省略其他源集

方法

| 方法 | 描述 |

| — | — |

| setRoot(path) | 将源集的根设置为给定的路径。源集合的所有条目都位于此根目录下。 |

通过setRoot方法,我们可以直接指定某个源集的目录,例如如果你有多个ProductFlavor,并且创建了对应的源集目录,那么我们可以把非main的目录都放到一起,避免src目录太多文件。

sourceSets.all { set ->

if (set.name.toLowerCase().contains(flavor)

&& !set.name.equals(“main”)) {

set.setRoot(“src/other/$flavor”)

}

}

源集类型


main 源集包含了所有其他构件变体共用的代码和资源,即所有的其他构建变体,src/main是其共同拥有的。

其他源集目录为可选项,如果我们想要为某个单独的构建变体添加特有的代码或者资源,可以创建对应的目录。例如,构建“demoDebug”这个变体, Gradle 会查看以下目录,并为它们指定以下优先级

  1. src/demoDebug/(build 变体源代码集)

  2. src/debug/(build 类型源代码集)

  3. src/demo/(产品变种源代码集)

  4. src/main/(主源代码集)

当存在重复的资源时,Gradle 将按以下优先顺序决定使用哪一个文件(左侧源集替换右侧源集的文件和设置):

构建变体 > 构建类型[BuildType] > 产品风味[ProductFlavor] > 主源集[main] > 库依赖项

  • java/ 目录中的所有源代码将一起编译以生成单个输出

注意的是,java文件是不能被覆盖的,如果我们在main目录中创建了src/main/Utility.java,那么是不能其他源集目录中定义同名文件进行覆盖的,因为,Gradle 在构建过程中会查看这两个目录并抛出“重复类”错误。如果我们想要在不同的 build 类型有不同版本的 Utility.java,只能让每个 build 类型定义各自的文件版本,这样是比较麻烦的。

  • 所有Manifest都将合并为一个清单。合并的优先级和上面提到的一致。

  • 同样,values/ 目录中的文件也会合并在一起。如果两个文件同名,例如存在两个 strings.xml 文件,按照上述的优先级覆盖。

  • res/ 和 asset/ 目录中的资源会打包在一起。

  • 最后,在构建 APK 时,Gradle 会为库模块依赖项随附的资源和清单指定最低优先级。

配置过滤规则


回顾上面的AndroidSourceDirectorySet接口,其继承了PatternFilterable接口

public interface PatternFilterable {

Set getIncludes();

Set getExcludes();

PatternFilterable setIncludes(Iterable includes);

PatternFilterable setExcludes(Iterable excludes);

PatternFilterable include(String… includes);

PatternFilterable include(Iterable includes);

PatternFilterable include(Spec includeSpec);

PatternFilterable include(Closure includeSpec);

PatternFilterable exclude(String… excludes);

PatternFilterable exclude(Iterable excludes);

PatternFilterable exclude(Spec excludeSpec);

PatternFilterable exclude(Closure excludeSpec);

该接口提供了一系列的includeexclude方法,我们可以对源集目录做一些过滤。

sourceSets {

main {

java {

exclude ‘com/cooke/library/Test.java’

exclude ‘com/cooke/library/model/**.java’

}

}

}

上面例子提到,其他的源集目录无法覆盖同名java文件,但是我们可以通过SourceSet对main目录中的java进行exclude.

注意:includeexclude并不能对res生效,如果想要对res进行过滤,需要通过定义res/raw/keep.xml,详见Android文档,这里就不具体展开了。

Variant

======================================================================

Variant 即为变体,可以分为ApplicationVariantLibraryVariant,分别对应了apk的变体和aar的变体。变体的构成由BuildTypeProductFlavor组合而成.即

variant = buildType * productFlavor

例如上面我们定义了freepaid两种productFlavor,结合debugrelease两种buildType,就产生了4种组合,如下图

我们可以遍历ApplicationVariantLibraryVariant列表,干预构建apk和aar的过程。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

学习分享

在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了

很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘

如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。

2021最新上万页的大厂面试真题

七大模块学习资料:如NDK模块开发、Android框架体系架构…

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。

由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

ndroid)**

学习分享

在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了

很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘

如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。

2021最新上万页的大厂面试真题

[外链图片转存中…(img-pkyknmTD-1712131884970)]

七大模块学习资料:如NDK模块开发、Android框架体系架构…

[外链图片转存中…(img-NS32Zhnn-1712131884971)]

只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。

这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。

由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值