diff options
| author | Chet Haase <chet@google.com> | 2010-10-27 13:18:54 -0700 |
|---|---|---|
| committer | Chet Haase <chet@google.com> | 2010-10-27 13:48:02 -0700 |
| commit | e3bc4e6f102fbef760fe0a59dd807363571b0867 (patch) | |
| tree | 23f2cfa012e4632f4ac70ba235f1601c4d9dbf4a | |
| parent | 9440083be22ec13e252aa347e654911d33e50086 (diff) | |
| download | frameworks_base-e3bc4e6f102fbef760fe0a59dd807363571b0867.zip frameworks_base-e3bc4e6f102fbef760fe0a59dd807363571b0867.tar.gz frameworks_base-e3bc4e6f102fbef760fe0a59dd807363571b0867.tar.bz2 | |
Use ThreadLocal for static Animator variables.
Certain fields in Animator are statics, like the list of current animations and the
main handler. However, since there may be >1 UI thread per process, these should really
be ThreadLocal variables, so that they are local to each UI thread. For example,
most animators will cause an invalidation in the view hierarchy, which can only
happen in the UI thread for that view.
Change-Id: I42be61c781ffac97b527f78ce1ffc2e0cdc42999
| -rwxr-xr-x | core/java/android/animation/ValueAnimator.java | 139 |
1 files changed, 92 insertions, 47 deletions
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index e269c31..ad8c971 100755 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -83,15 +83,61 @@ public class ValueAnimator extends Animator { */ private long mSeekTime = -1; + // TODO: We access the following ThreadLocal variables often, some of them on every update. + // If ThreadLocal access is significantly expensive, we may want to put all of these + // fields into a structure sot hat we just access ThreadLocal once to get the reference + // to that structure, then access the structure directly for each field. + // The static sAnimationHandler processes the internal timing loop on which all animations // are based - private static AnimationHandler sAnimationHandler; + private static ThreadLocal<AnimationHandler> sAnimationHandler = + new ThreadLocal<AnimationHandler>(); + + // The per-thread list of all active animations + private static final ThreadLocal<ArrayList<ValueAnimator>> sAnimations = + new ThreadLocal<ArrayList<ValueAnimator>>() { + @Override + protected ArrayList<ValueAnimator> initialValue() { + return new ArrayList<ValueAnimator>(); + } + }; + + // The per-thread set of animations to be started on the next animation frame + private static final ThreadLocal<ArrayList<ValueAnimator>> sPendingAnimations = + new ThreadLocal<ArrayList<ValueAnimator>>() { + @Override + protected ArrayList<ValueAnimator> initialValue() { + return new ArrayList<ValueAnimator>(); + } + }; + + /** + * Internal per-thread collections used to avoid set collisions as animations start and end + * while being processed. + */ + private static final ThreadLocal<ArrayList<ValueAnimator>> sDelayedAnims = + new ThreadLocal<ArrayList<ValueAnimator>>() { + @Override + protected ArrayList<ValueAnimator> initialValue() { + return new ArrayList<ValueAnimator>(); + } + }; - // The static list of all active animations - private static final ArrayList<ValueAnimator> sAnimations = new ArrayList<ValueAnimator>(); + private static final ThreadLocal<ArrayList<ValueAnimator>> sEndingAnims = + new ThreadLocal<ArrayList<ValueAnimator>>() { + @Override + protected ArrayList<ValueAnimator> initialValue() { + return new ArrayList<ValueAnimator>(); + } + }; - // The set of animations to be started on the next animation frame - private static final ArrayList<ValueAnimator> sPendingAnimations = new ArrayList<ValueAnimator>(); + private static final ThreadLocal<ArrayList<ValueAnimator>> sReadyAnims = + new ThreadLocal<ArrayList<ValueAnimator>>() { + @Override + protected ArrayList<ValueAnimator> initialValue() { + return new ArrayList<ValueAnimator>(); + } + }; // The time interpolator to be used if none is set on the animation private static final TimeInterpolator sDefaultInterpolator = @@ -136,14 +182,6 @@ public class ValueAnimator extends Animator { private int mPlayingState = STOPPED; /** - * Internal collections used to avoid set collisions as animations start and end while being - * processed. - */ - private static final ArrayList<ValueAnimator> sEndingAnims = new ArrayList<ValueAnimator>(); - private static final ArrayList<ValueAnimator> sDelayedAnims = new ArrayList<ValueAnimator>(); - private static final ArrayList<ValueAnimator> sReadyAnims = new ArrayList<ValueAnimator>(); - - /** * Flag that denotes whether the animation is set up and ready to go. Used to * set up animation that has not yet been started. */ @@ -609,11 +647,14 @@ public class ValueAnimator extends Animator { @Override public void handleMessage(Message msg) { boolean callAgain = true; + ArrayList<ValueAnimator> animations = sAnimations.get(); + ArrayList<ValueAnimator> delayedAnims = sDelayedAnims.get(); switch (msg.what) { // TODO: should we avoid sending frame message when starting if we // were already running? case ANIMATION_START: - if (sAnimations.size() > 0 || sDelayedAnims.size() > 0) { + ArrayList<ValueAnimator> pendingAnimations = sPendingAnimations.get(); + if (animations.size() > 0 || delayedAnims.size() > 0) { callAgain = false; } // pendingAnims holds any animations that have requested to be started @@ -621,10 +662,10 @@ public class ValueAnimator extends Animator { // cause more to be added to the pending list (for example, if one animation // starting triggers another starting). So we loop until sPendingAnimations // is empty. - while (sPendingAnimations.size() > 0) { + while (pendingAnimations.size() > 0) { ArrayList<ValueAnimator> pendingCopy = - (ArrayList<ValueAnimator>) sPendingAnimations.clone(); - sPendingAnimations.clear(); + (ArrayList<ValueAnimator>) pendingAnimations.clone(); + pendingAnimations.clear(); int count = pendingCopy.size(); for (int i = 0; i < count; ++i) { ValueAnimator anim = pendingCopy.get(i); @@ -633,7 +674,7 @@ public class ValueAnimator extends Animator { anim.mPlayingState == CANCELED) { anim.startAnimation(); } else { - sDelayedAnims.add(anim); + delayedAnims.add(anim); } } } @@ -642,45 +683,47 @@ public class ValueAnimator extends Animator { // currentTime holds the common time for all animations processed // during this frame long currentTime = AnimationUtils.currentAnimationTimeMillis(); + ArrayList<ValueAnimator> readyAnims = sReadyAnims.get(); + ArrayList<ValueAnimator> endingAnims = sEndingAnims.get(); // First, process animations currently sitting on the delayed queue, adding // them to the active animations if they are ready - int numDelayedAnims = sDelayedAnims.size(); + int numDelayedAnims = delayedAnims.size(); for (int i = 0; i < numDelayedAnims; ++i) { - ValueAnimator anim = sDelayedAnims.get(i); + ValueAnimator anim = delayedAnims.get(i); if (anim.delayedAnimationFrame(currentTime)) { - sReadyAnims.add(anim); + readyAnims.add(anim); } } - int numReadyAnims = sReadyAnims.size(); + int numReadyAnims = readyAnims.size(); if (numReadyAnims > 0) { for (int i = 0; i < numReadyAnims; ++i) { - ValueAnimator anim = sReadyAnims.get(i); + ValueAnimator anim = readyAnims.get(i); anim.startAnimation(); - sDelayedAnims.remove(anim); + delayedAnims.remove(anim); } - sReadyAnims.clear(); + readyAnims.clear(); } // Now process all active animations. The return value from animationFrame() // tells the handler whether it should now be ended - int numAnims = sAnimations.size(); + int numAnims = animations.size(); for (int i = 0; i < numAnims; ++i) { - ValueAnimator anim = sAnimations.get(i); + ValueAnimator anim = animations.get(i); if (anim.animationFrame(currentTime)) { - sEndingAnims.add(anim); + endingAnims.add(anim); } } - if (sEndingAnims.size() > 0) { - for (int i = 0; i < sEndingAnims.size(); ++i) { - sEndingAnims.get(i).endAnimation(); + if (endingAnims.size() > 0) { + for (int i = 0; i < endingAnims.size(); ++i) { + endingAnims.get(i).endAnimation(); } - sEndingAnims.clear(); + endingAnims.clear(); } // If there are still active or delayed animations, call the handler again // after the frameDelay - if (callAgain && (!sAnimations.isEmpty() || !sDelayedAnims.isEmpty())) { + if (callAgain && (!animations.isEmpty() || !delayedAnims.isEmpty())) { sendEmptyMessageDelayed(ANIMATION_FRAME, Math.max(0, sFrameDelay - (AnimationUtils.currentAnimationTimeMillis() - currentTime))); } @@ -935,13 +978,13 @@ public class ValueAnimator extends Animator { mCurrentIteration = 0; mPlayingState = STOPPED; mStartedDelay = false; - sPendingAnimations.add(this); - if (sAnimationHandler == null) { - sAnimationHandler = new AnimationHandler(); + sPendingAnimations.get().add(this); + AnimationHandler animationHandler = sAnimationHandler.get(); + if (animationHandler == null) { + animationHandler = new AnimationHandler(); + sAnimationHandler.set(animationHandler); } - // TODO: does this put too many messages on the queue if the handler - // is already running? - sAnimationHandler.sendEmptyMessage(ANIMATION_START); + animationHandler.sendEmptyMessage(ANIMATION_START); } @Override @@ -965,14 +1008,16 @@ public class ValueAnimator extends Animator { @Override public void end() { - if (!sAnimations.contains(this) && !sPendingAnimations.contains(this)) { + if (!sAnimations.get().contains(this) && !sPendingAnimations.get().contains(this)) { // Special case if the animation has not yet started; get it ready for ending mStartedDelay = false; - sPendingAnimations.add(this); - if (sAnimationHandler == null) { - sAnimationHandler = new AnimationHandler(); + sPendingAnimations.get().add(this); + AnimationHandler animationHandler = sAnimationHandler.get(); + if (animationHandler == null) { + animationHandler = new AnimationHandler(); + sAnimationHandler.set(animationHandler); } - sAnimationHandler.sendEmptyMessage(ANIMATION_START); + animationHandler.sendEmptyMessage(ANIMATION_START); } // Just set the ENDED flag - this causes the animation to end the next time a frame // is processed. @@ -1009,7 +1054,7 @@ public class ValueAnimator extends Animator { * called on the UI thread. */ private void endAnimation() { - sAnimations.remove(this); + sAnimations.get().remove(this); mPlayingState = STOPPED; if (mListeners != null) { ArrayList<AnimatorListener> tmpListeners = @@ -1026,7 +1071,7 @@ public class ValueAnimator extends Animator { */ private void startAnimation() { initAnimation(); - sAnimations.add(this); + sAnimations.get().add(this); if (mStartDelay > 0 && mListeners != null) { // Listeners were already notified in start() if startDelay is 0; this is // just for delayed animations @@ -1225,6 +1270,6 @@ public class ValueAnimator extends Animator { * @hide */ public static int getCurrentAnimationsCount() { - return sAnimations.size(); + return sAnimations.get().size(); } } |
