【问题标题】:Implementing Spring-like package scanning in Android在 Android 中实现类似 Spring 的包扫描
【发布时间】:2012-07-10 20:06:21
【问题描述】:

我正在尝试为我正在开发的 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;
}

【问题讨论】:

    标签: java android spring reflection dalvik


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      我同意 Joop Eggen 的观点,并认为他的方法很好。在 Android 中,我尽量避免导致应用程序长时间启动的常见网络应用程序功能。我不使用反射或包扫描。

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

      1) 看看

      2) 安卓注解

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

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

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

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

      【讨论】:

      • 诚然,我对编译时源代码生成的工作原理不是很熟悉。我将不得不看看 AndroidAnnotations 是如何做到的,但我担心将我的框架切换到编译时代码生成会做很多工作,尽管降低性能开销会很好。
      • 第一个链接对您有帮助吗?
      • ClassIndex library 实现第三点,我可以确认它适用于 Android。
      【解决方案3】:

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

      【讨论】:

      • 谢谢,杰西。这似乎是一个非常好的解决方案,但似乎java.class.path 系统属性只返回.,所以我无法从中创建 dex 文件。我没有得到什么?
      【解决方案4】:

      编辑:

      我在 Android 问题跟踪器中找到了this issueClassLoader.getResource(String) 似乎“按预期工作”,因为它返回 null。这是意料之中的,因为 DalvikVM 在编译后不会保留资源。问题中列出了解决方法,但可能还有其他方法可以访问您想要的类。

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

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

      /EDIT

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

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

      【讨论】:

      • 你确定你可以像这样访问 dex 文件吗? ClassLoader.getResource('com/foo') 正在返回 null 并且基于一些快速搜索,这似乎实际上是不可能的。否则这将是一个很好的解决方案。
      • 这是我在 Java 应用程序中用来执行此操作的专有本地方法。我认为它可以在 Android 中运行,因为它不依赖于外部库。
      • 我明白了。不幸的是,在 Android 中,这些类被编译成一个 classes.dex 文件,所以看起来这不是一个选项。我也对它寄予厚望!
      • 恐怕这也行不通,因为“源目录”实际上只是 APK。似乎我最好的选择仍然是将 APK 作为DexFile 加载并迭代其所有类条目,就像我在更新的问题中描述的那样。它有效,但我不喜欢它,而且速度很慢。
      【解决方案5】:

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

      【讨论】:

        猜你喜欢
        • 2021-03-24
        • 1970-01-01
        • 2015-07-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-10
        • 1970-01-01
        • 2017-06-04
        相关资源
        最近更新 更多