查找给定类的所有子类(Android)

时间:2013-08-22 10:10:10

标签: java android reflection

我之前使用Reflections库来查找Java中给定类的所有子类。

这是我在简单java 项目中使用的代码段:

    Reflections reflections = new Reflections(PACKAGE_NAME);

    Set<Class<? extends SuperClass>> subTypes =
            reflections.getSubTypesOf(SuperClass.class);

    for (Class<? extends SuperClass> subType : subTypes) {

        log("Subclass = " + subType.getSimpleName());
    }

当我在 android 项目中运行相同的代码时,“subType”List返回空。

任何人都可以帮我在Android上完成这项工作吗?

修改 我为整个工作添加的罐子是:

  • 反射-0.9.9-RC1-uberjar.jar
  • 了Javassist-3.12.1.GA.jar
  • 番石榴14.0.1.jar

4 个答案:

答案 0 :(得分:1)

也许你可以试试这个:

public abstract class ClassScanner {

    private static final String TAG = "ClassScanner"; 
    private Context mContext;

    public ClassScanner(Context context) {
        mContext = context;
    }

    public Context getContext() {
        return mContext;
    }

    void scan() throws IOException, ClassNotFoundException, NoSuchMethodException {
        long timeBegin = System.currentTimeMillis();

        PathClassLoader classLoader = (PathClassLoader) getContext().getClassLoader();
        //PathClassLoader classLoader = (PathClassLoader) Thread.currentThread().getContextClassLoader();//This also works good
        DexFile dexFile = new DexFile(getContext().getPackageCodePath());
        Enumeration<String> classNames = dexFile.entries();
        while (classNames.hasMoreElements()) {
            String className = classNames.nextElement();
            if (isTargetClassName(className)) {
                //Class<?> aClass = Class.forName(className);//java.lang.ExceptionInInitializerError
                //Class<?> aClass = Class.forName(className, false, classLoader);//tested on 魅蓝Note(M463C)_Android4.4.4 and Mi2s_Android5.1.1
                Class<?> aClass = classLoader.loadClass(className);//tested on 魅蓝Note(M463C)_Android4.4.4 and Mi2s_Android5.1.1
                if (isTargetClass(aClass)) {
                    onScanResult(aClass);
                }
            }
        }

        long timeEnd = System.currentTimeMillis();
        long timeElapsed = timeEnd - timeBegin;
        Log.d(TAG, "scan() cost " + timeElapsed + "ms");
    }

    protected abstract boolean isTargetClassName(String className);

    protected abstract boolean isTargetClass(Class clazz);

    protected abstract void onScanResult(Class clazz);
}

这是一个如何使用的例子:

    new ClassScanner(context) {

    @Override
    protected boolean isTargetClassName(String className) {
        return className.startsWith(getContext().getPackageName())//I want classes under my package
                && !className.contains("$");//I don't need none-static inner classes
    }

    @Override
    protected boolean isTargetClass(Class clazz) {
        return AbsFactory.class.isAssignableFrom(clazz)//I want subclasses of AbsFactory
                && !Modifier.isAbstract(clazz.getModifiers());//I don't want abstract classes
    }

    @Override
    protected void onScanResult(Class clazz) {
        Constructor constructor = null;
        try {
            constructor = clazz.getDeclaredConstructor();
            constructor.setAccessible(true);
            constructor.newInstance();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}.scan();

答案 1 :(得分:0)

由于Android中使用的编译过程,这可能无效。使用Java编译器将源代码转换为.class文件。下一步将.class文件转换为Android .dex文件(Dalvik字节码),它们不太可能保留所有元数据。

http://developer.android.com/tools/building/index.html

答案 2 :(得分:0)

有一个处理dex文件的官方API。有关详细信息,请参阅here。基本上,给定.dex文件的路径,您可以枚举其中的所有文件。要获取classes.dex的网址,您可以使用

  Thread.currentThread().getContextClassLoader().getResource("classes.dex")

(您可能需要剪切尾随"!/classes.dex",现在无法检查)。然后,您可以迭代classes.dex内容并尝试加载您以这种方式找到的所有类。 (Class.forName),最后,检查您需要的任何类属性。但请注意,classes.dex包含所有类,而“all”可能“非常多”。处理.dex文件时还需要记住其他性能问题。但是,只要你不尝试一些不必要的低效率,你就应该没事。特别是因为我以类似的方式完成了它的魔力,就我而言。

答案 3 :(得分:0)

这是基于fantouch答案的单方法解决方案。
结果也将包含不正确的后代。

public class Utils
{
    // ...

    public static ArrayList<Class> GetSubClasses( Context context, String packageName, Class targetSuperClass )
    {
        ArrayList<Class> subClasses = new ArrayList<>();

        try
        {
            DexFile dex = new DexFile( context.getPackageCodePath() );
            for ( Enumeration<String> iterator = dex.entries(); iterator.hasMoreElements(); )
            {
                String className = iterator.nextElement();

                if ( !className.startsWith( packageName ) || className.contains("$") )
                {
                    continue;
                }

                Class classObj = Class.forName( className );

                Class superClass = classObj;
                while ( true )
                {
                    superClass = superClass.getSuperclass();

                    if ( superClass == null || superClass == Object.class )
                    {
                        break;
                    }

                    if ( superClass == targetSuperClass )
                    {
                        subClasses.add( classObj );
                        break;
                    }
                }
            }
        }
        catch ( Exception e )
        {
            e.printStackTrace();
        }

        return subClasses;
    }
}