如何使用gradle与存储和收集集成Reflections库在android studio中

时间:2017-07-16 16:29:22

标签: android gradle reflection android-gradle

我的问题与@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文件中生成信息似乎更好,以便在运行时提供类信息。但是我怎样才能正确设置呢?

谢谢

3 个答案:

答案 0 :(得分:1)

这里有一点鸡蛋或鸡蛋问题

  1. 您希望Reflections API访问从src/main/java
  2. 编译的类
  3. Gradle任务和Reflections类由Gradle buildscript classloader
  4. 加载
  5. src/main/java中的类是在buildscript类加载器定义
  6. 之后编译的

    您需要引入另一个可以访问已编译类的类加载器来打破循环依赖。然后可以将其传递给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);