在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。 动态扫描效率更高。
- java.lang.NullPointerException:尝试调用虚方法’java.lang.String android.net.Uri.getScheme()’
- 如何初始化Keystore
- java中的DateFormat转换问题?
- 在Eclipse上创建Android模块化应用程序
- 将Admob添加到libgdx
- UsbRequest.queue崩溃Android 3.1应用程序
- AlarmManager setInexactRepeating,setWindow,setRepeating方法在工作日内从循环内调用时不会触发警报
- 避免索引超出范围的exception
- 使用Jsoup解析Html时出错