Вопрос по dalvik, reflection, spring, java, android – Реализация Spring-подобного сканирования пакетов в Android

4

Я пытаюсь реализовать функцию сканирования пакетов, аналогичную Springcomponent-scanдля платформы Android, которую я разрабатываю. По сути, я хотел бы иметь возможность указать базовый пакет, например,com.foo.bar и получить всеClass экземпляры, которые имеют конкретную аннотацию. Я не хочу регистрировать каждый компонент в моей платформе, так как это противоречит цели автоматического сканирования.

Основываясь на моих исследованиях, кажется, что с помощью Java невозможно получить ресурсы, используя имя пакета, используя отражение. Тем не менее, я кратко изучилРазмышления рамкии мне интересно, существует ли совместимый с Android эквивалент. Если нет, возможно, есть немного менее очевидный способ выполнить то, что я хочу сделать.

Я немного заглянул в источник Spring, чтобы увидеть, как они достигли этого, но я не думаю, что то, что они делают, сработает в среде выполнения Dalvik.

Update

В настоящее время приведенный ниже код является лучшим, что я могу сделать для извлечения всех классов, которые содержат конкретную аннотацию, но, честно говоря, это довольно плохое решение. Это делает некоторые действительно небезопасные предположения о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;
}

Ваш Ответ

5   ответов
0

ClassPathScanner, Он использует его для поиска тестовых случаев на пути к классам.

Спасибо, Джесси. Похоже, это действительно хорошее решение, но кажется, чтоjava.class.path системное свойство просто возвращается.поэтому я не могу создать файл dex из этого. Что я не получаю? Tyler Treat
3

стараюсь избегать обычных функций веб-приложений, которые приводят к длительному запуску приложений. Я не использую отражение или сканирование пакетов.

Но если вы хотите .... если я правильно понимаю, вы хотите иметь аннотацию для класса. Вместо использования аннотаций вы также можете использовать маркерные интерфейсы (чтобы иметь больше возможностей).

1) Look at Annotation: Java custom annotation and dynamic loading
Has an implementation in the question which just answers your question. Annotation: Scanning Java annotations at runtime Interface: Find Java classes implementing an interface Interface: Is something similar to ServiceLoader in Java 1.5? Interface: How can I get a list of all the implementations of an interface programmatically in Java? Interface: Since the approach is expensive, maybe the ServiceLoader is a compromise between execution time and comfort, since it loads only the classes given in the services file. On the other hand if only classes with a certain interface are in your package then the ServiceLoader isn't that faster. 2) AndroidAnnotations

Я бы предпочел способAndroidAnnotations работать (возможно, интеграция в AndroidAnnotations является предпочтительным способом): он автоматически добавляет дополнительный этап компиляции, который генерирует исходный код, используя стандартный инструмент обработки аннотаций Java. Поэтому вместо сканирования во время выполнения вы выполняете код на основе аннотаций, сгенерированных во время компиляции.

Я думаю, что аннотация Bean / EBean может работать для вас (только один класс):https://github.com/excilys/androidannotations/wiki/Enhance%20custom%20classes

Функция сканирования недоступна, см. Этонить

3) Writing your own annotation processor See APT (Annotation Processing Tool). The idea would be to generate a static function which returns a list of classes which are annotated, so that no class scanning is needed. A very good ressource is http://javadude.com/articles/annotations/index.html
И первая ссылка помогла вам?
ClassIndex library реализует 3-й пункт, и я могу подтвердить, что он работает с Android.
По общему признанию, я не очень знаком с тем, как работает генерация исходного кода во время компиляции. Мне придется взглянуть на то, как AndroidAnnotations это делает, но я боюсь, что переключение моей среды на генерацию кода во время компиляции будет большой работой, хотя сокращение накладных расходов было бы неплохо. Tyler Treat
0

генерируя данные / код для инъекций. Это может быть перенесено на Dalvik. Это еще более эффективно, чем динамическое сканирование.

0

EDIT:

я нашелЭта проблема в трекере проблем Android. Похоже, чтоClassLoader.getResource(String) «работает как положено» в том смысле, что он возвращаетnull, Это ожидается, потому что DalvikVM не хранит ресурсы после компиляции. В этом выпуске перечислены обходные пути, но может быть и другой способ получить доступ к нужным вам классам.

ИспользоватьPackageManager ухватиться за экземплярApplicationInfo. ApplicationInfo имеет публичное поле под названиемsourceDir который является полным путем (String) к месту расположения исходного каталога для этого приложения. СоздатьFile из этогоStringи вы должны быть в состоянии перейти к вашему пакету в исходном каталоге. Оказавшись там, вы можете использовать метод из моего первоначального ответа, чтобы найти классы, которые вы ищете.

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

/EDIT

Вы должны быть в состоянии использоватьClassLoader.getResource(String) чтобы получитьURL в ваш конкретный пакет (переданный вString имя пакета, которое вас интересует, разделенное путями, а не точками). С этимURL Вы можете позвонитьgetFile(), из которого вы можете создать JavaFile в папку пакета. ВызовpackageFile.listFiles() оттуда, и у вас есть свои классы / подпакеты.

Будьте рекурсивны с подпакетами, и с классами найдитеClass объект с использованием статическогоClass.forName(String) метод.

Это исключительно нативный подход, который я использовал для этого в приложении Java. Я предполагал, что это будет работать в Android, поскольку нет никакой зависимости от внешних библиотек.
@Tyler Проверьте, позволяет ли мое редактирование сделать это.
Я боюсь, что это не сработает, так как «исходный каталог» на самом деле просто APK. Мне все еще кажется, что мой лучший вариант - загрузить APK какDexFile и переберите все записи своего класса, как я описал в обновленном вопросе. Это работает, но мне это не нравится, и это довольно медленно. Tyler Treat
Понимаю. К сожалению, в Android классы скомпилированы вclasses.dex файл, так что похоже, что это не вариант. Я тоже очень на это надеялся! Tyler Treat
Вы уверены, что можете получить доступ к файлам Dex, как это?ClassLoader.getResource('com/foo') возвращаетсяnull и на основании некоторого быстрого поиска это кажется чем-то, что на самом деле не возможно. В противном случае это было бы отличным решением. Tyler Treat
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();
        }
    }
}

Похожие вопросы