我刚刚创建了一个库并上传到 bintray 和 jcenter。
在我的测试应用中,这个库被添加为一个模块:
实现项目(':dropdownview')
一切都很好。
库模块上传到jcenter后,我改用这个:
实现 '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]]
(我一直在关注 this guide 来发布库。我在使用相同方法之前发布了 3 个库,它们都运行良好;但这是我第一次在我自己的库中包含另一个第三方库依赖项.)
然后我尝试更改我的库的第 3 方库依赖项
实现 'com.andkulikov:transitionseverywhere:1.7.9'
到
编译 'com.andkulikov:transitionseverywhere:1.7.9'
(请注意,这不是应用程序对我的库的依赖,而是我的库对另一个库的依赖)
然后再次上传到 0.9.2 版本的 bintray。
实现'com.asksira.android:dropdownview:0.9.2
这次成功了吗?!
这是 Android Studio/Gradle 的某种错误(但谷歌表示他们将在 2018 年底之前删除 compile ......),还是我做错了什么?
可以找到 v0.9.1 的完整源代码 here .
请注意,我没有直接从 app 到 TransitionsEverywhere 访问任何方法。具体来说,当我点击 DropDownView 时发生 ClassNotFoundException,并且 DropDownView 调用 expand() 这是一个 public 内部方法。
为了排除其他因素,下面是我在将 implementation 更改为 compile 之前尝试过的事情,但都没有成功:
最佳答案
我现在遇到了完全相同的问题,根据您的评论,我真的怀疑这是否应该是这样。我的意思是用 api 替换库中的所有 implementation 对于干净的抽象没有意义。如果不需要,有时甚至不应该允许使用,为什么我应该将我的库的已用依赖项公开给消费者/应用程序。
我还检查了生成的 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>
我使用以下脚本添加依赖项,并基于implementation 或api 向它们添加正确的范围(based on that nice info)
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)
}
您需要了解的关键部分:
implementation 依赖项也包含 api 依赖项,只需运行任务 listDependencies() 即可查看输出runtime 范围内,API 在应用程序/消费者中不可用,但它是类路径的一部分。这样,消费者无法直接访问这些依赖项,只能通过您自己的库提供的方法使这些依赖项“不可见”,但它们将成为类路径的一部分,因此当“不可见”依赖项的那些类是由类加载器加载。上面的脚本现在生成以下 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>
总结一下:
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
}
关于android - ClassNotFoundException 当 "implementation"用于库的库依赖时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49552607/
我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou
我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-
大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这
我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que
我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file