Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43f40fe9 authored by Chet Haase's avatar Chet Haase Committed by Android (Google) Code Review
Browse files

Merge "Adding JNI methods as a faster reflection mechanism"

parents 1ab022b8 6e0ecb4e
Loading
Loading
Loading
Loading
+10 −0
Original line number Original line Diff line number Diff line
@@ -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 {


@@ -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
+116 −13
Original line number Original line Diff line number Diff line
@@ -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;


@@ -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
@@ -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
@@ -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 {
@@ -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);
@@ -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;
    }
    }
@@ -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);
    }
    }


@@ -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;


@@ -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;
@@ -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;


@@ -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;
@@ -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
+17 −18
Original line number Original line Diff line number Diff line
@@ -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;


/**
/**
@@ -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;


@@ -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);
@@ -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);
            }
            }
@@ -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();
            }
            }
@@ -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;
@@ -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);
            }
            }
@@ -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();
            }
            }
@@ -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);
            }
            }
+2 −1
Original line number Original line Diff line number Diff line
@@ -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) \
+3 −0
Original line number Original line Diff line number Diff line
@@ -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;


@@ -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