Why are annotations under Android such a performance issue (slow)?
Google has acknowledged the issue and fixed it "post-Honeycomb"
So at least they know about it and have supposedly fixed it for some future version.
Here's a generic version of Gray's & user931366's idea:
public class AnnotationElementsReader { private static Field elementsField; private static Field nameField; private static Method validateValueMethod; public static HashMap<String, Object> getElements(Annotation annotation) throws Exception { HashMap<String, Object> map = new HashMap<String, Object>(); InvocationHandler handler = Proxy.getInvocationHandler(annotation); if (elementsField == null) { elementsField = handler.getClass().getDeclaredField("elements"); elementsField.setAccessible(true); } Object[] annotationMembers = (Object[]) elementsField.get(handler); for (Object annotationMember : annotationMembers) { if (nameField == null) { Class<?> cl = annotationMember.getClass(); nameField = cl.getDeclaredField("name"); nameField.setAccessible(true); validateValueMethod = cl.getDeclaredMethod("validateValue"); validateValueMethod.setAccessible(true); } String name = (String) nameField.get(annotationMember); Object val = validateValueMethod.invoke(annotationMember); map.put(name, val); } return map; }}
I've benchmarked an annotation with 4 elements.
Millisecond times for 10000 iterations of either getting values of all of them or calling the method above:
Device Default HackHTC Desire 2.3.7 11094 730Emulator 4.0.4 3157 528Galaxy Nexus 4.3 1248 392
Here's how I've integrated it into DroidParts: https://github.com/yanchenko/droidparts/commit/93fd1a1d6c76c2f4abf185f92c5c59e285f8bc69.
To follow up on this, there's still a problem here when calling methods on annotations. The bug listed above by candrews fixes the getAnnotation() slowness, but calling a method on the annotation is still a problem due to the Method.equals() issues.
Couldn't find a bug report for Method.equals() so I created one here:https://code.google.com/p/android/issues/detail?id=37380
Edit:So my work around for this (thanks for the ideas @Gray), is actually pretty simple.(this is trunkcated code, some caching and such is omitted)
annotationFactory = Class.forName("org.apache.harmony.lang.annotation.AnnotationFactory");getElementDesc = annotationFactory.getMethod("getElementsDescription", Class.class);Object[] members = (Object[])getElementDesc.invoke(annotationFactory, clz); // these are AnnotationMember[]Object element = null;for (Object e:members){ // AnnotationMembers Field f = e.getClass().getDeclaredField("name"); f.setAccessible(true); String fname = (String) f.get(e); if (methodName.equals(fname)){ element = e; break; }}if (element == null) throw new Exception("Element was not found");Method m = element.getClass().getMethod("validateValue");return m.invoke(element, args);
You mileage will vary based on use, but in may case this was about 15-20 times faster then doing it the "right way"