Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reflections works on Android? #127

Open
avurro opened this issue Mar 11, 2016 · 19 comments
Open

reflections works on Android? #127

avurro opened this issue Mar 11, 2016 · 19 comments

Comments

@avurro
Copy link

avurro commented Mar 11, 2016

Hi guys,

I wanted to know if this library works on Android.

Thank you in advance

@christophetd
Copy link

same question here

@xandi1987
Copy link

Hy,
I tried to use in in my app. Here is the code:


public List<String> getAllInterfaces() throws IllegalAccessException, InstantiationException {
    List<String> retVal = new ArrayList<>();
    Reflections reflections = new Reflections("Engines");
    Set<Class<? extends AbstractEngine>> allEngines = reflections.getSubTypesOf(AbstractEngine.class);
    for(Class<? extends AbstractEngine> c : allEngines){            
          AbstractEngine t =   c.newInstance();              
          retVal.add(t.getProviderName());            
    }
    return retVal;
}

Unfortunately it didn't work. When I test it in my unit-tests it works. Do you have any idea why it doesn't work?

@christophetd
Copy link

I made a few tests and couldn't get it working. My assumption is that it cannot work on Android due to the way classes are loaded, which is different than on a standard JVM setup.

@xandi1987
Copy link

Do you know any workarounds?

@christophetd
Copy link

christophetd commented May 16, 2016

I switched to using Gradle at build time to do what I needed, but I didn't find any workaround to use reflections. Would be interested if someone finds one, though.

@ronmamo
Copy link
Owner

ronmamo commented May 17, 2016

@christophetd , not sure if that what you've ment, but one solution/workaround is to generate xml on build time, then let Reflections collect it on bootstrap. see here

@zackpollard
Copy link

@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(). My current way doesn't work and i'm struggling to get it to play ball, any chance of some pointers/example gradle configuration?

@joe15000
Copy link

joe15000 commented Jul 16, 2017

@ronmamo, thank you for the Reflections library. I have tried to integrate it into my Android project using several sources of information and I am running into the same problem. I also have to admit I am not a gradle (or maven) expert so this may be more difficult for me to understand.

Your answer on generating a xml file on build and letting Reflections collecting it on "bootstrap" seems to be the way to go, however I seem unable to find the way to actually do this. Your link explains the general possibility and the reflections-maven repository gives a maven example. With the help of stackoverflow, I have tried to convert the maven example to grade. But it does not actually work. What does "at boostrap" actually mean specifically as refering to project / java files? Where do I insert

Reflections reflections = Reflections.collect() ?

Does this need to go into a special file or can I use this at any point in my code (this is what I am currently doing)?

From several stackoverflow posts and your git readme files, I have come up with this for now:

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,
Joe

@almighty972
Copy link

almighty972 commented Feb 21, 2018

Anyone succeed in executing Reflections on Android ? (via Gradle or not)

I'm using the last version of Reflections library : 0.9.11

Here is what I added in my app/build.gradle in order to generate the json file:

afterEvaluate {
        android.applicationVariants.each { variant ->
            if (variant.productFlavors.find { it.name == 'a' } != null) {
                variant.javaCompiler.doLast {
                    def collection = files { variant.javaCompiler.destinationDir.listFiles() }
                    URL[] urls = collection.collect {
                        it.toURI().toURL()
                    }
                    ClassLoader classLoader = new URLClassLoader(urls, ClassLoader.systemClassLoader)
                    org.reflections.Configuration config = org.reflections.util.ConfigurationBuilder
                            .build("my.package") // my.package just for the example of course
                            .addClassLoader(classLoader)
                            .setUrls(urls)
                    org.reflections.Reflections reflections = new org.reflections.Reflections(config)
                    reflections.save("${variant.javaCompiler.destinationDir}/META-INF/reflections/usecases-reflections.json",
                            new org.reflections.serializers.JsonSerializer())
                }
            }
        }
    }

The json file is created into the ...app/build/intermediates/classes/a/debug/META-INF/reflections/ folder but doesn't contain the scanned classes.

Here is the generated json file content:

{
  "store": {
    "storeMap": {
      "SubTypesScanner": {},
      "TypeAnnotationsScanner": {}
    }
  }
}

With the default Scanner, it should work @ronmamo right ?

Thank you.

@almighty972
Copy link

Finally, I got it working (the gradle task part) but at runtime the Reflections library doesn't work, because of Android dex format, I guess.

@crunchy234
Copy link

Has anyone been able to get this working?

@ted-dev-42
Copy link

I am looking for something that can run on android, sad.

@NoahNyy
Copy link

NoahNyy commented Apr 13, 2019

+1

@Andavin
Copy link

Andavin commented Apr 26, 2019

My use case is not specific to Android. However, there is a plugin that can be used to generate the XML file at compile time: https://github.com/manosbatsis/gradle-reflections-plugin

I changed the default params in the build script via

import org.reflections.scanners.MethodAnnotationsScanner
import org.reflections.scanners.SubTypesScanner

reflectionsConfig.params = [ new SubTypesScanner(), new MethodAnnotationsScanner() ]

and also ended up having to not use the default Reflections.collect() since for some reason this entire project is just super inconsistent for me. Instead I manually parsed the file via

// Use protected constructor via a reflection utility
Reflection reflections = newInstance(findConstructor(Reflections.class));
XmlSerializer serializer = new XmlSerializer();
URL resource = this.getClassLoader().getResource("META-INF/reflections/ProjectName-reflections.xml");
checkNotNull(resource);
try (InputStream inputStream = resource.openConnection().getInputStream()) {
    reflections.merge(serializer.read(inputStream));
} catch (IOException e) {
    Logger.severe(e);
}

Not sure if this will help anyone, but perhaps.

@nabellows
Copy link

My use case is not specific to Android. However, there is a plugin that can be used to generate the XML file at compile time: https://github.com/manosbatsis/gradle-reflections-plugin

I changed the default params in the build script via

import org.reflections.scanners.MethodAnnotationsScanner
import org.reflections.scanners.SubTypesScanner

reflectionsConfig.params = [ new SubTypesScanner(), new MethodAnnotationsScanner() ]

and also ended up having to not use the default Reflections.collect() since for some reason this entire project is just super inconsistent for me. Instead I manually parsed the file via

// Use protected constructor via a reflection utility
Reflection reflections = newInstance(findConstructor(Reflections.class));
XmlSerializer serializer = new XmlSerializer();
URL resource = this.getClassLoader().getResource("META-INF/reflections/ProjectName-reflections.xml");
checkNotNull(resource);
try (InputStream inputStream = resource.openConnection().getInputStream()) {
    reflections.merge(serializer.read(inputStream));
} catch (IOException e) {
    Logger.severe(e);
}

Not sure if this will help anyone, but perhaps.

How did you get the gradle plugin working with the android plugin? Android plugin is not compatible with the java plugin, so you can't access the 'classes' and 'jar' tasks.

@manosbatsis
Copy link

Sadly i have no experience in android builds. That said, any contribution to manosbatsis/gradle-reflections-plugin#5 is welcome if it can be useful.

@Heath123
Copy link

It works if you downgrade to 0.9.11

@Dev-AliGhasemi
Copy link

It works if you downgrade to 0.9.11

Are u sure ?
Is it work in android ?

@Dev-AliGhasemi
Copy link

version 0.9.11 compiled fine !
but can not fetch classes with specific annotation .
this is my code :

package com.jira;
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Reflections reflections = new Reflections("com.jira", new SubTypesScanner(false), new TypeAnnotationsScanner());
    Log.e("anot", reflections.getTypesAnnotatedWith(Test.class).size() + "");
}

}
`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests