diff options
| author | 2017-08-29 16:05:47 -0700 | |
|---|---|---|
| committer | 2017-08-31 17:35:13 +0000 | |
| commit | 271e32270b7a026f8746c24615c23b39ba48539b (patch) | |
| tree | eb039440ea49deaa04771c09abcd38c97ea4dfa5 | |
| parent | f8900f1a48323aaacafef342cc966c3305404abd (diff) | |
Changing RemoteViews to using MethodHandles instead of relection
Test: am instrument -w -e class android.widget.RemoteViewsTest com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
Change-Id: I1c1eb58d863e1196a38fadb15e08ad2fdcff1d4d
| -rw-r--r-- | core/java/android/widget/RemoteViews.java | 187 |
1 files changed, 90 insertions, 97 deletions
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 5adbdbe8ab4f..46742b8852ff 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -73,6 +73,9 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; @@ -188,17 +191,12 @@ public class RemoteViews implements Parcelable, Filter { private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler(); - private static final Object[] sMethodsLock = new Object[0]; - private static final ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>> sMethods = - new ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>>(); - private static final ArrayMap<Method, Method> sAsyncMethods = new ArrayMap<>(); + private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>(); - private static final ThreadLocal<Object[]> sInvokeArgsTls = new ThreadLocal<Object[]>() { - @Override - protected Object[] initialValue() { - return new Object[1]; - } - }; + /** + * This key is used to perform lookups in sMethods without causing allocations. + */ + private static final MethodKey sLookupKey = new MethodKey(); /** * @hide @@ -255,37 +253,47 @@ public class RemoteViews implements Parcelable, Filter { } /** - * Handle with care! + * Stores information related to reflection method lookup. */ - static class MutablePair<F, S> { - F first; - S second; - - MutablePair(F first, S second) { - this.first = first; - this.second = second; - } + static class MethodKey { + public Class targetClass; + public Class paramClass; + public String methodName; @Override public boolean equals(Object o) { - if (!(o instanceof MutablePair)) { + if (!(o instanceof MethodKey)) { return false; } - MutablePair<?, ?> p = (MutablePair<?, ?>) o; - return Objects.equal(p.first, first) && Objects.equal(p.second, second); + MethodKey p = (MethodKey) o; + return Objects.equal(p.targetClass, targetClass) + && Objects.equal(p.paramClass, paramClass) + && Objects.equal(p.methodName, methodName); } @Override public int hashCode() { - return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode()); + return Objects.hashCode(targetClass) ^ Objects.hashCode(paramClass) + ^ Objects.hashCode(methodName); + } + + public void set(Class targetClass, Class paramClass, String methodName) { + this.targetClass = targetClass; + this.paramClass = paramClass; + this.methodName = methodName; } } + /** - * This pair is used to perform lookups in sMethods without causing allocations. + * Stores information related to reflection method lookup result. */ - private final MutablePair<String, Class<?>> mPair = - new MutablePair<String, Class<?>>(null, null); + static class MethodArgs { + public MethodHandle syncMethod; + public MethodHandle asyncMethod; + public String asyncMethodName; + } + /** * This annotation indicates that a subclass of View is allowed to be used @@ -307,6 +315,12 @@ public class RemoteViews implements Parcelable, Filter { public ActionException(String message) { super(message); } + /** + * @hide + */ + public ActionException(Throwable t) { + super(t); + } } /** @hide */ @@ -943,73 +957,66 @@ public class RemoteViews implements Parcelable, Filter { return rect; } - private Method getMethod(View view, String methodName, Class<?> paramType) { - Method method; + private MethodHandle getMethod(View view, String methodName, Class<?> paramType, + boolean async) { + MethodArgs result; Class<? extends View> klass = view.getClass(); - synchronized (sMethodsLock) { - ArrayMap<MutablePair<String, Class<?>>, Method> methods = sMethods.get(klass); - if (methods == null) { - methods = new ArrayMap<MutablePair<String, Class<?>>, Method>(); - sMethods.put(klass, methods); - } - - mPair.first = methodName; - mPair.second = paramType; + synchronized (sMethods) { + // The key is defined by the view class, param class and method name. + sLookupKey.set(klass, paramType, methodName); + result = sMethods.get(sLookupKey); - method = methods.get(mPair); - if (method == null) { + if (result == null) { + Method method; try { if (paramType == null) { method = klass.getMethod(methodName); } else { method = klass.getMethod(methodName, paramType); } - } catch (NoSuchMethodException ex) { - throw new ActionException("view: " + klass.getName() + " doesn't have method: " - + methodName + getParameters(paramType)); - } + if (!method.isAnnotationPresent(RemotableViewMethod.class)) { + throw new ActionException("view: " + klass.getName() + + " can't use method with RemoteViews: " + + methodName + getParameters(paramType)); + } - if (!method.isAnnotationPresent(RemotableViewMethod.class)) { - throw new ActionException("view: " + klass.getName() - + " can't use method with RemoteViews: " + result = new MethodArgs(); + result.syncMethod = MethodHandles.publicLookup().unreflect(method); + result.asyncMethodName = + method.getAnnotation(RemotableViewMethod.class).asyncImpl(); + } catch (NoSuchMethodException | IllegalAccessException ex) { + throw new ActionException("view: " + klass.getName() + " doesn't have method: " + methodName + getParameters(paramType)); } - methods.put(new MutablePair<String, Class<?>>(methodName, paramType), method); + MethodKey key = new MethodKey(); + key.set(klass, paramType, methodName); + sMethods.put(key, result); } - } - - return method; - } - /** - * @return the async implementation of the provided method. - */ - private Method getAsyncMethod(Method method) { - synchronized (sAsyncMethods) { - int valueIndex = sAsyncMethods.indexOfKey(method); - if (valueIndex >= 0) { - return sAsyncMethods.valueAt(valueIndex); + if (!async) { + return result.syncMethod; } - - RemotableViewMethod annotation = method.getAnnotation(RemotableViewMethod.class); - Method asyncMethod = null; - if (!annotation.asyncImpl().isEmpty()) { + // Check this so see if async method is implemented or not. + if (result.asyncMethodName.isEmpty()) { + return null; + } + // Async method is lazily loaded. If it is not yet loaded, load now. + if (result.asyncMethod == null) { + MethodType asyncType = result.syncMethod.type() + .dropParameterTypes(0, 1).changeReturnType(Runnable.class); try { - asyncMethod = method.getDeclaringClass() - .getMethod(annotation.asyncImpl(), method.getParameterTypes()); - if (!asyncMethod.getReturnType().equals(Runnable.class)) { - throw new ActionException("Async implementation for " + method.getName() + - " does not return a Runnable"); - } - } catch (NoSuchMethodException ex) { - throw new ActionException("Async implementation declared but not defined for " + - method.getName()); + result.asyncMethod = MethodHandles.publicLookup().findVirtual( + klass, result.asyncMethodName, asyncType); + } catch (NoSuchMethodException | IllegalAccessException ex) { + throw new ActionException("Async implementation declared as " + + result.asyncMethodName + " but not defined for " + methodName + + ": public Runnable " + result.asyncMethodName + " (" + + TextUtils.join(",", asyncType.parameterArray()) + ")"); } } - sAsyncMethods.put(method, asyncMethod); - return asyncMethod; + return result.asyncMethod; } } @@ -1018,12 +1025,6 @@ public class RemoteViews implements Parcelable, Filter { return "(" + paramType + ")"; } - private static Object[] wrapArg(Object value) { - Object[] args = sInvokeArgsTls.get(); - args[0] = value; - return args; - } - /** * Equivalent to calling a combination of {@link Drawable#setAlpha(int)}, * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}, @@ -1140,10 +1141,8 @@ public class RemoteViews implements Parcelable, Filter { if (view == null) return; try { - getMethod(view, this.methodName, null).invoke(view); - } catch (ActionException e) { - throw e; - } catch (Exception ex) { + getMethod(view, this.methodName, null, false /* async */).invoke(view); + } catch (Throwable ex) { throw new ActionException(ex); } } @@ -1516,12 +1515,9 @@ public class RemoteViews implements Parcelable, Filter { if (param == null) { throw new ActionException("bad type: " + this.type); } - try { - getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value)); - } catch (ActionException e) { - throw e; - } catch (Exception ex) { + getMethod(view, this.methodName, param, false /* async */).invoke(view, this.value); + } catch (Throwable ex) { throw new ActionException(ex); } } @@ -1537,11 +1533,10 @@ public class RemoteViews implements Parcelable, Filter { } try { - Method method = getMethod(view, this.methodName, param); - Method asyncMethod = getAsyncMethod(method); + MethodHandle method = getMethod(view, this.methodName, param, true /* async */); - if (asyncMethod != null) { - Runnable endAction = (Runnable) asyncMethod.invoke(view, wrapArg(this.value)); + if (method != null) { + Runnable endAction = (Runnable) method.invoke(view, this.value); if (endAction == null) { return ACTION_NOOP; } else { @@ -1555,9 +1550,7 @@ public class RemoteViews implements Parcelable, Filter { return new RunnableAction(endAction); } } - } catch (ActionException e) { - throw e; - } catch (Exception ex) { + } catch (Throwable ex) { throw new ActionException(ex); } @@ -2672,7 +2665,7 @@ public class RemoteViews implements Parcelable, Filter { * given {@link RemoteViews}. * * @param viewId The id of the parent {@link ViewGroup} to add the child into. - * @param nestedView {@link RemoveViews} of the child to add. + * @param nestedView {@link RemoteViews} of the child to add. * @param index The position at which to add the child. * * @hide |