我终于被 implementation 和 api 逼疯了

7,859 阅读3分钟

背景

本人是一枚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仓库!让用户的代码跑的飞起!