Найти все классы в пакете на Android

Как я могу найти все классы внутри пакета на Android? Я использую PathClassLoader, но он всегда возвращает пустое перечисление?

Дополнительная информация

Пробовал предложенный подход «Размышления». Несколько важных моментов в библиотеке отражений. Библиотека, доступная через центральный центр maven, несовместима с Android и дает ошибки dex. Мне пришлось включить источник и компилировать dom4j, java-assist.

Проблема с отражениями и мое исходное решение заключается в том, что PathClassLoader в android возвращает пустое перечисление для пакета.

Проблема с подходом заключается в том, что getResource в Android всегда возвращает пустое перечисление.

final String resourceName = aClass.getName().replace(".", "/") + ".class"; for (ClassLoader classLoader : loaders) { try { final URL url = classLoader.getResource(resourceName); if (url != null) { final String normalizedUrl = url.toExternalForm().substring(0, url.toExternalForm().lastIndexOf(aClass.getPackage().getName().replace(".", "/"))); return new URL(normalizedUrl); } } catch (MalformedURLException e) { e.printStackTrace(); } } 

Использование DexFile для перечисления всех классов в вашем apk:

  try { DexFile df = new DexFile(context.getPackageCodePath()); for (Enumeration<String> iter = df.entries(); iter.hasMoreElements();) { String s = iter.nextElement(); } } catch (IOException e) { e.printStackTrace(); } 

Остальные используют regexp или что-то еще, чтобы отфильтровать ваш желаемый класс.

На самом деле я нашел решение. Спасибо за ответ tao .

 private String[] getClassesOfPackage(String packageName) { ArrayList<String> classes = new ArrayList<String>(); try { String packageCodePath = getPackageCodePath(); DexFile df = new DexFile(packageCodePath); for (Enumeration<String> iter = df.entries(); iter.hasMoreElements(); ) { String className = iter.nextElement(); if (className.contains(packageName)) { classes.add(className.substring(className.lastIndexOf(".") + 1, className.length())); } } } catch (IOException e) { e.printStackTrace(); } return classes.toArray(new String[classes.size()]); } 

Протестировано на Android 5.0 Lolipop

попробуй это..

  Reflections reflections = new Reflections("my.project.prefix"); Set<Class<? extends Object>> allClasses = reflections.getSubTypesOf(Object.class); 

Вот решение, которое приносит вам не только имя класса,
Но и объект Class<?> .

