当"实现"用于库的库依赖时,ClassNotFoundException

Sir*_*Lam 7 dependencies android gradle android-studio android-gradle-plugin

我刚刚创建了一个库并上传到bintray和jcenter.

在我的测试应用程序中,此库作为模块添加:

implementation project(':dropdownview')

一切都井井有条.

将库模块上传到jcenter后,我改为使用它:

implementation 'com.asksira.android:dropdownview:0.9.1

然后,当库尝试调用依赖于另一个库的方法时,会发生运行时错误:

Caused by: java.lang.ClassNotFoundException: Didn't find class "com.transitionseverywhere.TransitionSet" on path: DexPathList[[zip file "/data/app/com.asksira.dropdownviewdemo-6fj-Q2LdwKQcRAnZHd2jlw==/base.apk"],nativeLibraryDirectories=[/data/app/com.asksira.dropdownviewdemo-6fj-Q2LdwKQcRAnZHd2jlw==/lib/arm64, /system/lib64, /system/vendor/lib64]]
Run Code Online (Sandbox Code Playgroud)

(我按照本指南发布了库.我在使用相同的方法之前已经发布了3个库,它们都运行良好;但这是我第一次在我自己的库中包含另一个第三方库依赖项.)

编译与实现

然后我尝试更改我的库的第三方库依赖项

implementation 'com.andkulikov:transitionseverywhere:1.7.9'

compile 'com.andkulikov:transitionseverywhere:1.7.9'

(请注意,这不是app对我的库的依赖,而是我的库到另一个库)

然后再次上传到版本0.9.2的bintray.

implementation 'com.asksira.android:dropdownview:0.9.2

这次它工作了吗?!

我的问题

这是Android Studio/Gradle的某种错误(但Google表示他们将在compile2018年底删除...),或者我做错了什么?

可在此处找到v0.9.1的完整源代码.

请注意,我并没有直接访问任何方法appTransitionsEverywhere.具体来说,ClassNotFoundException当我点击DropDownView,并DropDownView调用expand()哪个是public内部方法时发生.

更多信息

为了消除等因素的影响,以下是我已经改变之前试过的东西implementationcompile,根本没有运气:

  1. 清洁和重建
  2. 卸载app + clean并重新构建
  3. 使应用程序成为MultiDexApplication
  4. 即时运行已被禁用

War*_*ith 4

我现在遇到了完全相同的问题,根据您的评论,我真的怀疑这是否应该是这样。我的意思是用干净的抽象替换implementation库内的所有内容是api没有意义的。如果不需要,有时甚至不应该允许使用我的库的使用依赖项,为什么我应该向消费者/应用程序公开它们。

我还检查了生成的 APK 确实包含它抱怨找不到的类。

由于我之前遇到过依赖问题,我记得我自己改进了为库生成的 POM。

在我改进之前,生成的pom是这样的:

<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>tld.yourdomain.project</groupId>
    <artifactId>library-custom</artifactId>
    <version>1.2.0-SNAPSHOT</version>
    <packaging>aar</packaging>
    <dependencies/>
</project>
Run Code Online (Sandbox Code Playgroud)

我使用以下脚本添加依赖项,并基于implementationapi向它们添加正确的范围(基于该不错的信息

apply plugin: 'maven-publish'

task sourceJar(type: Jar) {
    from android.sourceSets.main.java.srcDirs
    archiveClassifier = "sources"
}

task listDependencies() {
    // Curious, "implementation" also contains "api"...
    configurations.implementation.allDependencies.each { dep -> println "Implementation: ${dep}" }
    configurations.api.allDependencies.each { dep -> println "Api: ${dep}" }
}

afterEvaluate {
    publishing {
        publications {
            mavenAar(MavenPublication) {
                groupId libraryGroupId
                artifactId libraryArtefactId
                version versionName

                artifact sourceJar
                artifact bundleReleaseAar

                pom.withXml {
                    def dependenciesNode = asNode().appendNode('dependencies')
                    configurations.api.allDependencies
                            .findAll { dependency -> dependency.name != "unspecified" }
                            .each { dependency ->
                        addDependency(dependenciesNode.appendNode('dependency'), dependency, "compile")
                    }

                    configurations.implementation.allDependencies
                            .findAll { dependency -> !configurations.api.allDependencies.contains(dependency) }
                            .findAll { dependency -> dependency.name != "unspecified" }
                            .each { dependency ->
                        addDependency(dependenciesNode.appendNode('dependency'), dependency, "runtime")
                    }
                }
            }
        }

        repositories {
            maven {
                def snapshot = "http://repo.yourdomainname.tld/content/repositories/snapshots/"
                def release = "http://repo.yourdomainname.tld/content/repositories/releases/"
                url = versionName.endsWith("-SNAPSHOT") ? snapshot : release
                credentials {
                    username nexusUsername
                    password nexusPassword
                }
            }
        }
    }
}

def addDependency(dependencyNode, dependency, scope) {
    dependencyNode.appendNode('groupId', dependency.group)
    dependencyNode.appendNode('artifactId', dependency.name)
    dependencyNode.appendNode('version', dependency.version)
    dependencyNode.appendNode('scope', scope)
}
Run Code Online (Sandbox Code Playgroud)

您需要了解的关键部分:

  • 如果您没有定义范围,则将假定为“编译”
  • implementationapi依赖项也包含这些,只需运行任务listDependencies()即可查看输出
  • 在该范围内,runtimeAPI 在应用程序/消费者中不可用,但属于类路径的一部分。这样,消费者就无法直接访问这些依赖项,只能通过您自己的库提供的方法使这些依赖项“不可见”,但它们将成为类路径的一部分,因此当“不可见”依赖项的这些类被访问时,应用程序不会崩溃。由类加载器加载。

上面的脚本现在生成以下 pom:

<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>tld.yourdomain.project</groupId>
    <artifactId>library-custom</artifactId>
    <version>1.2.0-SNAPSHOT</version>
    <packaging>aar</packaging>
    <dependencies>
        <dependency>
            <groupId>tld.dependency</groupId>
            <artifactId>android-sdk</artifactId>
            <version>1.2.3</version>
            <scope>compile</scope> <!-- From api -->
        </dependency>
        <dependency>
            <groupId>tld.dependency.another</groupId>
            <artifactId>another-artifact</artifactId>
            <version>1.2.3</version>
            <scope>runtime</scope> <!-- From implementation -->
        </dependency>
        <!-- and much more -->
    </dependencies>
</project>
Run Code Online (Sandbox Code Playgroud)

把它们加起来:

  • api发布类,使消费者也可以访问依赖项
  • implementation也运送类,但不会使依赖项可供使用者访问,但在定义的runtime范围内,它仍然是类路径的一部分,使类加载器知道这些类在运行时可用

编辑

如果您要进行大量更改并测试快照,请确保已禁用它们的缓存。将其添加到您的根 build.gradle 文件中:

allprojects {
    configurations.all() {
        // to make sure SNAPSHOTS are fetched again each time
        resolutionStrategy.cacheDynamicVersionsFor 0, 'seconds'
        resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
    }
    // more stuff here
}
Run Code Online (Sandbox Code Playgroud)