上半年的技术需求,是做白牌组件集成,就是将白牌独立出来的组件,集成到合作项目中.
看起来很简单,就像我们使用okhttp,只要导入一句**compile 'com.squareup.okhttp3:okhttp:3.4.1'**即可.
但是事与愿违.过程充满了艰辛,下面记录一下这次集成中的总结.
项目背景
首先明确一下项目的背景.
在合作项目中,小说使用replugin形式集成,表现为一个插件apk.我们称之为壳app,这个app是一个壳,他的实际入口为壳app中依赖的读书aar.
我们所要做的,就是在这个aar中集成白牌组件.即插件apk-->读书aar-->白牌组件.
以下所有均围绕这个过程准备.在此之前我们需要做一个依赖统一管理,如果不做这个,那么各个版本依赖冲突起来,简直解决到怀疑人生.
依赖统一
在项目根目录新建config.gradle文件,配置如下
ext {
android = [
minSdkVersion : 15,
targetSdkVersion : 25,
compileSdkVersion: 25,
buildToolsVersion: "25.0.3"
]
dependencies = [
"support-v4" : 'com.android.support:support-v4:25.3.1',
"appcompat-v7" : 'com.android.support:appcompat-v7:25.3.1',
"okhttp3" : 'com.squareup.okhttp3:okhttp:3.4.1',
"xlog" : 'com.xxxx.cooperate.xlog:xlog-common:1.0.0',
"mmkv" : 'com.tencent:mmkv:1.0.10',
"android-gif-drawable": 'pl.droidsonroids.gif:android-gif-drawable:1.2.6'
]
}
然后在项目根目录的build.gradle中引入,
apply from: "config.gradle"
就可以在任意子moudle中使用了
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
compile rootProject.ext.dependencies["appcompat-v7"]
回到我们具体项目里面,白牌组件目前需要接入的有两个,一个是common模块,一个是core模块.这两个模块也有依赖关系,表现为common依赖core.core模块依赖三方模块.
那么具体到项目表现为
SDK --->commom,core,三方
common --->core,三方
core --->三方
针对上面复杂的依赖关系,总结了四种依赖集成方式.
使用源码依赖
对项目而言,我们集成三方依赖是为了使用它的代码,完成某些任务,那么最简单的集成方式,就是将源码拷贝过来,进行项目集成.
我们可以做如下方式集成:
1.将common和core模块下载到本地,并拷贝到三方壳项目中
2.在setting.gradle中引入项目,':CooperateCommonModule',':CooperateCoreModule'
3.调整项目依赖.
core中如下:
dependencies {
compile fileTree(include: ['*.so', '*.jar'], dir: 'libs')
compile rootProject.ext.dependencies["appcompat-v7"]
compile rootProject.ext.dependencies["okhttp3"]
compile rootProject.ext.dependencies["xlog"]
compile rootProject.ext.dependencies["mmkv"]
compile rootProject.ext.dependencies["android-gif-drawable"]
}
common中如下:
dependencies {
compile fileTree(dir: 'libs/main', include: ['*.jar'])
compile fileTree(include: ['*.jar'], dir: 'libs')
compile project( ':CooperateCoreModule')
}
SDK中如下:
dependencies {
FileTree tree = fileTree(dir: 'libs', includes: ['*.so', '*.jar'])
compile tree
provided fileTree(include: ['*.jar'], dir: 'providedlib')
compile project( ':CooperateCommonModule')
}
这样就可以正常使用白牌组件了.当然这中间还有很多冲突问题需要解决.我会写在最后的备注中.
使用aar集成
有了上一步源码集成成功的前提,我们下一步改造为aar集成.aar集成有三种方式,我们使用第一种,单aar集成.
因为我们的项目是层层依赖的,那我们先从最底层的core模块处理.
对core模块进行assembleRelease命令,会在build/output文件夹获得一个aar文件.
这种build方式打出的aar,只包含模块本身的代码,不包含三方依赖.
打开内网maven,登陆后使用upload功能,上传此aar.然后配置如下

