美文网首页
Android 内存泄漏 - Animator的使用与释放

Android 内存泄漏 - Animator的使用与释放

作者: chengjian666 | 来源:发表于2018-04-04 16:47 被阅读144次

Animator 导致泄漏

在属性动画中如果定义为无限循环,如果在Activity中播放这类动画并且在其退出(生命周期结果前)未停止动画,造成内存泄漏。

举例说明

假设有如下无限循环动画:

public class LeakActivity extends AppCompatActivity {

    private TextView textView;
    private ValueAnimator warningAnimation;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leak);
        textView = (TextView)findViewById(R.id.text_view);
        warningAnimation.setDuration(1000);
            warningAnimation.setRepeatMode(ValueAnimator.REVERSE);
            //定义为无线循环动画
            warningAnimation.setRepeatCount(ValueAnimator.INFINITE);
            warningAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animator) {
                    textView.setBackgroundColor((int) animator.getAnimatedValue());
                }
            });
    }
}

但是在符合某一条件但是没有满足(或者根本没有对该动画进行停止)即退出该LeakActivity.

解决方法

确保Animator.cancel() 能够在Activity退出时被执行到(尤其留意是否存在条件判断)。

源码分析泄漏原因

那么为什么不对Animator动画做取消动作,就会出现内存泄漏呢?
看到Animator中的cancel函数的源码:

@Override
    public void cancel() {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }

        // If end has already been requested, through a previous end() or cancel() call, no-op
        // until animation starts again.
        if (mAnimationEndRequested) {
            return;
        }

        // Only cancel if the animation is actually running or has been started and is about
        // to run
        // Only notify listeners if the animator has actually started
        if ((mStarted || mRunning) && mListeners != null) {
            if (!mRunning) {
                // If it's not yet running, then start listeners weren't called. Call them now.
                notifyStartListeners();
            }
            ArrayList<AnimatorListener> tmpListeners =
                    (ArrayList<AnimatorListener>) mListeners.clone();
            for (AnimatorListener listener : tmpListeners) {
                listener.onAnimationCancel(this);
            }
        }
        endAnimation();

    }

从源码中可以看出:cancel函数会针对AnimatorSet中所有的自动化逐个进行释放。
继续看endAnimation()函数:

private void endAnimation() {
        if (mAnimationEndRequested) {
            return;
        }
        removeAnimationCallback();
        ......
}

继续看到removeAnimationCallback()函数:

private void removeAnimationCallback() {
        if (!mSelfPulse) {
            return;
        }
        getAnimationHandler().removeCallback(this);
    }

其中getAnimationHandler()函数:

public AnimationHandler getAnimationHandler() {
        return AnimationHandler.getInstance();
    }

可以看出,其中可以猜想到getAnimationHandler()获取的应该是单例:

public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
    private boolean mListDirty = false;

    public static AnimationHandler getInstance() {
        if (sAnimatorHandler.get() == null) {
            sAnimatorHandler.set(new AnimationHandler());
        }
        return sAnimatorHandler.get();
    }

可以从源码中看出,确实是通过单例实现的,因此Animator的引用实际上在未调用cancel()/end()之前,如果该动画是无限循环,即自身不会主动结束释放,那么是一直被静态变量sAnimatorHandler所持有的,导致即使Activity退出,此块Animator的内存始终无法被回收。

相关文章

网友评论

      本文标题:Android 内存泄漏 - Animator的使用与释放

      本文链接:https://www.haomeiwen.com/subject/kdxlhftx.html