И хотя PathClassLoader.class.getDeclaredField("mDexs") часто не работает,
new DexFile(getContext().getPackageCodePath()) выглядит намного более стабильным.

 public abstract class ClassScanner { private static final String TAG = "ClassScanner"; private Context mContext; public ClassScanner(Context context) { mContext = context; } public Context getContext() { return mContext; } void scan() throws IOException, ClassNotFoundException, NoSuchMethodException { long timeBegin = System.currentTimeMillis(); PathClassLoader classLoader = (PathClassLoader) getContext().getClassLoader(); //PathClassLoader classLoader = (PathClassLoader) Thread.currentThread().getContextClassLoader();//This also works good DexFile dexFile = new DexFile(getContext().getPackageCodePath()); Enumeration<String> classNames = dexFile.entries(); while (classNames.hasMoreElements()) { String className = classNames.nextElement(); if (isTargetClassName(className)) { //Class<?> aClass = Class.forName(className);//java.lang.ExceptionInInitializerError //Class<?> aClass = Class.forName(className, false, classLoader);//tested on 魅蓝Note(M463C)_Android4.4.4 and Mi2s_Android5.1.1 Class<?> aClass = classLoader.loadClass(className);//tested on 魅蓝Note(M463C)_Android4.4.4 and Mi2s_Android5.1.1 if (isTargetClass(aClass)) { onScanResult(aClass); } } } long timeEnd = System.currentTimeMillis(); long timeElapsed = timeEnd - timeBegin; Log.d(TAG, "scan() cost " + timeElapsed + "ms"); } protected abstract boolean isTargetClassName(String className); protected abstract boolean isTargetClass(Class clazz); protected abstract void onScanResult(Class clazz); } 

И это пример использования:

 new ClassScanner(context) { @Override protected boolean isTargetClassName(String className) { return className.startsWith(getContext().getPackageName())//I want classes under my package && !className.contains("$");//I don't need none-static inner classes } @Override protected boolean isTargetClass(Class clazz) { return AbsFactory.class.isAssignableFrom(clazz)//I want subclasses of AbsFactory && !Modifier.isAbstract(clazz.getModifiers());//I don't want abstract classes } @Override protected void onScanResult(Class clazz) { Constructor constructor = null; try { constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); constructor.newInstance(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }.scan(); 

Я нашел то же решение Сергея Шустикова, с именем пакета, полученным из контекста и обрабатывающим внутренние классы. К несчастью, пока он работает на Galaxy Nexus, он не работает на Nexus 6 и Nexus 6p (не тестируется на других устройствах).

Вот код:

 private HashMap<String, String> loadClassFullnames() { final HashMap<String, String> ret = new HashMap<>(); try { final String thisPackage = context.getPackageName(); final DexFile tmp = new DexFile(context.getPackageCodePath()); for (Enumeration<String> iter = tmp.entries(); iter.hasMoreElements(); ) { final String classFullname = iter.nextElement(); if (classFullname.startsWith(thisPackage)) { // TODO filter out also anonymous classes (es Class1$51) final int index = classFullname.lastIndexOf("."); final String c = (index >= 0) ? classFullname.substring(index + 1) : classFullname; ret.put(c.replace('$', '.'), classFullname); } } } catch (Throwable ex) { Debug.w("Unable to collect class fullnames. Reason: " + ex.getCause() + "\n\rStack Trace:\n\r" + ((ex.getCause() != null)? ex.getCause().getStackTrace(): "none")); } return ret; } 

Он швыряет на Galaxy Nexus, DexFile содержит также пакеты приложений (тот, который мы пытаемся найти!), В то время как на Nexus 6 [p] нет пакета от соответствия thisPackage, а некоторые другие пакеты есть … Кто-нибудь Имеют такую ​​же проблему?

Это взято из http://mindtherobot.com/

Пример кода

Скажем, у нас есть аннотация под названием Foo и несколько классов, украшенных им:

 @Retention(RetentionPolicy.RUNTIME) public @interface Foo { String value(); } @Foo("person") public class Person { } @Foo("order") public class Order { } 

Вот простой класс, который охватывает все классы в вашем приложении во время выполнения, находит те, которые отмечены с помощью @Foo, и получают значение аннотации. Не копируйте и не вставляйте этот код в свое приложение – его нужно много исправлять и добавлять, как описано ниже в коде.

 public class ClasspathScanner { private static final String TAG = ClasspathScanner.class.getSimpleName(); private static Field dexField; static { try { dexField = PathClassLoader.class.getDeclaredField("mDexs"); dexField.setAccessible(true); } catch (Exception e) { // TODO (1): handle this case gracefully - nobody promised that this field will always be there Log.e(TAG, "Failed to get mDexs field"); } } public void run() { try { // TODO (2): check here - in theory, the class loader is not required to be a PathClassLoader PathClassLoader classLoader = (PathClassLoader) Thread.currentThread().getContextClassLoader(); DexFile[] dexs = (DexFile[]) dexField.get(classLoader); for (DexFile dex : dexs) { Enumeration<String> entries = dex.entries(); while (entries.hasMoreElements()) { // (3) Each entry is a class name, like "foo.bar.MyClass" String entry = entries.nextElement(); Log.d(TAG, "Entry: " + entry); // (4) Load the class Class<?> entryClass = dex.loadClass(entry, classLoader); if (entryClass != null) { Foo annotation = entryClass.getAnnotation(Foo.class); if (annotation != null) { Log.d(TAG, entry + ": " + annotation.value()); } } } } } catch (Exception e) { // TODO (5): more precise error handling Log.e(TAG, "Error", e); } } }