我试图实施一揽子扫描功能,类似于Spring的component-scan
,对于Android框架我开发。 基本上,我希望能够指定一个基础包,如com.foo.bar
和检索所有Class
具有特定注释实例。 我不希望有每个组件与我的框架中注册,因为这将击败自动扫描的目的。
根据我的研究,它似乎是不可能与Java给出了使用反射包名称检索资源。 不过,我简要地看着的思考框架 ,我想知道如果有一个Android的兼容等同。 如果不是这样,也许有来完成我想要做一个稍微不那么明显的方式。
我看着春源一点,看看他们是如何实现这一点,但我不认为他们在做什么,将在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;
}
我分享乔普埃根的意见,并找到他的做法是好的。 在Android中我尽量避免了通常的网络应用功能,从而导致持久的应用程序启动。 我不使用反射或包扫描。
但是,如果你想....如果我理解正确的话,你想拥有一类的注释。 相反,使用注释您也可以使用标记接口的(只是有更多的possibilites)。
1)看
- 注释: Java自定义注释和动态加载
在刚刚回答您的问题这个问题的实现。 - 注释: 扫描Java标注在运行时
- 接口方面: 查找Java类实现一个接口
- 接口: 类似的ServiceLoader东西在Java 1.5中?
- 接口: 我怎样才能在编程的Java接口的所有实现的列表?
- 接口方面:由于该方法是昂贵的,也许是的ServiceLoader的执行时间和舒适性之间的妥协,因为它加载只在服务文件中给出的类。 在另一方面,如果仅与某个接口类是在你的包那么的ServiceLoader是不是更快。
2)AndroidAnnotations
我喜欢的方式AndroidAnnotations工作(也许在AndroidAnnotations的融合是最好的方式):它会自动添加一个额外的编译步骤,生成的源代码,使用标准的Java注释处理工具。 因此,而不是运行时扫描基于在编译时产生的注解,你执行代码。
我认为豆/ EBean注释可以为你(只有单级)工作: https://github.com/excilys/androidannotations/wiki/Enhance%20custom%20classes
可扫描的功能不可用,请参阅本线
3)编写您自己的批注处理器
- 见APT(注释处理工具) 。 这个想法是产生它返回的这些注释类列表的静态函数,这样就不需要类扫描。
- 一个很好的ressource是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<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();
}
}
}
看看沃加尔的ClassPathScanner 。 它使用它找到的类路径上的测试用例。
编辑:
我发现这个问题在Android问题跟踪。 看来, ClassLoader.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
使用静态对象Class.forName(String)
方法。
在Java构建过程包括类路径扫描,产生喷射数据/代码。 这可能随后也移植到Dalvik的。 它甚至更高效,动态扫描。