美文网首页
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