用于过滤aar中冲突类(class)和so库的脚本,也可以用来过滤jar中冲突class
需求产生的原因,有时候我们接入三方包的时候,会出现类冲突,这个时候我们就想能不能把三方包中的冲突类过滤掉,不参与编译。网上百度,google都没有找到一个好的解决方案,然后自己动手丰衣足食。
灵感来源:Configuring Multi-Project Builds,创建 Android 库
AAR库的Class和SO文件过滤
将aar库导入项目的方式有2种:
1,
android{repositories {flatDir {dirs 'libs'}}
}implementation"(name: path, ext: 'aar')
2,
- 添加已编译的 AAR(或 JAR)文件:
- 点击 File > New Module。
- 依次点击 Import .JAR/.AAR Package 和 Next。
- 输入 AAR 或 JAR 文件的位置,然后点击 Finish。
- 将库模块导入到您的项目中:
- 点击 File > New > Import Module。
- 输入库模块目录的位置,然后点击 Finish。
这里我们采用第二种方法将aar包(后续的jar包也一样)导入我们的项目中,因为我希望有一个独立的module和相应的Gradle脚本文件来完成过滤class文件脚本的编写。我这里已经将想要过滤的aar导入工程了:
其实就是一个简单的module,打开它的build.gradle文件看一下:
简单解释一下这2个方法:
NamedDomainObjectContainer#maybeCreate(String)
/*** Looks for an item with the given name, creating and adding it to this container if it does not exist.** @param name The name to find or assign to the created object* @return The found or created object. Never null.*/T maybeCreate(String name);
简单翻译就是:查找具有给定名称的项目,如果该项目不存在,则创建它并将其添加到此容器中。
此容器是那个容器?
根据代码我们可以看到是:ConfigurationContainer
此项目是什么什么?
根据代码我们可以看到是:Configuration
ArtifactHandler#add(String,Object)
/*** Adds an artifact to the given configuration.** @param configurationName The name of the configuration.* @param artifactNotation The artifact notation, in one of the notations described above.* @return The artifact.*/PublishArtifact add(String configurationName, Object artifactNotation);
简单翻译就是:给给定的配置(Configuratin)添加一个工件(Artifact)。
该方法最终调用的是:
private PublishArtifact pushArtifact(Configuration configuration, Object notation, Action<? super ConfigurablePublishArtifact> configureAction) {ConfigurablePublishArtifact publishArtifact = publishArtifactFactory.parseNotation(notation);configuration.getArtifacts().add(publishArtifact);configureAction.execute(publishArtifact);return publishArtifact;}
可以看到通过artifactNotation生成了一个publishArtifact并添加到名为configurationName的Configuration中了。
那么我们如何引用这个aar的module依赖呢?
其实就简单的工程依赖就可以了:implementation project(':xxx-2.2.2')。
注:默认情况下,当你声明依赖于projectA时,你实际上声明依赖于projectA的'default'配置。如果您需要依赖projectA的特定配置,需要这样:
configurationName project(path: ':projectA', configuration: 'someOtherConfiguration')
所以上面也可以这样写:implementation project(path:':xxx-2.2.2',configuration:'default')
那我是不是可以仿照上面的,自己写一个特定的配置,然后依赖这个特定的配置?
我的思路是这样的:
1,获取到需要过滤的原始AAR包
2,解压AAR包
3,解压AAR包中所包含的jar
4,删除解压AAR包中包含的jar
5,按照过滤规则对解压之后的class文件按重新打包(AAR的class文件过滤在这里实现的)
6,按照过滤规则重新打包成AAR包(AAR的SO文件过滤在这里实现的)
7,创建一个新的Configuration并且添加一个Artifact
8,在工程中引用过滤后的AAR
其实思路很简单啦~(*^__^*)
① 获取到需要过滤的原始AAR包
def getDefaultAar() {//获取到名称为default的ConfigurationConfiguration c = configurations.getByName("default")//在Configuration中通过PublishArtifactSet获取到对应的文件def files = c.artifacts.files.filter {it.name ==~ /.*\.aar/}def file = nullif (!files.empty) {file = files[0]}return file
}
简单的解释已经体现在注释上面了
② 解压AAR包
/*** 解压getDefaultAar()返回的aar包*/
task unZipAar(type: Copy) {from zipTree(getDefaultAar())into unZipAarFile//完成aar解压之后,设置unzipJar的from和deleteJars的deletedoLast {Set<File> jarFiles = new HashSet<>()if (unZipAarFile.exists()) {unZipAarFile.traverse(type: groovy.io.FileType.FILES, nameFilter: ~/.*\.jar/) { file ->jarFiles.add(file)}}unzipJar.from(jarFiles.collect {zipTree(it)})deleteJars.delete(jarFiles)}
}
③ 解压AAR包中所包含的jar
/*** 注意:from 已经在上面的unZipAar设置了* 解压aar包中包含的jar包*/
task unzipJar(type: Copy) {into unZipJarFile
}
④ 删除解压之后的jars
/**** 注意:from 已经在上面的unZipAar设置了* 删除解压之后的jars*/
task deleteJars(type: Delete)
⑤ 按照过滤规则对解压的class.jar重新打包(这个是重点)
/*** 用Jar任务过滤并生成新的jar包*/
task zipJar(type: Jar) {baseName = 'classes'from unZipJarFiledestinationDir unZipAarFileexclude getExcludePackageRegex(excludePackages)exclude getExcludeClassRegex(excludeClasses)
}
⑥ 重新打包成AAR包
/*** 重新把文件压缩成新的aar包*/
task excludeAar(type: Zip) {group 'Siy'description '生成一个过滤之后的aar包'baseName excludeAarNameextension "aar"from unZipAarFileexclude getExcludeSoRegex(excludeSos)destinationDir excludeAarFile
}
⑦ 创建一个新的Configuration并且添加一个Artifact
configurations.maybeCreate("exclude")
artifacts.add("exclude",excludeAar)
⑧ 在工程中引用过滤后的AAR
implementation project(path: ':xxx_aar', configuration: 'exclude')
附:
//需要过滤的包名
String[] excludePackages = ['com.baidu']
//需要过滤的类(需要全类名,不需要.class结尾)
String[] excludeClasses = []
//需要过滤的so
String[] excludeSos = ['liblocSDK7b']static String[] getExcludePackageRegex(String[] packages) {packages?.collect {it?.replace('.', '\\')?.plus("\\**")}
}static String[] getExcludeClassRegex(String[] classes) {classes?.collect {it?.replace('.', '\\')?.plus(".class")}
}static String[] getExcludeSoRegex(String[] sos) {sos?.collect {"**\\${it}.so"}
}
OK!我们看看效果:
那么我的过滤配置需要这样写:
//需要过滤的包名
String[] excludePackages = ["com.payeco.android.plugin.ui.datepick","com.payeco.android.plugin.ui.view"]
//需要过滤的类(需要全类名,不需要.class结尾)
String[] excludeClasses = ["com.payeco.android.plugin.payin.PayecoPluginPayIn"]
//需要过滤的so
String[] excludeSos = []
过滤后的效果:
很明显通过依赖到External Libraries的AAR已经没有datepick,view这个2个包也没有PayecoPluginPayIn这个类了。
其实写到这里,AAR过滤指定package和class已经实现了。这里我再提几个小知识点:
1,不是任何类型都可以做artifactNotation
从这个错误图里面可以看出它支持哪些类型。
2,我们可以这样创建一个新的Configuration并且添加一个Artifact
configurations {exclude
}
artifacts {exclude excludeAar
}
3,为了解耦我们可以把这些过滤脚本写在一个单独的Gradle文件中,如上面:excludeAar.gradle
引用是在build.gradle中加上
apply from: "${project.projectDir.absoluteFile}\\excludeAar.gradle"
这里附上完整的excludeAar.gradle代码:excludeAar.gradle
Jar库的Class文件过滤
Jar库的实现思路和AAR库是一样的,比AAR还要简单
1,获取到需要过滤的原始JAR包
2,解压JAR包
3,按照过滤规则对解压的class文件重新打包
4,创建一个新的Configuration并且添加一个Artifact
5,在工程中引用过滤后的JAR
① 获取到需要过滤的原始JAR包
def getDefaultJar() {Configuration c = configurations.getByName("default")def files = c.artifacts.files.filter {it.name ==~ /.*\.jar/}def file = nullif (!files.empty) {file = files[0]}return file
}
② 解压JAR包
/*** 解压getDefaultJar()返回的jar文件*/
task unzipJar(type: Copy) {def zipFile = getDefaultJar()def outputDir = unZipJarFilefrom zipTree(zipFile)into outputDir
}
③ 按照过滤规则对解压的JAR重新打包
/*** 用Jar任务过滤并生成新的jar包*/
task excludeJar(type: Jar) {group 'Siy'description '生成一个过滤之后的jar包'//需要打包的资源所在的路径集和from unZipJarFile//去除路径集下部分的资源exclude getExcludePackageRegex(excludePackages)exclude getExcludeClassRegex(excludeClasses)//整理输出的 Jar 文件后缀名extension = "jar"//最终的 Jar 文件名......如果没设置,默认为 [baseName]-[appendix]-[version]-[classifier].[extension]baseName = excludeJarName//生成jar包的路径destinationDir excludeJarFile
}
④ 创建一个新的Configuration并且添加一个Artifact
configurations.maybeCreate("siy_test")
artifacts.add("siy_test", zipJar)
⑤ 在工程中引用过滤后的JAR
implementation project(path: ':test_jar_exclude', configuration: 'siy_test')
附:
//需要过滤的包名
def excludePackages = ['com.baidu']
//需要过滤的类(需要全类名)
def excludeClasses = []static def getExcludePackageRegex(def packages) {packages?.collect {it?.replace('.', '\\')?.plus("\\**")}
}static def getExcludeClassRegex(def classes) {classes?.collect {it?.replace('.', '\\')?.plus(".class")}
}
这里附上完整的excludeJar.gradle代码:excludeJar.gradle
上面说的那么多都是用来解释原理,这里给出一个简单的使用方法。
JCenter地址:
classpath 'coder.siy:exclude-dependencies-plugin:1.0.0'
使用方法
apply plugin: 'exclude_plugin'excludePluginExt {autoDependencies = true //是否自动依赖即是否依赖过滤之后的架包aars {BaiduLBS_Android_debug { //过滤架包的名称path "/libs/exclude/BaiduLBS_Android_debug.aar" //架包的路径excludePackages 'com.baidu.location' //过滤的包名}}jars{BaiduLBS_Android_7_5_2{//过滤架包的名称path "/libs/exclude/BaiduLBS_Android_7.5.2.jar" //架包的路径excludePackages 'com.baidu.android','com.baidu.lbsapi' //过滤的包名}map_baidu{//过滤架包的名称path "/libs/exclude/map-baidu.jar"//架包的路径excludePackages "io.dcloud.js.map.adapter"//过滤的包名excludeClasses "io.dcloud.js.map.IFMapDispose","io.dcloud.js.map.JsMapCircle","io.dcloud.js.map.MapJsUtil"//过滤的类名}}}
注意:
配置完之后运行一下根据名称生成的过滤任务
属性解释:
属性名 | 默认值 | 解释 |
---|---|---|
path | 路径 | 无默认值(必要值) |
excludePackages | 空数组 | 需要过滤的包名 |
excludeClasses | 空数组 | 需要过滤的类名(全类名,不要".class"结尾) |
excludeSos | 空数组 | 需要过滤的so名(不要".so"结尾,aar包特有) |
番外:如果你认真看了上面的文章,提一个问题:apply plugin: 'com.android.application' 的工程可以被依赖么。
答案:可以的
从上面知道 implementation project(':app')就相当于implementation project(path: ':app', configuration: 'default')。
apply plugin: 'com.android.application' 工程 default 下面没有任何文件。
Configuration c = project.configurations.getByName("default")c.artifacts.files.each {println(it.name)}
所以没办法引用,如果我们这样改一下,application也是可以依赖的。
configurations.maybeCreate("test")
artifacts.add("test", file('BaiduLBS_Android_release.aar'))dependencies {implementation project(path: ':app',configuration:'test')
}
gitHub地址:这里
用于过滤aar中冲突类(class)和so库的脚本,也可以用来过滤jar中冲突class相关推荐
- C++对象内存布局--③测试多继承中派生类的虚函数在哪一张虚函数表中
C++对象内存布局--③测试多继承中派生类的虚函数在哪一张虚函数表中 测试2:证明派生类的虚函数的地址跟第一基类的虚函数地址保存在同一张虚函数表中. 派生类有多少个拥有虚函数的基类,派生类对象就有多少 ...
- android jar包冲突_用好这几个技巧,解决Maven Jar包冲突易如反掌
前言 大家在项目中肯定有碰到过Maven的Jar包冲突问题,经常出现的场景为: 本地运行报NoSuchMethodError,ClassNotFoundException.明明在依赖里有这个Jar包啊 ...
- 关于两个jar包中存在包名和类名都完全相同的jar包冲突问题
2019独角兽企业重金招聘Python工程师标准>>> 最近弄webservice,我使用的jdk自带的wsimport工具来生成客户端代码,发现生成的代码具有编译错误,错误描述如下 ...
- 【持续更新】C++中string类使用总结
C++中string类的功能以及使用方法可类比于C语言中的字符数组,但是相互比较之下可谓是是有过之而无不及,实在是太方便,故留此贴总结日常学习所得经验. 智障blog,排版这么难???? 1. 创建字 ...
- C#中自定义类数组和结构数组的使用
如有雷同,不胜荣幸,若转载,请注明 C#中自定义类数组和结构数组的使用 最近在很多项目中发现很多时候给定的数组要实现某个逻辑或处理很是麻烦,一维数组,二维数组,,,等等需要经过n多转换,还不如自己写一 ...
- 正确认识使用UML中的类图——辨析类图的两种存在形式
摘要 本文通过对一个"学生选课系统"示例的简要分析与设计,说明UML图之一类图的两种作用及存在形式,以期借此澄清有些朋友可能对类图存在的误解与困惑. 前言 ...
- 游戏开发小结——在unity3d中开发类塞尔达游戏的反冲效果
游戏开发小结--在unity3d中开发类塞尔达游戏的反冲效果 在 2D ARPG 中为角色创建反冲效果 我们将在Unity中实现这个效果.首先,我们将进行演示场景的设置.我们将创建一个控制器(cont ...
- lib目录和maven dependency目录的jar包冲突
用eclipse时新建项目时,会在lib目录下自动生成一些jar包,然后又在pom.xml文件中添加了依赖,导致lib下的jar包和maven dependency目录下的jar包产生了冲突.刚开始r ...
- (附源码)springboot基于微信小程序的高校计算机类课程思政库的设计与实现 毕业设计 271611
小程序+spring boot高校计算机类课程思政库 摘 要 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用 ...
最新文章
- Linux 准确查找结构体定义位置
- http://www.csdn.net/
- 如何获取filecoin_Filecoin如何获取更大的出块机会?
- 干货 | Elasticsearch7.X Scripting脚本使用详解
- 百度推送java_关于百度推送,请教一下大家
- powerbi python词云图_Python 练手项目: 抓取豆瓣陈情令评论,并制作词云图
- 术语html的含义是,术语html指的是什么
- 大道至简 爱上Metro
- style文件的指定
- ai电磁组属于什么组_飞思卡尔智能车电磁组分区算法介绍
- STM32定义数组到flash的指定位置
- 虚拟机客户端访问不了服务器域名,虚拟机ESXI 篇四:让你电脑打开网页不用等待---SmartDns...
- Educational Codeforces Round 91 D. Berserk And Fireball
- 玉伯:从前端到体验,如何把格局做大
- easyswoole not controller class match
- mc服务器怎么修改浮空字,我的世界浮空字体怎么使用 浮空字体使用攻略
- 2021最新上海互联网公司排名
- 光场视差与深度的关系(lytro深度计算公式)
- 云南2018年GDP增长8.9% 较2017年增长速度有所下降
- 使用icecast搭建在线电台并使用ffmpeg推流