我终于被 implementation 和 api 逼疯了
背景
本人是一枚Android SDK开发程序猿,就是开发SDK以提供给客户使用。以前我们SDK开发本着能不依赖第三方就不依赖第三方的原则,使用的全是原生提供的API。
由于上次经过爬虫 Android Push哪家强——分析豌豆荚1400个APP 得到使用OkHttp的APP占比已经很高了,再加上看到国外的SaaS服务公司提供的SDK对于接入第三方开源SDK很是开放。所以我们也想将底层网络库从传统的HttpURLConnection切换为OkHttp3。然而这才是噩梦的开始……
依赖产生问题
开发进行的很顺利,我们在自己的SDK module中添加了okhttp的依赖
dependencies {
...
implementation 'com.squareup.okhttp3:okhttp:3.12.1'
}
在测试demo APP中添加了SDK module依赖
dependencies {
...
implementation project(':sdk-lib')
}
嗯,demo APP运行的很完美,代码是跑的飞起。但是当我们发布到Maven上去,然后新建一个APP直接添加我们的依赖的时候,出现问题了。
dependencies {
...
implementation 'com.xxxx.xxxx:sdk-lib:1.0.0'
}
居然报出NoClassDefFoundError什么情况?!我不是在我的sdk-lib已经添加了OkHttp的依赖了吗?!
java.lang.NoClassDefFoundError: Failed resolution of: Lokhttp3/MediaType;
implementation 和 api的区别
根据官网依赖项配置的介绍
implementation 只是不对外暴露依赖的SDK的API而已呀,为什么主APP没有产生依赖呢?不是说运行时会提供?我们来看看最后Maven仓库中的pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xxxx.xxxx</groupId>
<artifactId>sdk-lib</artifactId>
<version>1.0.0</version>
<packaging>aar</packaging>
<dependencies>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.12.1</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
我们看到scope的类型是runtime。百度后基本的做法就是在主APP的依赖中重新添加OkHttp的依赖,或者使用api替代implementation。好,那我们使用api依赖后再看看依赖的pom文件,发现scope的类型是compile。
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xxxx.xxxx</groupId>
<artifactId>sdk-lib</artifactId>
<version>1.0.0</version>
<packaging>aar</packaging>
<dependencies>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.12.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
重新新建一个APP并依赖新的sdk-lib,果然没问题,代码跑的飞起。
更复杂的情况
如果到这里基本已经结束了,那岂不是又是一篇水文?众所周知,我们在开发和发布的时候经常会有不同的依赖情况,比如开发的时候使用的是module依赖,发布的时候使用的线上Maven库依赖。比如需要发布的sdk-lib SDK依赖了一个自己开发core模块。
dependencies {
...
debugApi project(':core-lib')
releaseApi "com.xxxx.xxxx:core:$coreVersion"
}
当我们使用gradle的generatePomFileForReleasePublication Task生成Pom文件发现,并没有core-lib的依赖,什么鬼?!不是说好用api就行了吗?!
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xxxx.xxxx</groupId>
<artifactId>sdk-lib</artifactId>
<version>1.0.0</version>
<packaging>aar</packaging>
</project>
解决问题
没办法,看来gradle的发布插件maven-publish已经满足不了我们了,那就自己编写pom依赖吧,把所有的 implementation 和 api 依赖都添加进去。如下,在APP的build.gradle中添加
publishing {
publications {
mavenAgent(MavenPublication) {
artifact "${project.buildDir}/outputs/aar/${project.name}-release.aar"
groupId yourGroupId
artifactId yourArtifactId
version yourVersion
pom.withXml {
writePom(asNode())
}
}
}
}
void writePom(node) {
def allDependencies = new HashSet<DependencySet>()
allDependencies.addAll(configurations.api.allDependencies)
allDependencies.addAll(configurations.releaseApi.allDependencies)
allDependencies.addAll(configurations.implementation.allDependencies)
allDependencies.addAll(configurations.releaseImplementation.allDependencies)
def iterator = allDependencies.iterator()
while (iterator.hasNext()) {
def dep = iterator.next()
//移除project类型的依赖
if (dep.name == "unspecified" || dep.version == "unspecified") {
iterator.remove()
}
}
def depsNode = node.appendNode('dependencies')
allDependencies.each { dep ->
def depNode = depsNode.appendNode('dependency')
depNode.appendNode('groupId', dep.group)
depNode.appendNode('artifactId', dep.name)
depNode.appendNode('version', dep.version)
depNode.appendNode('scope', 'compile')
}
}
我们在gradle任务列表中执行下属两个任务(其中mavenAgent是你自己取的发布别名),并查看依赖情况。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xxxx.xxxx</groupId>
<artifactId>sdk-lib</artifactId>
<version>1.0.0</version>
<packaging>aar</packaging>
<dependencies>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.12.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.xxxx.xxxx</groupId>
<artifactId>core</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
好,大功告成,发布到线上jcenter仓库!让用户的代码跑的飞起!