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

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

根据我的研究,似乎Java无法使用reflection检索包名称的资源。 但是,我简要地研究了Reflections框架 ,我想知道是否有与Android兼容的等价物。 如果没有,或许有一种稍微不那么明显的方式来实现我想做的事情。

我查看了Spring源代码,看看他们是如何实现这一目标的,但我不认为他们在做什么会在Dalvik运行时内工作。

更新

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

 public Set<Class> getClassesWithAnnotation(Class 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 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; } 

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

但是如果你想…如果我理解正确你想要一个类的注释。 您也可以使用标记界面(只有更多的可能性),而不是使用注释。

1)看看

  • 注释: Java自定义注释和动态加载
    在问题中有一个实现,只是回答你的问题。
  • 注释: 在运行时扫描Java注释
  • 接口: 查找实现接口的Java类
  • 接口: 类似于Java 1.5中的ServiceLoader吗?
  • 接口: 如何在Java中以编程方式获取接口的所有实现的列表?
  • 接口:由于方法很昂贵,因此ServiceLoader可能是执行时间和舒适度之间的折衷,因为它只加载services文件中给出的类。 另一方面,如果只有具有特定接口的类在您的包中,那么ServiceLoader就不那么快了。

2)AndroidAnnotations

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

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

扫描function不可用,请参阅此主题

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

  • 请参阅APT(注释处理工具) 。 我们的想法是生成一个静态函数,该函数返回一个注释类的列表,这样就不需要进行类扫描。
  • 一个非常好的资源是http://javadude.com/articles/annotations/index.html

我想在运行时找到所有子类。 所以我一直在寻找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 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(); } } } 

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

编辑:

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

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

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

/编辑

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

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

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