我正在尝试为我正在开发的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;
}
答案 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应用程序功能,这会导致应用程序启动持久。我不使用反射或包扫描。
但是如果你想....如果我理解正确你想要一个类的注释。除了使用注释,您还可以使用标记接口(只有更多的可能性)。
我更喜欢AndroidAnnotations的工作方式(可能是AndroidAnnotations中的集成是更好的方式):它会自动添加一个额外的编译步骤,使用标准的Java Annotation Processing Tool生成源代码。因此,不是运行时扫描,而是根据编译期间生成的注释执行代码。
我认为Bean / EBean注释可以为你工作(只有单个类):https://github.com/excilys/androidannotations/wiki/Enhance%20custom%20classes
扫描功能不可用,请参阅此thread
答案 2 :(得分:0)
修改强> 的
我在Android问题跟踪器中找到了this issue。 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.forName(String)
方法找到Class
对象。
答案 3 :(得分:0)
看看Vogar的ClassPathScanner。它使用它在类路径上查找测试用例。
答案 4 :(得分:0)
在您的java构建过程中合并类路径扫描,生成注入数据/代码。然后这也可以移植到Dalvik。动态扫描效率更高。