4

component-scan我正在尝试为我正在开发的 Android 框架实现类似于 Spring 的包扫描功能。基本上,我希望能够指定一个基本包,例如com.foo.bar并检索所有Class具有特定注释的实例。我不想在我的框架中注册每个组件,因为这会破坏自动扫描的目的。

根据我的研究,Java 似乎无法使用反射检索给定包名称的资源。但是,我简要研究了反射框架,我想知道是否有与 Android 兼容的等价物。如果没有,也许有一种不太明显的方法来完成我想做的事情。

我稍微研究了 Spring 源代码,看看他们是如何实现这一点的,但我认为他们所做的事情不会在 Dalvik 运行时中起作用。

更新

目前,下面的代码是我能做的最好的检索所有包含特定注释的类,但坦率地说,这是一个非常糟糕的解决方案。ClassLoader它对扫描(和加载)所有应用程序类的加号做了一些非常不安全的假设。

public Set<Class<?>> getClassesWithAnnotation(Class<? extends Annotation> annotation) {
    Set<Class<?>> classes = new HashSet<Class<?>>();
    Field dexField = PathClassLoader.class.getDeclaredField("mDexs");
    dexField.setAccessible(true);
    PathClassLoader classLoader = (PathClassLoader) Thread.currentThread().getContextClassLoader();
    DexFile[] dexs = (DexFile[]) dexField.get(classLoader);
    for (DexFile dex : dexs) {
        Enumeration<String> entries = dex.entries();
        while (entries.hasMoreElements()) {
            String entry = entries.nextElement();
            Class<?> entryClass = dex.loadClass(entry, classLoader);
            if (entryClass != null && entryClass.isAnnotationPresent(annotation)) {
                classes.add(entryClass);
            }
        }
    }
    return classes;
}
4

5 回答 5

4

我想在运行时找到所有子类。所以我一直在寻找android类扫描。这是我在 web.xml 中收集到的最终代码。你会明白的。

public static void findSubClasses(Context context, Class parent) {
    ApplicationInfo ai = context.getApplicationInfo();
    String classPath = ai.sourceDir;
    DexFile dex = null;
    try {
        dex = new DexFile(classPath);
        Enumeration<String> apkClassNames = dex.entries();
        while (apkClassNames.hasMoreElements()) {
            String className = apkClassNames.nextElement();
            try {
                Class c = context.getClassLoader().loadClass(className);
                if (parent.isAssignableFrom(c)) {
                    android.util.Log.i("nora", className);
                }
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //              android.util.Log.i("nora", className);
        }
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } finally {
        try {
            dex.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
于 2012-11-01T10:17:57.380 回答
3

我同意 Joop Eggen 的观点,并发现他的方法很好。在 Android 中,我尽量避免导致应用程序长时间启动的常见网络应用程序功能。我不使用反射或包扫描。

但是,如果您想....如果我理解正确,您希望为班级添加注释。除了使用注释,您还可以使用标记接口(只是有更多的可能性)。

1)看

2)Android注解

我更喜欢AndroidAnnotations的工作方式(也许 AndroidAnnotations 中的集成是更可取的方式):它使用标准的 Java Annotation Processing Tool 自动添加一个额外的编译步骤来生成源代码。因此,不是运行时扫描,而是基于编译时生成的注释执行代码。

我认为 Bean/EBean 注释可以为你工作(只有一个类):https ://github.com/excilys/androidannotations/wiki/Enhance%20custom%20classes

扫描功能不可用,请参阅此线程

3)编写自己的注释处理器

于 2012-07-13T06:42:13.190 回答
0

编辑:

我在 Android 问题跟踪器中发现了这个问题。似乎ClassLoader.getResource(String)“按预期工作”,因为它返回null. 这是意料之中的,因为 DalvikVM 在编译后不会保留资源。问题中列出了解决方法,但可能还有另一种方法可以访问您想要的类。

使用PackageManager获取 的实例ApplicationInfoApplicationInfo有一个名为的公共字段,它是该应用程序源目录位置的sourceDir完整路径 (a )。从此String创建一个,您应该能够导航到源目录中的包。在那里,您可以使用我原始答案中的方法来查找您正在寻找的课程。FileString

String applicationSourceDir = 
    getPackageManager().getApplicationInfo(androidPackageName, 0).sourceDir;

/编辑

您应该能够使用ClassLoader.getResource(String)来获取URL您的特定包(传入的String是您感兴趣的包名称,由路径分隔符而不是句点分隔)。有了这个URL,您可以调用getFile(),您可以从中创建一个 JavaFile到包文件夹。从那里打电话packageFile.listFiles(),你就有了你的类/子包。

对子包进行递归,对类Class使用静态Class.forName(String)方法查找对象。

于 2012-07-10T20:18:14.193 回答
0

看看 Vogar 的ClassPathScanner。它使用它来查找类路径上的测试用例。

于 2012-07-11T12:10:56.300 回答
0

在您的 java 构建过程中合并类路径扫描,生成注入数据/代码。然后这也可以移植到 Dalvik。它甚至比动态扫描更有效。

于 2012-07-12T21:03:55.127 回答