接着我们就可以使用compile ''com.xxxx.cooperate.core:core-thirdpart:1.0.2"使用此aar了.
我们提一个知识点,就是maven仓库中的某aar的pom.xml文件.这个文件记录了aar的描述,声明,依赖等.
观察这个aar的pom.xml文件,可以看到
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>comxxxx.cooperate.core</groupId>
<artifactId>core-thirdpart</artifactId>
<version>1.0.2</version>
<packaging>aar</packaging>
</project>
只有aar自身的描述,没有包含的三方依赖声明.这就和前面的只包含模块本身的代码,不包含三方依赖一致.
同理,打出common模块的aar,遵循上面的描述,我们需要配置下core模块的引用,及core模块的三方引用.
dependencies {
compile fileTree(dir: 'libs/main', include: ['*.jar'])
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.xxxx.cooperate.core:core-thirdpart:1.0.2'
compile rootProject.ext.dependencies["appcompat-v7"]
compile rootProject.ext.dependencies["okhttp3"]
compile rootProject.ext.dependencies["xlog"]
compile rootProject.ext.dependencies["mmkv"]
compile rootProject.ext.dependencies["android-gif-drawable"]
}
上传并得到使用方式compile 'com.xxxx.cooperate.common:common-thirdpart:1.0.2'
同理在SDK中使用common,core的远程依赖.
dependencies {
FileTree tree = fileTree(dir: 'libs', includes: ['*.so', '*.jar'])
compile tree
provided fileTree(include: ['*.jar'], dir: 'providedlib')
compile 'com.xxxx.cooperate.common:common-thirdpart:1.0.2'
compile 'com.xxxx.cooperate.core:core-thirdpart:1.0.2'
compile rootProject.ext.dependencies["appcompat-v7"]
compile rootProject.ext.dependencies["okhttp3"]
compile rootProject.ext.dependencies["xlog"]
compile rootProject.ext.dependencies["mmkv"]
compile rootProject.ext.dependencies["android-gif-drawable"]
}
这样就可以正常使用白牌组件了.
使用fat-aar集成
从上面的集成方式可以看到这么搞起来好像很复杂啊,有没有简单一点的方式呢.
我们可以使用fat-aar技术,官网这么解释的.
Gradle script that allows you to merge and embed dependencies in generated aar file.
它是一个gradle脚本,会把各个模块合并到一起,打成一个胖aar.这个aar里面包含了各个模块的所有东西,java文件,res文件等.
实话讲,这种方式我研究了一阵子,因为涉及到脚本的修改以及各种版本库的冲突,我放弃了这种方案.此处提供一些资料,供有兴趣的同学研究一下.
这项技术对比下面的maven远程依赖技术有优点与缺点.
优点:如果使用方无法访问我们的maven库,那可以把所有模块资源打成fat-aar,提供一个aar供依赖方使用.
缺点:这个库已经不维护了,需要使用特定版本的gradle编译,并需要修改脚本,适应自己的项目.
使用maven远程依赖集成
探索到了现在我一直在思考一个问题,github上有很多组件供大家使用,他们是怎么完成这个依赖的问题呢?
比如我们使用的okhttp,它依赖okio,但是我们使用的时候,只需要compile okhttp就行了.
以前也在网上搜过相应的文章,但是只是简单的提到,需要把三方库变成maven远程依赖.对于我们项目,方式二已经变成了远程依赖,但是为什么做不到呢?
其实,很简单,和库的pom.xml有关.我们方式二打包出来的pom.xml,是在网页生成的,并没有这个类的远程依赖描述.我们观察下别人家的库pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.tencent.news</groupId>
<artifactId>xxxx-lib</artifactId>
<version>2.7.20</version>
<packaging>aar</packaging>
<dependencies>
<dependency>
<groupId>com.android.support</groupId>
<artifactId>appcompat-v7</artifactId>
<version>25.2.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.tencent.news</groupId>
<artifactId>xxxx</artifactId>
<version>2.7.20</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.tencent.news</groupId>
<artifactId>xxxx</artifactId>
<version>2.7.20</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
这里面有一个节点dependencies描述了当前库的远程依赖.如何生成这个pom.xml文件就和我们的打包方式有关了.
方式二中我们使用assembleRelease方式打包,这种方式打出来的包,只是简单的压缩了下代码.我们需要引入maven打包方式.
1.在core模块下新建upload.gradle文件
// 指定编码
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
apply plugin: 'maven'
// 对应的仓库地址
def URL_PUCBLIC = "http://dev.inner.xxxx.local/nexus/repository/maven-releases/"
def USERNAME = "xxxx"
def PASSWORD = "xxxx"
def GROUP_ID = "com.xxxx.cooperate.core"
def ARTIFACT_ID = "core-thirdpart"
def VERSION = "1.0.5"
// 上传到公共仓库
task uploadToPublic(type: Upload) {
group = 'upload'
configuration = configurations.archives
uploadDescriptor = true
repositories{
mavenDeployer {
repository(url: URL_PUCBLIC) {
authentication(userName: USERNAME, password: PASSWORD)
}
pom.version = VERSION
pom.artifactId = ARTIFACT_ID
pom.groupId = GROUP_ID
}
}
}
2.在core的build.gradle中引入
// 引用上传脚本
apply from: "./upload.gradle"
3.刷新项目,就可以再gradle的任务中看到uploadToPublic,双击执行这个task.就可以在maven库中看到当前aar已上传.我们观察下它的pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xxxx.cooperate.core</groupId>
<artifactId>core-thirdpart</artifactId>
<version>1.0.5</version>
<packaging>aar</packaging>
<dependencies>
<dependency>
<groupId>com.android.support</groupId>
<artifactId>appcompat-v7</artifactId>
<version>25.3.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.4.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.xxxx.cooperate.xlog</groupId>
<artifactId>xlog-common</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.tencent</groupId>
<artifactId>mmkv</artifactId>
<version>1.0.10</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>pl.droidsonroids.gif</groupId>
<artifactId>android-gif-drawable</artifactId>
<version>1.2.6</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
可以看到我们已经做到了当前aar的三方远程依赖.
4.在common中也可以按照上述操作.
整理完以后我们可以看下依赖如何:
common中使用
dependencies {
compile fileTree(dir: 'libs/main', include: ['*.jar'])
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.xxxx.cooperate.core:core-thirdpart:1.0.5'
}
在SDK中使用
dependencies {
FileTree tree = fileTree(dir: 'libs', includes: ['*.so', '*.jar'])
compile tree
provided fileTree(include: ['*.jar'], dir: 'providedlib')
compile 'com.xxxx.cooperate.common:common-thirdpart:1.0.3'
}
完美!
这才是我心目中的使用方式,其实我们也可以把sdk的aar做下类似的包装.不过就比较麻烦了,还有一堆provided的依赖需要处理,这个还是暴露给业务方处理比较合适.
备注
冲突问题如何解决.
在解决aar打包问题前,我使用的是源码依赖.但是这种最简单的方式依旧冲突重重.
首先提供学习资料
Gradle3.0新指令api、provided、implementation等对比
只要掌握了exclude,provided的奥义,就能排除问题.我们以wup.jar为例,看下jar类型冲突解决.
子模块Reader,Jce均使用了wup.jar,如果都使用compile的话,会出现类冲突.所以我们可以在各自模块中,使用
provided fileTree(include: ['*.jar'], dir: 'providedlib')
只保证编译期通过,不降代码打入各模块
然后在最顶层的app中,打入wup.jar
compile fileTree(include: ['*.jar'], dir: 'libs')
这样就解决了类冲突.
在集成当中,我发现,冲突最多的还是android自身的support包,各种版本号不一致,会导致各种各样的问题.我们尽量使用依赖的统一管理,来统一各模块的库版本号,尽可能的减少冲突发生.