5

我以前使用反射库在 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”列表返回空。

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

编辑 我为整个工作添加的罐子是:

  • 反射-0.9.9-RC1-uberjar.jar
  • javassist-3.12.1.GA.jar
  • 番石榴-14.0.1.jar
4

5 回答 5

2

也许你可以试试这个:

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();
于 2015-06-27T10:54:28.723 回答
1

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

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

于 2013-08-22T10:36:50.430 回答
0

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

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

(你可能需要剪掉尾部"!/classes.dex",现在不能检查)。然后,您可以遍历 classes.dex 内容并尝试加载您以这种方式找到的所有类。( Class.forName),最后,检查您需要的任何类属性。但是请记住,它classes.dex包含所有类,并且“all”可以是“相当多”。在处理 .dex 文件时,还需要记住其他性能问题。但是,您应该没问题,只要您不尝试一些不必要的低效操作。尤其是在我记得的时候,Reflections 以类似的方式实现了它的魔力。

于 2013-08-28T13:37:53.977 回答
0

这是一个也适用于接口的 Kotlin 解决方案。您需要的是一个真实的Context实例,因此请注意您无法在单元测试中使用这种方法。请注意,由于 API 脆弱且没有有用的替代品,DexFile因此不推荐使用。

private inline fun <reified T> getAllSubclasses(
    startsWithPackage: String,
    context: Context,
): Set<Class<out T>> {
    val classNames = DexFile(context.packageCodePath).entries()
    val classLoader = context.classLoader

    val superclass = T::class.java

    val result = mutableSetOf<Class<out T>>()

    classNames.iterator().forEach { className ->
        if (className.startsWith(startsWithPackage) && className.contains("$").not()) {
            val loadedClass = classLoader.loadClass(className)
            if (loadedClass.superclass == superclass || loadedClass.interfaces.contains(superclass)) {
                @Suppress("UNCHECKED_CAST")
                result.add(loadedClass as Class<out T>)
            }
        }
    }

    return result
}

上面的方法可以这样使用。

val subclasses = getAllSubclasses<SuperClass>("com.example", context)
于 2021-12-07T10:13:54.030 回答
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;
    }
}
于 2018-03-26T13:22:31.393 回答