我的问题与@ronmamo在github上的Reflections库有关,并将其集成到我的Android项目中,以动态访问从某个接口继承的所有类。
我对gradle或maven并不熟悉,所以这对我来说是一个学习过程,但我遇到了障碍,不知道如何调试/找到答案。
正如@ronmamo建议here,我想在构建中生成一个包含所有已扫描元数据的xml文件,并且当我在代码中使用它时,让Reflection收集它:
虽然可以在您的自举时间轻松完成扫描 应用 - 并且不应该花费很长时间,这有时是一个好主意 将思考融入您的构建生活中。简单 Maven / Gradle / SBT /您可以保存所有扫描的任何配置 编译时间之后的元数据到xml / json文件中。后来,什么时候 你的项目是引导你可以让思考收集所有 这些资源并为您重新创建元数据 在运行时可用,无需重新扫描类路径 - 从而减少 自举时间。
我不确定我是否完全理解这个" bootstrapping"发生(在Android应用程序生命周期等方面,甚至建立时间?)所以我不确定究竟在哪里调用Reflections.collect()。目前,当用户已达到程序中的某个点时,我稍后会在我的应用程序中调用它。
从几个stackoverflow帖子和git自述文件,我现在想出了这个:( [...]意味着删除了不相关的代码)
build.gradle(模块:app):
dependencies {
[...]
compile 'org.reflections:reflections:0.9.11'
}
build.gradle(Project:MyProject):
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'org.reflections:reflections:0.9.11'
}
}
allprojects {
repositories {
jcenter()
}
}
task runReflections {
doLast {
org.reflections.Reflections("f.q.n").save("${sourceSet.main.output.classesDir}/META-INF/reflections/myproject-reflections.xml")
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
稍后在我的代码中(这个类在某个时候通过用户输入到达,而不是在app start上加载):
Reflections reflections = Reflections.collect();
Set<Class<? extends MyInterface>> allClasses = reflections.getSubTypesOf(MyInterface.class);
这会产生以下异常,因为&#34;反射&#34;未实例化且值为&#34; null&#34;:
Attempt to invoke virtual method 'java.util.Set org.reflections.Reflections.getSubTypesOf(java.lang.Class)' on a null object reference
据我所知,生成的.xml文件驻留在构建正在进行的计算机上,我不确定这是否也会转移到Android设备上,所以我的猜测是这就是为什么这会失败。但是,在我的Android设备上传输并运行apk之前,我的Java代码在什么时候可以访问此文件?
我尝试从不同的角度以多种不同的方式搜索它,但我似乎无法找到使反射在Android中工作的解决方案。我理解这里解释的原理,在构建时在xml文件中生成信息似乎更好,以便在运行时提供类信息。但是我怎样才能正确设置呢?
谢谢
答案 0 :(得分:1)
这里有一点鸡蛋或鸡蛋问题
Reflections
API访问从src/main/java
Reflections
类由Gradle buildscript
classloader src/main/java
中的类是在buildscript
类加载器定义您需要引入另一个可以访问已编译类的类加载器来打破循环依赖。然后可以将其传递给Reflections
。例如:
buildscript {
classpath 'org.reflections:reflections:0.9.11'
}
task doReflectyStuff {
dependsOn compileJava
doLast {
URL[] urls = sourceSets.main.runtimeClasspath.files.collect {
it.toURI().toURL()
}
ClassLoader classLoader = new URLClassLoader(urls, null)
Configuration config = new ConfigurationBuilder("com.mypackage", classLoader)
Reflections reflections = new ReflectionsBuilder(config)
...
}
}
有关类似问题,请参阅here
答案 1 :(得分:1)
我一直试图在Android中使用Reflections几天,这是我迄今为止所取得的成就。我在project&gra; build.gradle中创建了一个任务:
task myTask(dependsOn: compileJava) {
doLast {
URL[] urls = sourceSets.main.runtimeClasspath.files.collect {
it.toURI().toURL()
}
ClassLoader classLoader = new URLClassLoader(urls, ClassLoader.systemClassLoader)
org.reflections.Configuration config = new ConfigurationBuilder()
.addClassLoader(classLoader)
.filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("com.company.project")))
.addScanners(new SubTypesScanner(false))
Reflections reflections = new Reflections(config)
reflections.save("${sourceSets.main.output.classesDirs}/META-INF/reflections/mcommerce-reflections.json", new JsonSerializer())
}
}
稍后在项目的一个类中,我实例化了Reflections,就像在GitHub的例子中一样(我使用Kotlin):
val reflections = Reflections.collect(
"META-INF/reflections",
FilterBuilder().include(".*-reflections.json"),
JsonSerializer()
)
如果在终端上运行myTask,则构建成功,但我收到此消息&#34;给定扫描网址为空。在配置中设置网址&#34;,我在Google上搜索了这个网址,但没有找到任何有用的信息。
我尝试了在gradle文件上配置Reflections的不同方法,但是当我收集它们时,我总是收到一个null实例。
我希望我的答案对某人有用。
答案 2 :(得分:1)
这就是我所做的:任务是在Android上对带有依赖项的类(即JAR文件中的类)使用Reflections。此解决方案对我有用:
top build.gradle:
dependencies {
classpath 'org.reflections:reflections:0.9.10'
}
项目build.gradle:
afterEvaluate {
android.applicationVariants.each { variant ->
variant.javaCompiler.doLast {
// get JAR file that contains the classes
def collection = project.configurations.compile*.toURI().find { URI uri -> new File(uri).name.startsWith("startOfJarFileNameHere") }
URL[] urls = collection.collect {
println "Collecting classes using Reflections from " + it
it.toURL()
}
// collect all classes
ClassLoader classLoader = new URLClassLoader(urls, ClassLoader.systemClassLoader)
org.reflections.Configuration config = org.reflections.util.ConfigurationBuilder
.build("package.name.of.interest.here")
.addClassLoader(classLoader)
.setUrls(urls)
org.reflections.Reflections reflections = new org.reflections.Reflections(config)
// save as JSON file into the assets folder
// (a) generate file for current debug or release build
reflections.save(
"${variant.javaCompiler.destinationDir}/../../assets/${variant.buildType.name}/reflections/my-reflections.json",
new org.reflections.serializers.JsonSerializer())
// (b) always update fall-back file for debug (used when running app from Android Studio or IntelliJ)
reflections.save(
"${variant.javaCompiler.destinationDir}/../../../../src/debug/assets/reflections/my-reflections.json",
new org.reflections.serializers.JsonSerializer())
}
}
}
Android上的Java代码:
InputStream iStream = getAssets().open("reflections/my-reflections.json");
Configuration config = ConfigurationBuilder.build().setSerializer(new JsonSerializer());
Reflections reflections = new Reflections(config);
reflections.collect(iStream);
Set<Class<? extends MyType>> myTypes = reflections.getSubTypesOf(MyType.class);