Cache reflection lookups

This change also cleanups the code a little bit. Caching lookups
not only speed things up but it also avoids unnecessary allocations.

Change-Id: Ie3d1ae106d32b3b5f2c5d33b6318ed93cd9e27f9
This commit is contained in:
Romain Guy
2013-07-22 13:02:02 -07:00
parent 4a8baef3f5
commit e4d4e20e8f

View File

@@ -37,6 +37,7 @@ import android.os.Parcelable;
import android.os.StrictMode;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.LayoutInflater.Filter;
@@ -136,6 +137,16 @@ 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<String, Method>> sMethods =
new ArrayMap<Class<? extends View>, ArrayMap<String, Method>>();
private static final ThreadLocal<Object[]> sInvokeArgsTls = new ThreadLocal<Object[]>() {
@Override
protected Object[] initialValue() {
return new Object[1];
}
};
/**
* This annotation indicates that a subclass of View is alllowed to be used
* with the {@link RemoteViews} mechanism.
@@ -206,9 +217,8 @@ public class RemoteViews implements Parcelable, Filter {
* Overridden by each class to report on it's own memory usage
*/
public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
// We currently only calculate Bitmap memory usage, so by default, don't do anything
// here
return;
// We currently only calculate Bitmap memory usage, so by default,
// don't do anything here
}
public void setBitmapCache(BitmapCache bitmapCache) {
@@ -346,7 +356,7 @@ public class RemoteViews implements Parcelable, Filter {
}
if (target == root) {
target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent);
} else if (target != null && fillInIntent != null) {
} else if (fillInIntent != null) {
OnClickListener listener = new OnClickListener() {
public void onClick(View v) {
// Insure that this view is a child of an AdapterView
@@ -372,16 +382,7 @@ public class RemoteViews implements Parcelable, Filter {
PendingIntent pendingIntent = (PendingIntent) parent.getTag();
final float appScale = v.getContext().getResources()
.getCompatibilityInfo().applicationScale;
final int[] pos = new int[2];
v.getLocationOnScreen(pos);
final Rect rect = new Rect();
rect.left = (int) (pos[0] * appScale + 0.5f);
rect.top = (int) (pos[1] * appScale + 0.5f);
rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
final Rect rect = getSourceBounds(v);
fillInIntent.setSourceBounds(rect);
handler.onClickHandler(v, pendingIntent, fillInIntent);
@@ -452,16 +453,7 @@ public class RemoteViews implements Parcelable, Filter {
}
if (fillInIntent == null) return;
final float appScale = view.getContext().getResources()
.getCompatibilityInfo().applicationScale;
final int[] pos = new int[2];
view.getLocationOnScreen(pos);
final Rect rect = new Rect();
rect.left = (int) (pos[0] * appScale + 0.5f);
rect.top = (int) (pos[1] * appScale + 0.5f);
rect.right = (int) ((pos[0] + view.getWidth()) * appScale + 0.5f);
rect.bottom = (int) ((pos[1] + view.getHeight()) * appScale + 0.5f);
final Rect rect = getSourceBounds(view);
final Intent intent = new Intent();
intent.setSourceBounds(rect);
@@ -679,33 +671,22 @@ public class RemoteViews implements Parcelable, Filter {
}
}
if (target != null) {
// If the pendingIntent is null, we clear the onClickListener
OnClickListener listener = null;
if (pendingIntent != null) {
listener = new OnClickListener() {
public void onClick(View v) {
// Find target view location in screen coordinates and
// fill into PendingIntent before sending.
final float appScale = v.getContext().getResources()
.getCompatibilityInfo().applicationScale;
final int[] pos = new int[2];
v.getLocationOnScreen(pos);
// If the pendingIntent is null, we clear the onClickListener
OnClickListener listener = null;
if (pendingIntent != null) {
listener = new OnClickListener() {
public void onClick(View v) {
// Find target view location in screen coordinates and
// fill into PendingIntent before sending.
final Rect rect = getSourceBounds(v);
final Rect rect = new Rect();
rect.left = (int) (pos[0] * appScale + 0.5f);
rect.top = (int) (pos[1] * appScale + 0.5f);
rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
final Intent intent = new Intent();
intent.setSourceBounds(rect);
handler.onClickHandler(v, pendingIntent, intent);
}
};
}
target.setOnClickListener(listener);
final Intent intent = new Intent();
intent.setSourceBounds(rect);
handler.onClickHandler(v, pendingIntent, intent);
}
};
}
target.setOnClickListener(listener);
}
public String getActionName() {
@@ -717,6 +698,64 @@ public class RemoteViews implements Parcelable, Filter {
public final static int TAG = 1;
}
private static Rect getSourceBounds(View v) {
final float appScale = v.getContext().getResources()
.getCompatibilityInfo().applicationScale;
final int[] pos = new int[2];
v.getLocationOnScreen(pos);
final Rect rect = new Rect();
rect.left = (int) (pos[0] * appScale + 0.5f);
rect.top = (int) (pos[1] * appScale + 0.5f);
rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
return rect;
}
private static Method getMethod(View view, String methodName, Class<?> paramType) {
Method method;
Class<? extends View> klass = view.getClass();
synchronized (sMethodsLock) {
ArrayMap<String, Method> methods = sMethods.get(klass);
if (methods == null) {
methods = new ArrayMap<String, Method>();
sMethods.put(klass, methods);
}
method = methods.get(methodName);
if (method == null) {
try {
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));
}
methods.put(methodName, method);
}
}
return method;
}
private static String getParameters(Class<?> paramType) {
if (paramType == null) return "()";
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)},
@@ -836,28 +875,10 @@ public class RemoteViews implements Parcelable, Filter {
final View view = root.findViewById(viewId);
if (view == null) return;
Class klass = view.getClass();
Method method;
try {
method = klass.getMethod(this.methodName);
} catch (NoSuchMethodException ex) {
throw new ActionException("view: " + klass.getName() + " doesn't have method: "
+ this.methodName + "()");
}
if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
throw new ActionException("view: " + klass.getName()
+ " can't use method with RemoteViews: "
+ this.methodName + "()");
}
try {
//noinspection ConstantIfStatement
if (false) {
Log.d(LOG_TAG, "view: " + klass.getName() + " calling method: "
+ this.methodName + "()");
}
method.invoke(view);
getMethod(view, this.methodName, null).invoke(view);
} catch (ActionException e) {
throw e;
} catch (Exception ex) {
throw new ActionException(ex);
}
@@ -1157,7 +1178,7 @@ public class RemoteViews implements Parcelable, Filter {
}
}
private Class getParameterType() {
private Class<?> getParameterType() {
switch (this.type) {
case BOOLEAN:
return boolean.class;
@@ -1197,37 +1218,16 @@ public class RemoteViews implements Parcelable, Filter {
final View view = root.findViewById(viewId);
if (view == null) return;
Class param = getParameterType();
Class<?> param = getParameterType();
if (param == null) {
throw new ActionException("bad type: " + this.type);
}
Class klass = view.getClass();
Method method;
try {
method = klass.getMethod(this.methodName, getParameterType());
}
catch (NoSuchMethodException ex) {
throw new ActionException("view: " + klass.getName() + " doesn't have method: "
+ this.methodName + "(" + param.getName() + ")");
}
if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
throw new ActionException("view: " + klass.getName()
+ " can't use method with RemoteViews: "
+ this.methodName + "(" + param.getName() + ")");
}
try {
//noinspection ConstantIfStatement
if (false) {
Log.d(LOG_TAG, "view: " + klass.getName() + " calling method: "
+ this.methodName + "(" + param.getName() + ") with "
+ (this.value == null ? "null" : this.value.getClass().getName()));
}
method.invoke(view, this.value);
}
catch (Exception ex) {
getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value));
} catch (ActionException e) {
throw e;
} catch (Exception ex) {
throw new ActionException(ex);
}
}
@@ -1323,7 +1323,7 @@ public class RemoteViews implements Parcelable, Filter {
}
public String getActionName() {
return "ViewGroupAction" + this.nestedViews == null ? "Remove" : "Add";
return "ViewGroupAction" + (nestedViews == null ? "Remove" : "Add");
}
public int mergeBehavior() {
@@ -1370,7 +1370,6 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
final Context context = root.getContext();
final TextView target = (TextView) root.findViewById(viewId);
if (target == null) return;
if (isRelative) {
@@ -1415,7 +1414,6 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
final Context context = root.getContext();
final TextView target = (TextView) root.findViewById(viewId);
if (target == null) return;
target.setTextSize(units, size);
@@ -1462,7 +1460,6 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
final Context context = root.getContext();
final View target = root.findViewById(viewId);
if (target == null) return;
target.setPadding(left, top, right, bottom);
@@ -1494,6 +1491,7 @@ public class RemoteViews implements Parcelable, Filter {
return mMemoryUsage;
}
@SuppressWarnings("deprecation")
public void addBitmapMemory(Bitmap b) {
final Bitmap.Config c = b.getConfig();
// If we don't know, be pessimistic and assume 4
@@ -1597,7 +1595,7 @@ public class RemoteViews implements Parcelable, Filter {
if (mode == MODE_NORMAL) {
mPackage = parcel.readString();
mLayoutId = parcel.readInt();
mIsWidgetCollectionChild = parcel.readInt() == 1 ? true : false;
mIsWidgetCollectionChild = parcel.readInt() == 1;
int count = parcel.readInt();
if (count > 0) {