How to integrate the Reflections library in android studio using gradle with save and collect

android reflection
reflections jar

My question is related to the Reflections library by @ronmamo on github and integrating this into my Android project to dynamically access all classes that inherit from a certain interface.

I am not that familiar with gradle or maven so this is a learning process for me but i have reached a roadblock and do not know how to debug / find an answer to this one.

As @ronmamo suggests here, I want to generate a xml file on build containing all scanned metadata and let Reflections collect it later when I use it in my code:

Although scanning can be easily done on bootstrap time of your application - and shouldn't take long, it is sometime a good idea to integrate Reflections into your build lifecyle. With simple Maven/Gradle/SBT/whatever configuration you can save all scanned metadata into xml/json files just after compile time. Later on, when your project is bootstrapping you can let Reflections collect all those resources and re-create that metadata for you, making it available at runtime without re-scanning the classpath - thus reducing the bootstrapping time.

I am not sure I fully understand where exactly in the entire process this "bootstrapping" takes place (in terms of the android app lifecycle etc. or even build time?) so I am not certain where exactly to call Reflections.collect(). Currently I am calling it at some point later in my app when the user has reached a certain point in the program.

From several stackoverflow posts and the git readme files, I have come up with this for now: ([...] means removed unrelated code)

build.gradle (Module: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
}

And later on in my code (this class is reached at some point through user input, not loaded on app start):

Reflections reflections = Reflections.collect(); 
Set<Class<? extends MyInterface>> allClasses = reflections.getSubTypesOf(MyInterface.class);

This generates the following exception since "reflections" is not instantiated and has the value of "null":

Attempt to invoke virtual method 'java.util.Set org.reflections.Reflections.getSubTypesOf(java.lang.Class)' on a null object reference

I understand that the generated .xml file resides on the computer where the build is happening, and I am not sure if this is also transferred to the android device so my guess is that is why this fails. But at what point does my Java code have access to this file before the apk is transferred and run on my android device?

I have tried googling this in many different ways from different angles but I cannot seem to find a solution to make reflections work in Android. I understand the principle explained here and it seems better to generate the information in an xml file at build time to have the class information available at runtime. But how can I set this up properly?

Thank you

There's a little bit of a chicken-or-egg problem to solve here

  1. You want Reflections API to access the classes compiled from src/main/java
  2. Gradle tasks and the Reflections classes are loaded by Gradle's buildscript classloader
  3. The classes in src/main/java are compiled after the buildscript classloader is defined

You'll need to introduce another classloader that can access the compiled classes to break the cyclic dependency. This can then be passed to Reflections. Eg:

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)
        ...
    }
}

See here for a similar question

reflections works on Android? · Issue #127 · ronmamo/reflections , How to integrate the Reflections library in android studio using gradle with save and collect. Question. My question is related to the Reflections library by  If you have selected JAR, the library is ready for use. If you have selected AAR: To follow integration notice, it’s recommended to put the project in “Project” view (dropdown list at the top left), Android Studio is in “Android” view by default when the project is created.

This is what I did: The task was to use Reflections on Android for classes provided with a dependency (i.e. inside a JAR file). This solution works for me:

top build.gradle:

dependencies {
    classpath 'org.reflections:reflections:0.9.10'
}

project 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())
        }
    }
}

Java code on Android:

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);

manosbatsis/gradle-reflections-plugin: A Gradle plugin that , Hi guys, I wanted to know if this library works on Android. @ronmamo Considering this is still open, is there any specific gradle config that can be used to generate the classes at compile time using Reflections().save(). Reflections reflections = Reflections.collect(); Set<Class<? extends MyInterface>>  android-gradle-plugin. Score 6. 2 How to integrate the Reflections library in android studio using gradle with save and collect Jan 31 '19.

I have been trying to use Reflections in Android for some days and this is what I have achieved so far. I have created a task in project's 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())
    }
}

Later on a class from the project I instantiate Reflections just as is done in the GitHub's examples (I use Kotlin):

val reflections = Reflections.collect(
                        "META-INF/reflections",
                        FilterBuilder().include(".*-reflections.json"),
                        JsonSerializer()
                ) 

If myTask is run on the Terminal the build is successful but I get this message "given scan urls are empty. set urls in the configuration", I searched for this in Google but didn't find anything helpful.

I tried different ways of configuring Reflections on the gradle file but when I collect them I always receive a null instance.

I hope my answer is of some use for someone.

Improve code inspection with annotations, A Gradle plugin that uses Reflections to scan and index your project classes at build-time, allowing run-time index in your jar, then utilise the embedded index at ruintime using Reflections.collect(). The build has some unit and integration tests. gradle gradle-plugin gradle-java reflections reflection reflection-library  Create a new project on Android Studio called myApp. For the purposes of this guide, you can start with an Empty Activity. linkIntegrate PDFTron SDK into your Android application. Open the build.gradle file in your project's root directory: Edit it to include the following inside the allprojects.repositories section to include PDFTron's Maven

Create an Android library, To add the Support Anotations library to your project, include the following line in the dependencies block of your build.gradle file: If you use annotations in your own library module, the annotations are The @Size annotation checks the size of a collection or array, as well as the length of a string. Questions: I use Visual Studio 2019 for mobile application development. I use Android Emulator for a long time but I have some issues with it only during the last month. I have created some virtual de

Build local unit tests, Save the file and click File > Sync Project with Gradle Files. That's it. To use your Android library's code in another app module, proceed as follows: Add the  The Gradle build system in Android Studio makes it easy to include external binaries or other library modules to your build as dependencies. The dependencies can be located on your machine or in a remote repository, and any transitive dependencies they declare are automatically included as well.

Building Multiplatform Projects with Gradle, Save to shared storage In your Android Studio project, you must store the source files for local In your app's top-level build.gradle file, specify the following libraries as of the following classes without needing to use mocks or reflection: Include the Mockito library dependency in your build.gradle file,  The first step to integrate with Google Play Billing is to add the library to your app and initialize a connection. Add a dependency on Google Play Billing Note: If you've followed the Getting ready guide, then you've already added the necessary dependencies and can move on to the next section.

Comments
  • Thank you very much! Unfortunately it is not letting me upvote you as I am below 15 points but I have accepted the answer.
  • The static Reflections.collect(...) method assumes that the target class is loaded by the same classloader as Reflections itself which is NOT the case here. You'll need to use the non-static Reflections methods on a Reflections instance which is aware of the custom classloader. Or maybe there are some static methods which also accept a classloader?