Loading core/java/android/animation/ObjectAnimator.java +10 −0 Original line number Original line Diff line number Diff line Loading @@ -26,6 +26,9 @@ import java.lang.reflect.Method; * as well as the name of the property that will be animated. Appropriate set/get functions * as well as the name of the property that will be animated. Appropriate set/get functions * are then determined internally and the animation will call these functions as necessary to * are then determined internally and the animation will call these functions as necessary to * animate the property. * animate the property. * * @see #setPropertyName(String) * */ */ public final class ObjectAnimator extends ValueAnimator { public final class ObjectAnimator extends ValueAnimator { Loading @@ -42,6 +45,13 @@ public final class ObjectAnimator extends ValueAnimator { * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will * also be derived and called. * also be derived and called. * * * <p>For best performance of the mechanism that calls the setter function determined by the * name of the property being animated, use <code>float</code> or <code>int</code> typed values, * and make the setter function for those properties have a <code>void</code> return value. This * will cause the code to take an optimized path for these constrained circumstances. Other * property types and return types will work, but will have more overhead in processing * the requests due to normal reflection mechanisms.</p> * * <p>Note that the setter function derived from this property name * <p>Note that the setter function derived from this property name * must take the same parameter type as the * must take the same parameter type as the * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to Loading core/java/android/animation/PropertyValuesHolder.java +116 −13 Original line number Original line Diff line number Diff line Loading @@ -17,12 +17,9 @@ package android.animation; package android.animation; import android.util.Log; import android.util.Log; import android.animation.Keyframe.IntKeyframe; import android.animation.Keyframe.FloatKeyframe; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.HashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; Loading @@ -39,7 +36,7 @@ public class PropertyValuesHolder implements Cloneable { * unless this object is being used with ObjectAnimator. But this is the name by which * unless this object is being used with ObjectAnimator. But this is the name by which * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator. * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator. */ */ private String mPropertyName; String mPropertyName; /** /** * The setter function, if needed. ObjectAnimator hands off this functionality to * The setter function, if needed. ObjectAnimator hands off this functionality to Loading Loading @@ -99,10 +96,10 @@ public class PropertyValuesHolder implements Cloneable { // This lock is used to ensure that only one thread is accessing the property maps // This lock is used to ensure that only one thread is accessing the property maps // at a time. // at a time. private ReentrantReadWriteLock propertyMapLock = new ReentrantReadWriteLock(); final ReentrantReadWriteLock mPropertyMapLock = new ReentrantReadWriteLock(); // Used to pass single value to varargs parameter in setter invocation // Used to pass single value to varargs parameter in setter invocation Object[] mTmpValueArray = new Object[1]; final Object[] mTmpValueArray = new Object[1]; /** /** * The type evaluator used to calculate the animated values. This evaluator is determined * The type evaluator used to calculate the animated values. This evaluator is determined Loading Loading @@ -296,10 +293,7 @@ public class PropertyValuesHolder implements Cloneable { private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) { private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) { // TODO: faster implementation... // TODO: faster implementation... Method returnVal = null; Method returnVal = null; String firstLetter = mPropertyName.substring(0, 1); String methodName = getMethodName(prefix, mPropertyName); String theRest = mPropertyName.substring(1); firstLetter = firstLetter.toUpperCase(); String methodName = prefix + firstLetter + theRest; Class args[] = null; Class args[] = null; if (valueType == null) { if (valueType == null) { try { try { Loading Loading @@ -361,7 +355,7 @@ public class PropertyValuesHolder implements Cloneable { // another thread putting something in there after we've checked it // another thread putting something in there after we've checked it // but before we've added an entry to it // but before we've added an entry to it // TODO: can we store the setter/getter per Class instead of per Object? // TODO: can we store the setter/getter per Class instead of per Object? propertyMapLock.writeLock().lock(); mPropertyMapLock.writeLock().lock(); HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass); HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass); if (propertyMap != null) { if (propertyMap != null) { setterOrGetter = propertyMap.get(mPropertyName); setterOrGetter = propertyMap.get(mPropertyName); Loading @@ -375,7 +369,7 @@ public class PropertyValuesHolder implements Cloneable { propertyMap.put(mPropertyName, setterOrGetter); propertyMap.put(mPropertyName, setterOrGetter); } } } finally { } finally { propertyMapLock.writeLock().unlock(); mPropertyMapLock.writeLock().unlock(); } } return setterOrGetter; return setterOrGetter; } } Loading @@ -384,7 +378,7 @@ public class PropertyValuesHolder implements Cloneable { * Utility function to get the setter from targetClass * Utility function to get the setter from targetClass * @param targetClass The Class on which the requested method should exist. * @param targetClass The Class on which the requested method should exist. */ */ private void setupSetter(Class targetClass) { void setupSetter(Class targetClass) { mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType); mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType); } } Loading Loading @@ -650,8 +644,32 @@ public class PropertyValuesHolder implements Cloneable { return mAnimatedValue; return mAnimatedValue; } } /** * Utility method to derive a setter/getter method name from a property name, where the * prefix is typically "set" or "get" and the first letter of the property name is * capitalized. * * @param prefix The precursor to the method name, before the property name begins, typically * "set" or "get". * @param propertyName The name of the property that represents the bulk of the method name * after the prefix. The first letter of this word will be capitalized in the resulting * method name. * @return String the property name converted to a method name according to the conventions * specified above. */ static String getMethodName(String prefix, String propertyName) { char firstLetter = propertyName.charAt(0); String theRest = propertyName.substring(1); firstLetter = Character.toUpperCase(firstLetter); return prefix + firstLetter + theRest; } static class IntPropertyValuesHolder extends PropertyValuesHolder { static class IntPropertyValuesHolder extends PropertyValuesHolder { private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = new HashMap<Class, HashMap<String, Integer>>(); int mJniSetter; IntKeyframeSet mIntKeyframeSet; IntKeyframeSet mIntKeyframeSet; int mIntAnimatedValue; int mIntAnimatedValue; Loading Loading @@ -699,6 +717,10 @@ public class PropertyValuesHolder implements Cloneable { */ */ @Override @Override void setAnimatedValue(Object target) { void setAnimatedValue(Object target) { if (mJniSetter != 0) { nCallIntMethod(target, mJniSetter, mIntAnimatedValue); return; } if (mSetter != null) { if (mSetter != null) { try { try { mTmpValueArray[0] = mIntAnimatedValue; mTmpValueArray[0] = mIntAnimatedValue; Loading @@ -710,10 +732,48 @@ public class PropertyValuesHolder implements Cloneable { } } } } } } @Override void setupSetter(Class targetClass) { // Check new static hashmap<propName, int> for setter method try { mPropertyMapLock.writeLock().lock(); HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass); if (propertyMap != null) { Integer mJniSetterInteger = propertyMap.get(mPropertyName); if (mJniSetterInteger != null) { mJniSetter = mJniSetterInteger; } } if (mJniSetter == 0) { String methodName = getMethodName("set", mPropertyName); mJniSetter = nGetIntMethod(targetClass, methodName); if (mJniSetter != 0) { if (propertyMap == null) { propertyMap = new HashMap<String, Integer>(); sJNISetterPropertyMap.put(targetClass, propertyMap); } propertyMap.put(mPropertyName, mJniSetter); } } } catch (NoSuchMethodError e) { // System.out.println("Can't find native method using JNI, use reflection" + e); } finally { mPropertyMapLock.writeLock().unlock(); } if (mJniSetter == 0) { // Couldn't find method through fast JNI approach - just use reflection super.setupSetter(targetClass); } } } } static class FloatPropertyValuesHolder extends PropertyValuesHolder { static class FloatPropertyValuesHolder extends PropertyValuesHolder { private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = new HashMap<Class, HashMap<String, Integer>>(); int mJniSetter; FloatKeyframeSet mFloatKeyframeSet; FloatKeyframeSet mFloatKeyframeSet; float mFloatAnimatedValue; float mFloatAnimatedValue; Loading Loading @@ -761,6 +821,10 @@ public class PropertyValuesHolder implements Cloneable { */ */ @Override @Override void setAnimatedValue(Object target) { void setAnimatedValue(Object target) { if (mJniSetter != 0) { nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue); return; } if (mSetter != null) { if (mSetter != null) { try { try { mTmpValueArray[0] = mFloatAnimatedValue; mTmpValueArray[0] = mFloatAnimatedValue; Loading @@ -773,5 +837,44 @@ public class PropertyValuesHolder implements Cloneable { } } } } @Override void setupSetter(Class targetClass) { // Check new static hashmap<propName, int> for setter method try { mPropertyMapLock.writeLock().lock(); HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass); if (propertyMap != null) { Integer mJniSetterInteger = propertyMap.get(mPropertyName); if (mJniSetterInteger != null) { mJniSetter = mJniSetterInteger; } } if (mJniSetter == 0) { String methodName = getMethodName("set", mPropertyName); mJniSetter = nGetFloatMethod(targetClass, methodName); if (mJniSetter != 0) { if (propertyMap == null) { propertyMap = new HashMap<String, Integer>(); sJNISetterPropertyMap.put(targetClass, propertyMap); } } propertyMap.put(mPropertyName, mJniSetter); } } } catch (NoSuchMethodError e) { // System.out.println("Can't find native method using JNI, use reflection" + e); } finally { mPropertyMapLock.writeLock().unlock(); } if (mJniSetter == 0) { // Couldn't find method through fast JNI approach - just use reflection super.setupSetter(targetClass); } } } native static private int nGetIntMethod(Class targetClass, String methodName); native static private int nGetFloatMethod(Class targetClass, String methodName); native static private void nCallIntMethod(Object target, int methodID, int arg); native static private void nCallFloatMethod(Object target, int methodID, float arg); } } No newline at end of file core/java/android/view/ViewTreeObserver.java +17 −18 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.view; import android.graphics.Rect; import android.graphics.Rect; import java.util.ArrayList; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList; /** /** Loading @@ -32,10 +33,10 @@ import java.util.concurrent.CopyOnWriteArrayList; public final class ViewTreeObserver { public final class ViewTreeObserver { private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners; private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners; private CopyOnWriteArrayList<OnGlobalLayoutListener> mOnGlobalLayoutListeners; private CopyOnWriteArrayList<OnGlobalLayoutListener> mOnGlobalLayoutListeners; private CopyOnWriteArrayList<OnPreDrawListener> mOnPreDrawListeners; private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners; private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners; private CopyOnWriteArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners; private CopyOnWriteArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners; private CopyOnWriteArrayList<OnScrollChangedListener> mOnScrollChangedListeners; private CopyOnWriteArrayList<OnScrollChangedListener> mOnScrollChangedListeners; private ArrayList<OnPreDrawListener> mOnPreDrawListeners; private boolean mAlive = true; private boolean mAlive = true; Loading Loading @@ -354,7 +355,7 @@ public final class ViewTreeObserver { checkIsAlive(); checkIsAlive(); if (mOnPreDrawListeners == null) { if (mOnPreDrawListeners == null) { mOnPreDrawListeners = new CopyOnWriteArrayList<OnPreDrawListener>(); mOnPreDrawListeners = new ArrayList<OnPreDrawListener>(); } } mOnPreDrawListeners.add(listener); mOnPreDrawListeners.add(listener); Loading Loading @@ -526,7 +527,7 @@ public final class ViewTreeObserver { // could mutate the list by calling the various add/remove methods. This prevents // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. // the array from being modified while we iterate it. final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners; final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners; if (listeners != null) { if (listeners != null && listeners.size() > 0) { for (OnGlobalFocusChangeListener listener : listeners) { for (OnGlobalFocusChangeListener listener : listeners) { listener.onGlobalFocusChanged(oldFocus, newFocus); listener.onGlobalFocusChanged(oldFocus, newFocus); } } Loading @@ -544,7 +545,7 @@ public final class ViewTreeObserver { // could mutate the list by calling the various add/remove methods. This prevents // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. // the array from being modified while we iterate it. final CopyOnWriteArrayList<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners; final CopyOnWriteArrayList<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners; if (listeners != null) { if (listeners != null && listeners.size() > 0) { for (OnGlobalLayoutListener listener : listeners) { for (OnGlobalLayoutListener listener : listeners) { listener.onGlobalLayout(); listener.onGlobalLayout(); } } Loading @@ -560,15 +561,17 @@ public final class ViewTreeObserver { * @return True if the current draw should be canceled and resceduled, false otherwise. * @return True if the current draw should be canceled and resceduled, false otherwise. */ */ public final boolean dispatchOnPreDraw() { public final boolean dispatchOnPreDraw() { // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to // NOTE: we *must* clone the listener list to perform the dispatching. // perform the dispatching. The iterator is a safe guard against listeners that // The clone is a safe guard against listeners that // could mutate the list by calling the various add/remove methods. This prevents // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. // the array from being modified while we process it. boolean cancelDraw = false; boolean cancelDraw = false; final CopyOnWriteArrayList<OnPreDrawListener> listeners = mOnPreDrawListeners; if (mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0) { if (listeners != null) { final ArrayList<OnPreDrawListener> listeners = for (OnPreDrawListener listener : listeners) { (ArrayList<OnPreDrawListener>) mOnPreDrawListeners.clone(); cancelDraw |= !listener.onPreDraw(); int numListeners = listeners.size(); for (int i = 0; i < numListeners; ++i) { cancelDraw |= !(listeners.get(i).onPreDraw()); } } } } return cancelDraw; return cancelDraw; Loading @@ -580,13 +583,9 @@ public final class ViewTreeObserver { * @param inTouchMode True if the touch mode is now enabled, false otherwise. * @param inTouchMode True if the touch mode is now enabled, false otherwise. */ */ final void dispatchOnTouchModeChanged(boolean inTouchMode) { final void dispatchOnTouchModeChanged(boolean inTouchMode) { // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to // perform the dispatching. The iterator is a safe guard against listeners that // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners = final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners = mOnTouchModeChangeListeners; mOnTouchModeChangeListeners; if (listeners != null) { if (listeners != null && listeners.size() > 0) { for (OnTouchModeChangeListener listener : listeners) { for (OnTouchModeChangeListener listener : listeners) { listener.onTouchModeChanged(inTouchMode); listener.onTouchModeChanged(inTouchMode); } } Loading @@ -602,7 +601,7 @@ public final class ViewTreeObserver { // could mutate the list by calling the various add/remove methods. This prevents // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. // the array from being modified while we iterate it. final CopyOnWriteArrayList<OnScrollChangedListener> listeners = mOnScrollChangedListeners; final CopyOnWriteArrayList<OnScrollChangedListener> listeners = mOnScrollChangedListeners; if (listeners != null) { if (listeners != null && listeners.size() > 0) { for (OnScrollChangedListener listener : listeners) { for (OnScrollChangedListener listener : listeners) { listener.onScrollChanged(); listener.onScrollChanged(); } } Loading @@ -628,7 +627,7 @@ public final class ViewTreeObserver { // the array from being modified while we iterate it. // the array from being modified while we iterate it. final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners = final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners = mOnComputeInternalInsetsListeners; mOnComputeInternalInsetsListeners; if (listeners != null) { if (listeners != null && listeners.size() > 0) { for (OnComputeInternalInsetsListener listener : listeners) { for (OnComputeInternalInsetsListener listener : listeners) { listener.onComputeInternalInsets(inoutInfo); listener.onComputeInternalInsets(inoutInfo); } } Loading core/jni/Android.mk +2 −1 Original line number Original line Diff line number Diff line Loading @@ -144,7 +144,8 @@ LOCAL_SRC_FILES:= \ android_backup_FileBackupHelperBase.cpp \ android_backup_FileBackupHelperBase.cpp \ android_backup_BackupHelperDispatcher.cpp \ android_backup_BackupHelperDispatcher.cpp \ android_content_res_ObbScanner.cpp \ android_content_res_ObbScanner.cpp \ android_content_res_Configuration.cpp android_content_res_Configuration.cpp \ android_animation_PropertyValuesHolder.cpp LOCAL_C_INCLUDES += \ LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ $(JNI_H_INCLUDE) \ Loading core/jni/AndroidRuntime.cpp +3 −0 Original line number Original line Diff line number Diff line Loading @@ -171,6 +171,7 @@ extern int register_android_view_KeyEvent(JNIEnv* env); extern int register_android_view_MotionEvent(JNIEnv* env); extern int register_android_view_MotionEvent(JNIEnv* env); extern int register_android_content_res_ObbScanner(JNIEnv* env); extern int register_android_content_res_ObbScanner(JNIEnv* env); extern int register_android_content_res_Configuration(JNIEnv* env); extern int register_android_content_res_Configuration(JNIEnv* env); extern int register_android_animation_PropertyValuesHolder(JNIEnv *env); static AndroidRuntime* gCurRuntime = NULL; static AndroidRuntime* gCurRuntime = NULL; Loading Loading @@ -1291,6 +1292,8 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_res_ObbScanner), REG_JNI(register_android_content_res_ObbScanner), REG_JNI(register_android_content_res_Configuration), REG_JNI(register_android_content_res_Configuration), REG_JNI(register_android_animation_PropertyValuesHolder), }; }; /* /* Loading Loading
core/java/android/animation/ObjectAnimator.java +10 −0 Original line number Original line Diff line number Diff line Loading @@ -26,6 +26,9 @@ import java.lang.reflect.Method; * as well as the name of the property that will be animated. Appropriate set/get functions * as well as the name of the property that will be animated. Appropriate set/get functions * are then determined internally and the animation will call these functions as necessary to * are then determined internally and the animation will call these functions as necessary to * animate the property. * animate the property. * * @see #setPropertyName(String) * */ */ public final class ObjectAnimator extends ValueAnimator { public final class ObjectAnimator extends ValueAnimator { Loading @@ -42,6 +45,13 @@ public final class ObjectAnimator extends ValueAnimator { * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will * also be derived and called. * also be derived and called. * * * <p>For best performance of the mechanism that calls the setter function determined by the * name of the property being animated, use <code>float</code> or <code>int</code> typed values, * and make the setter function for those properties have a <code>void</code> return value. This * will cause the code to take an optimized path for these constrained circumstances. Other * property types and return types will work, but will have more overhead in processing * the requests due to normal reflection mechanisms.</p> * * <p>Note that the setter function derived from this property name * <p>Note that the setter function derived from this property name * must take the same parameter type as the * must take the same parameter type as the * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to Loading
core/java/android/animation/PropertyValuesHolder.java +116 −13 Original line number Original line Diff line number Diff line Loading @@ -17,12 +17,9 @@ package android.animation; package android.animation; import android.util.Log; import android.util.Log; import android.animation.Keyframe.IntKeyframe; import android.animation.Keyframe.FloatKeyframe; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.HashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; Loading @@ -39,7 +36,7 @@ public class PropertyValuesHolder implements Cloneable { * unless this object is being used with ObjectAnimator. But this is the name by which * unless this object is being used with ObjectAnimator. But this is the name by which * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator. * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator. */ */ private String mPropertyName; String mPropertyName; /** /** * The setter function, if needed. ObjectAnimator hands off this functionality to * The setter function, if needed. ObjectAnimator hands off this functionality to Loading Loading @@ -99,10 +96,10 @@ public class PropertyValuesHolder implements Cloneable { // This lock is used to ensure that only one thread is accessing the property maps // This lock is used to ensure that only one thread is accessing the property maps // at a time. // at a time. private ReentrantReadWriteLock propertyMapLock = new ReentrantReadWriteLock(); final ReentrantReadWriteLock mPropertyMapLock = new ReentrantReadWriteLock(); // Used to pass single value to varargs parameter in setter invocation // Used to pass single value to varargs parameter in setter invocation Object[] mTmpValueArray = new Object[1]; final Object[] mTmpValueArray = new Object[1]; /** /** * The type evaluator used to calculate the animated values. This evaluator is determined * The type evaluator used to calculate the animated values. This evaluator is determined Loading Loading @@ -296,10 +293,7 @@ public class PropertyValuesHolder implements Cloneable { private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) { private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) { // TODO: faster implementation... // TODO: faster implementation... Method returnVal = null; Method returnVal = null; String firstLetter = mPropertyName.substring(0, 1); String methodName = getMethodName(prefix, mPropertyName); String theRest = mPropertyName.substring(1); firstLetter = firstLetter.toUpperCase(); String methodName = prefix + firstLetter + theRest; Class args[] = null; Class args[] = null; if (valueType == null) { if (valueType == null) { try { try { Loading Loading @@ -361,7 +355,7 @@ public class PropertyValuesHolder implements Cloneable { // another thread putting something in there after we've checked it // another thread putting something in there after we've checked it // but before we've added an entry to it // but before we've added an entry to it // TODO: can we store the setter/getter per Class instead of per Object? // TODO: can we store the setter/getter per Class instead of per Object? propertyMapLock.writeLock().lock(); mPropertyMapLock.writeLock().lock(); HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass); HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass); if (propertyMap != null) { if (propertyMap != null) { setterOrGetter = propertyMap.get(mPropertyName); setterOrGetter = propertyMap.get(mPropertyName); Loading @@ -375,7 +369,7 @@ public class PropertyValuesHolder implements Cloneable { propertyMap.put(mPropertyName, setterOrGetter); propertyMap.put(mPropertyName, setterOrGetter); } } } finally { } finally { propertyMapLock.writeLock().unlock(); mPropertyMapLock.writeLock().unlock(); } } return setterOrGetter; return setterOrGetter; } } Loading @@ -384,7 +378,7 @@ public class PropertyValuesHolder implements Cloneable { * Utility function to get the setter from targetClass * Utility function to get the setter from targetClass * @param targetClass The Class on which the requested method should exist. * @param targetClass The Class on which the requested method should exist. */ */ private void setupSetter(Class targetClass) { void setupSetter(Class targetClass) { mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType); mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType); } } Loading Loading @@ -650,8 +644,32 @@ public class PropertyValuesHolder implements Cloneable { return mAnimatedValue; return mAnimatedValue; } } /** * Utility method to derive a setter/getter method name from a property name, where the * prefix is typically "set" or "get" and the first letter of the property name is * capitalized. * * @param prefix The precursor to the method name, before the property name begins, typically * "set" or "get". * @param propertyName The name of the property that represents the bulk of the method name * after the prefix. The first letter of this word will be capitalized in the resulting * method name. * @return String the property name converted to a method name according to the conventions * specified above. */ static String getMethodName(String prefix, String propertyName) { char firstLetter = propertyName.charAt(0); String theRest = propertyName.substring(1); firstLetter = Character.toUpperCase(firstLetter); return prefix + firstLetter + theRest; } static class IntPropertyValuesHolder extends PropertyValuesHolder { static class IntPropertyValuesHolder extends PropertyValuesHolder { private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = new HashMap<Class, HashMap<String, Integer>>(); int mJniSetter; IntKeyframeSet mIntKeyframeSet; IntKeyframeSet mIntKeyframeSet; int mIntAnimatedValue; int mIntAnimatedValue; Loading Loading @@ -699,6 +717,10 @@ public class PropertyValuesHolder implements Cloneable { */ */ @Override @Override void setAnimatedValue(Object target) { void setAnimatedValue(Object target) { if (mJniSetter != 0) { nCallIntMethod(target, mJniSetter, mIntAnimatedValue); return; } if (mSetter != null) { if (mSetter != null) { try { try { mTmpValueArray[0] = mIntAnimatedValue; mTmpValueArray[0] = mIntAnimatedValue; Loading @@ -710,10 +732,48 @@ public class PropertyValuesHolder implements Cloneable { } } } } } } @Override void setupSetter(Class targetClass) { // Check new static hashmap<propName, int> for setter method try { mPropertyMapLock.writeLock().lock(); HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass); if (propertyMap != null) { Integer mJniSetterInteger = propertyMap.get(mPropertyName); if (mJniSetterInteger != null) { mJniSetter = mJniSetterInteger; } } if (mJniSetter == 0) { String methodName = getMethodName("set", mPropertyName); mJniSetter = nGetIntMethod(targetClass, methodName); if (mJniSetter != 0) { if (propertyMap == null) { propertyMap = new HashMap<String, Integer>(); sJNISetterPropertyMap.put(targetClass, propertyMap); } propertyMap.put(mPropertyName, mJniSetter); } } } catch (NoSuchMethodError e) { // System.out.println("Can't find native method using JNI, use reflection" + e); } finally { mPropertyMapLock.writeLock().unlock(); } if (mJniSetter == 0) { // Couldn't find method through fast JNI approach - just use reflection super.setupSetter(targetClass); } } } } static class FloatPropertyValuesHolder extends PropertyValuesHolder { static class FloatPropertyValuesHolder extends PropertyValuesHolder { private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = new HashMap<Class, HashMap<String, Integer>>(); int mJniSetter; FloatKeyframeSet mFloatKeyframeSet; FloatKeyframeSet mFloatKeyframeSet; float mFloatAnimatedValue; float mFloatAnimatedValue; Loading Loading @@ -761,6 +821,10 @@ public class PropertyValuesHolder implements Cloneable { */ */ @Override @Override void setAnimatedValue(Object target) { void setAnimatedValue(Object target) { if (mJniSetter != 0) { nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue); return; } if (mSetter != null) { if (mSetter != null) { try { try { mTmpValueArray[0] = mFloatAnimatedValue; mTmpValueArray[0] = mFloatAnimatedValue; Loading @@ -773,5 +837,44 @@ public class PropertyValuesHolder implements Cloneable { } } } } @Override void setupSetter(Class targetClass) { // Check new static hashmap<propName, int> for setter method try { mPropertyMapLock.writeLock().lock(); HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass); if (propertyMap != null) { Integer mJniSetterInteger = propertyMap.get(mPropertyName); if (mJniSetterInteger != null) { mJniSetter = mJniSetterInteger; } } if (mJniSetter == 0) { String methodName = getMethodName("set", mPropertyName); mJniSetter = nGetFloatMethod(targetClass, methodName); if (mJniSetter != 0) { if (propertyMap == null) { propertyMap = new HashMap<String, Integer>(); sJNISetterPropertyMap.put(targetClass, propertyMap); } } propertyMap.put(mPropertyName, mJniSetter); } } } catch (NoSuchMethodError e) { // System.out.println("Can't find native method using JNI, use reflection" + e); } finally { mPropertyMapLock.writeLock().unlock(); } if (mJniSetter == 0) { // Couldn't find method through fast JNI approach - just use reflection super.setupSetter(targetClass); } } } native static private int nGetIntMethod(Class targetClass, String methodName); native static private int nGetFloatMethod(Class targetClass, String methodName); native static private void nCallIntMethod(Object target, int methodID, int arg); native static private void nCallFloatMethod(Object target, int methodID, float arg); } } No newline at end of file
core/java/android/view/ViewTreeObserver.java +17 −18 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.view; import android.graphics.Rect; import android.graphics.Rect; import java.util.ArrayList; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList; /** /** Loading @@ -32,10 +33,10 @@ import java.util.concurrent.CopyOnWriteArrayList; public final class ViewTreeObserver { public final class ViewTreeObserver { private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners; private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners; private CopyOnWriteArrayList<OnGlobalLayoutListener> mOnGlobalLayoutListeners; private CopyOnWriteArrayList<OnGlobalLayoutListener> mOnGlobalLayoutListeners; private CopyOnWriteArrayList<OnPreDrawListener> mOnPreDrawListeners; private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners; private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners; private CopyOnWriteArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners; private CopyOnWriteArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners; private CopyOnWriteArrayList<OnScrollChangedListener> mOnScrollChangedListeners; private CopyOnWriteArrayList<OnScrollChangedListener> mOnScrollChangedListeners; private ArrayList<OnPreDrawListener> mOnPreDrawListeners; private boolean mAlive = true; private boolean mAlive = true; Loading Loading @@ -354,7 +355,7 @@ public final class ViewTreeObserver { checkIsAlive(); checkIsAlive(); if (mOnPreDrawListeners == null) { if (mOnPreDrawListeners == null) { mOnPreDrawListeners = new CopyOnWriteArrayList<OnPreDrawListener>(); mOnPreDrawListeners = new ArrayList<OnPreDrawListener>(); } } mOnPreDrawListeners.add(listener); mOnPreDrawListeners.add(listener); Loading Loading @@ -526,7 +527,7 @@ public final class ViewTreeObserver { // could mutate the list by calling the various add/remove methods. This prevents // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. // the array from being modified while we iterate it. final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners; final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners; if (listeners != null) { if (listeners != null && listeners.size() > 0) { for (OnGlobalFocusChangeListener listener : listeners) { for (OnGlobalFocusChangeListener listener : listeners) { listener.onGlobalFocusChanged(oldFocus, newFocus); listener.onGlobalFocusChanged(oldFocus, newFocus); } } Loading @@ -544,7 +545,7 @@ public final class ViewTreeObserver { // could mutate the list by calling the various add/remove methods. This prevents // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. // the array from being modified while we iterate it. final CopyOnWriteArrayList<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners; final CopyOnWriteArrayList<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners; if (listeners != null) { if (listeners != null && listeners.size() > 0) { for (OnGlobalLayoutListener listener : listeners) { for (OnGlobalLayoutListener listener : listeners) { listener.onGlobalLayout(); listener.onGlobalLayout(); } } Loading @@ -560,15 +561,17 @@ public final class ViewTreeObserver { * @return True if the current draw should be canceled and resceduled, false otherwise. * @return True if the current draw should be canceled and resceduled, false otherwise. */ */ public final boolean dispatchOnPreDraw() { public final boolean dispatchOnPreDraw() { // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to // NOTE: we *must* clone the listener list to perform the dispatching. // perform the dispatching. The iterator is a safe guard against listeners that // The clone is a safe guard against listeners that // could mutate the list by calling the various add/remove methods. This prevents // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. // the array from being modified while we process it. boolean cancelDraw = false; boolean cancelDraw = false; final CopyOnWriteArrayList<OnPreDrawListener> listeners = mOnPreDrawListeners; if (mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0) { if (listeners != null) { final ArrayList<OnPreDrawListener> listeners = for (OnPreDrawListener listener : listeners) { (ArrayList<OnPreDrawListener>) mOnPreDrawListeners.clone(); cancelDraw |= !listener.onPreDraw(); int numListeners = listeners.size(); for (int i = 0; i < numListeners; ++i) { cancelDraw |= !(listeners.get(i).onPreDraw()); } } } } return cancelDraw; return cancelDraw; Loading @@ -580,13 +583,9 @@ public final class ViewTreeObserver { * @param inTouchMode True if the touch mode is now enabled, false otherwise. * @param inTouchMode True if the touch mode is now enabled, false otherwise. */ */ final void dispatchOnTouchModeChanged(boolean inTouchMode) { final void dispatchOnTouchModeChanged(boolean inTouchMode) { // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to // perform the dispatching. The iterator is a safe guard against listeners that // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners = final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners = mOnTouchModeChangeListeners; mOnTouchModeChangeListeners; if (listeners != null) { if (listeners != null && listeners.size() > 0) { for (OnTouchModeChangeListener listener : listeners) { for (OnTouchModeChangeListener listener : listeners) { listener.onTouchModeChanged(inTouchMode); listener.onTouchModeChanged(inTouchMode); } } Loading @@ -602,7 +601,7 @@ public final class ViewTreeObserver { // could mutate the list by calling the various add/remove methods. This prevents // could mutate the list by calling the various add/remove methods. This prevents // the array from being modified while we iterate it. // the array from being modified while we iterate it. final CopyOnWriteArrayList<OnScrollChangedListener> listeners = mOnScrollChangedListeners; final CopyOnWriteArrayList<OnScrollChangedListener> listeners = mOnScrollChangedListeners; if (listeners != null) { if (listeners != null && listeners.size() > 0) { for (OnScrollChangedListener listener : listeners) { for (OnScrollChangedListener listener : listeners) { listener.onScrollChanged(); listener.onScrollChanged(); } } Loading @@ -628,7 +627,7 @@ public final class ViewTreeObserver { // the array from being modified while we iterate it. // the array from being modified while we iterate it. final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners = final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners = mOnComputeInternalInsetsListeners; mOnComputeInternalInsetsListeners; if (listeners != null) { if (listeners != null && listeners.size() > 0) { for (OnComputeInternalInsetsListener listener : listeners) { for (OnComputeInternalInsetsListener listener : listeners) { listener.onComputeInternalInsets(inoutInfo); listener.onComputeInternalInsets(inoutInfo); } } Loading
core/jni/Android.mk +2 −1 Original line number Original line Diff line number Diff line Loading @@ -144,7 +144,8 @@ LOCAL_SRC_FILES:= \ android_backup_FileBackupHelperBase.cpp \ android_backup_FileBackupHelperBase.cpp \ android_backup_BackupHelperDispatcher.cpp \ android_backup_BackupHelperDispatcher.cpp \ android_content_res_ObbScanner.cpp \ android_content_res_ObbScanner.cpp \ android_content_res_Configuration.cpp android_content_res_Configuration.cpp \ android_animation_PropertyValuesHolder.cpp LOCAL_C_INCLUDES += \ LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ $(JNI_H_INCLUDE) \ Loading
core/jni/AndroidRuntime.cpp +3 −0 Original line number Original line Diff line number Diff line Loading @@ -171,6 +171,7 @@ extern int register_android_view_KeyEvent(JNIEnv* env); extern int register_android_view_MotionEvent(JNIEnv* env); extern int register_android_view_MotionEvent(JNIEnv* env); extern int register_android_content_res_ObbScanner(JNIEnv* env); extern int register_android_content_res_ObbScanner(JNIEnv* env); extern int register_android_content_res_Configuration(JNIEnv* env); extern int register_android_content_res_Configuration(JNIEnv* env); extern int register_android_animation_PropertyValuesHolder(JNIEnv *env); static AndroidRuntime* gCurRuntime = NULL; static AndroidRuntime* gCurRuntime = NULL; Loading Loading @@ -1291,6 +1292,8 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_res_ObbScanner), REG_JNI(register_android_content_res_ObbScanner), REG_JNI(register_android_content_res_Configuration), REG_JNI(register_android_content_res_Configuration), REG_JNI(register_android_animation_PropertyValuesHolder), }; }; /* /* Loading