Skip to content

Commit 2936fc0

Browse files
committed
Make animators more robust against ending mid-stream
The logic in the frame processing code of ValueAnimator did not handle the situation of animators being ended while the current animation list was being processed. In particular, if a call to an animation update (which could result in a call out to user code) caused that animation, or other current animators, to end, then there was the risk of running off the end of the list of current animators. The fix is to work from a copy of the current animator list, processing frames on each one only if they also exist in the real animations list. Issue #6992223 Frequent System UI crash Change-Id: I742964558f8354f04c311b7b51c7686f26a4dacf
1 parent 6b7d46b commit 2936fc0

File tree

1 file changed

+11
-18
lines changed

1 file changed

+11
-18
lines changed

core/java/android/animation/ValueAnimator.java

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,9 @@ private static class AnimationHandler implements Runnable {
536536
// The per-thread list of all active animations
537537
private final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
538538

539+
// Used in doAnimationFrame() to avoid concurrent modifications of mAnimations
540+
private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>();
541+
539542
// The per-thread set of animations to be started on the next animation frame
540543
private final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>();
541544

@@ -605,28 +608,18 @@ private void doAnimationFrame(long frameTime) {
605608
// Now process all active animations. The return value from animationFrame()
606609
// tells the handler whether it should now be ended
607610
int numAnims = mAnimations.size();
608-
int i = 0;
609-
while (i < numAnims) {
610-
ValueAnimator anim = mAnimations.get(i);
611-
if (anim.doAnimationFrame(frameTime)) {
611+
for (int i = 0; i < numAnims; ++i) {
612+
mTmpAnimations.add(mAnimations.get(i));
613+
}
614+
for (int i = 0; i < numAnims; ++i) {
615+
ValueAnimator anim = mTmpAnimations.get(i);
616+
if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
612617
mEndingAnims.add(anim);
613618
}
614-
if (mAnimations.size() == numAnims) {
615-
++i;
616-
} else {
617-
// An animation might be canceled or ended by client code
618-
// during the animation frame. Check to see if this happened by
619-
// seeing whether the current index is the same as it was before
620-
// calling animationFrame(). Another approach would be to copy
621-
// animations to a temporary list and process that list instead,
622-
// but that entails garbage and processing overhead that would
623-
// be nice to avoid.
624-
--numAnims;
625-
mEndingAnims.remove(anim);
626-
}
627619
}
620+
mTmpAnimations.clear();
628621
if (mEndingAnims.size() > 0) {
629-
for (i = 0; i < mEndingAnims.size(); ++i) {
622+
for (int i = 0; i < mEndingAnims.size(); ++i) {
630623
mEndingAnims.get(i).endAnimation(this);
631624
}
632625
mEndingAnims.clear();

0 commit comments

Comments
 (0)