在Android中实现类似Spring的包扫描

时间:2012-07-10 20:06:21

标签: java android spring reflection dalvik

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

根据我的研究,似乎Java无法使用反射检索包名称的资源。但是,我简要地查看了Reflections framework,我想知道是否有与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;
}

5 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:3)

我赞同Joop Eggen的意见,并认为他的方法很好。在Android中,我尝试避免通常的Web应用程序功能,这会导致应用程序启动持久。我不使用反射或包扫描。

但是如果你想....如果我理解正确你想要一个类的注释。除了使用注释,您还可以使用标记接口(只有更多的可能性)。

1)看看

2)AndroidAnnotations

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

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

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

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

答案 2 :(得分:0)

修改

我在Android问题跟踪器中找到了this issueClassLoader.getResource(String)似乎“按预期工作”,因为它返回null。这是预期的,因为DalvikVM在编译后不会保留资源。问题中列出了解决方法,但可能有另一种方法来访问所需的类。

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

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

/修改

您应该能够使用ClassLoader.getResource(String)获取特定包的URL(传递的String是您感兴趣的包名称,而不是路径分隔符。周期)。使用此URL,您可以调用getFile(),您可以从中创建Java File到包文件夹。从那里调用packageFile.listFiles(),你就有了你的课程/子包。

使用子包递归,并使用静态Class.forName(String)方法找到Class对象。

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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