一、示例
ValueAnimator 持续输出从0到1000的整形值,然后反向输出1000到0数值,如此循环往复。该整形值转换为字符串设置到 TextView。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class LeakActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_leak)
val a = ValueAnimator.ofInt(0, 1000)
a.duration = 1000
a.repeatMode = ValueAnimator.REVERSE
a.repeatCount = ValueAnimator.INFINITE
// 传入AnimatorUpdateListener实现,此实现隐式持有Activity引用
a.addUpdateListener { l -> textView.text = l.animatedValue.toString() }
a.start()
}
}
二、源码
2.1 类签名
首先关注 ValueAnimator 的父类,这里尤其关注类继承了接口 AnimationHandler.AnimationFrameCallback。
1
public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback
2.2 start()方法
然后看 start() 方法做了什么
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@Override
public void start() {
start(false);
}
// 初始化很多状态相关的布尔值,这些值都不是我们关心的,直接看方法第36行
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mReversing = playBackwards;
mSelfPulse = !mSuppressSelfPulseRequested;
// Special case: reversing from seek-to-0 should act as if not seeked at all.
if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
if (mRepeatCount == INFINITE) {
// Calculate the fraction of the current iteration.
float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
mSeekFraction = 1 - fraction;
} else {
mSeekFraction = 1 + mRepeatCount - mSeekFraction;
}
}
mStarted = true;
mPaused = false;
mRunning = false;
mAnimationEndRequested = false;
// Resets mLastFrameTime when start() is called, so that if the animation was running,
// calling start() would put the animation in the
// started-but-not-yet-reached-the-first-frame phase.
mLastFrameTime = -1;
mFirstFrameTime = -1;
mStartTime = -1;
// 注意这里调用addAnimationCallback()方法
// 此方法调用完,下面几行代码是实际开始动画的逻辑
addAnimationCallback(0);
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
// If there's no start delay, init the animation and notify start listeners right away
// to be consistent with the previous behavior. Otherwise, postpone this until the first
// frame after the start delay.
startAnimation();
if (mSeekFraction == -1) {
// No seek, start at play time 0. Note that the reason we are not using fraction 0
// is because for animations with 0 duration, we want to be consistent with pre-N
// behavior: skip to the final value immediately.
setCurrentPlayTime(0);
} else {
setCurrentFraction(mSeekFraction);
}
}
}
上面提到的 addAnimationCallback() 方法看一下。
方法实参 this 就是 ValueAnimator 实例,作为 AnimationHandler.AnimationFrameCallback 接口的实现类,添加到 AnimationHandler.addAnimationFrameCallback() 方法内。
1
2
3
4
5
6
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
由于在下文这个 AnimationHandler 会再次出现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class AnimationHandler {
// Internal per-thread collections used to avoid set collisions as animations start and end
// while being processed.
private final ArrayMap<AnimationFrameCallback, Long> mDelayedCallbackStartTime =
new ArrayMap<>();
private final ArrayList<AnimationFrameCallback> mAnimationCallbacks =
new ArrayList<>();
private final ArrayList<AnimationFrameCallback> mCommitCallbacks =
new ArrayList<>();
// callback放入到列表中
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);
}
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
if (delay > 0) {
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
}
}
}
2.2 cancel()方法
文处实例代码没有调用动画的取消方法,所以直接看 ValueAnimator.cancel() 是如何释放资源的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Override
public void cancel() {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
// 如果动画没启动,不需要取消而直接返回
if (mAnimationEndRequested) {
return;
}
// 只停止正在执行的动画监听器
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();
}
endAnimation() 方法由 ValueAnimator 内部调用,结束动画时会从动画列表移除该动画,即 removeAnimationCallback()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private void endAnimation() {
if (mAnimationEndRequested) {
return;
}
// 关注移除动画回调
removeAnimationCallback();
mAnimationEndRequested = true;
mPaused = false;
boolean notify = (mStarted || mRunning) && mListeners != null;
if (notify && !mRunning) {
// If it's not yet running, then start listeners weren't called. Call them now.
notifyStartListeners();
}
mRunning = false;
mStarted = false;
mStartListenersCalled = false;
mLastFrameTime = -1;
mFirstFrameTime = -1;
mStartTime = -1;
if (notify && mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; ++i) {
// 这里面会移除示例设置的AnimatorUpdateListener
// 就是这个AnimatorUpdateListener持有的Activity引用
// 只有本方法不调用,这个监听器就不会移除
tmpListeners.get(i).onAnimationEnd(this, mReversing);
}
}
// mReversing needs to be reset *after* notifying the listeners for the end callbacks.
mReversing = false;
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
System.identityHashCode(this));
}
}
endAnimation() 方法调用以下方法,前文 start() 方法分析出现的 AnimationHandler 再次出现。
1
2
3
4
5
6
private void removeAnimationCallback() {
if (!mSelfPulse) {
return;
}
getAnimationHandler().removeCallback(this);
}
继续跳到这里获取单例
1
2
3
public AnimationHandler getAnimationHandler() {
return AnimationHandler.getInstance();
}
可见 AnimationHandler 存放在 ThreadLocal 里面,就是主线程的 ThreadLocal 区域内。而 AnimationFrameCallback 保存在 AnimationHandler 中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class AnimationHandler {
private final ArrayMap<AnimationFrameCallback, Long> mDelayedCallbackStartTime = new ArrayMap<>();
private final ArrayList<AnimationFrameCallback> mAnimationCallbacks = new ArrayList<>();
private final ArrayList<AnimationFrameCallback> mCommitCallbacks = new ArrayList<>();
// 所有注册的AnimationHandler按照线程分类,存放在线程的ThreadLocal区域内
// 因为动画在主线程创建和播放,所以保存在主线程ThreadLocal
// 而AnimationHandler实例包含的数据就是上面三个列表
public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
private boolean mListDirty = false;
// AnimationHandler实例被获取之后
// 会被调用后面的removeCallback(AnimationFrameCallback)方法
public static AnimationHandler getInstance() {
if (sAnimatorHandler.get() == null) {
sAnimatorHandler.set(new AnimationHandler());
}
return sAnimatorHandler.get();
}
// 从三个列表移除回调
public void removeCallback(AnimationFrameCallback callback) {
// 如果页面退出但动画没有取消
// 则callback隐式持有的Activity引用并一直保存在列表中
mCommitCallbacks.remove(callback);
mDelayedCallbackStartTime.remove(callback);
int id = mAnimationCallbacks.indexOf(callback);
if (id >= 0) {
mAnimationCallbacks.set(id, null);
mListDirty = true;
}
}
.....
}
三、总结
总结内存泄漏路径:
- ThreadLocal 在主线程持有 AnimationHandler 对象;
- AnimationHandler 持有多个 AnimationFrameCallback 列表,列表负责不同功能;
- AnimationFrameCallback 接口由 ValueAnimator 类实现,就是我们示例代码创建的 ValueAnimator;
- 创建 ValueAnimator 实例时,我们设置了监听器修改 textView 的值,该监听器隐式持有 Activity 实例;
若不结束或清除 ThreadLocal 中 AnimationHandler 的 AnimationFrameCallback 回调,AnimationFrameCallback 就是 ValueAnimator,而 Activity 引用就被 ValueAnimator 的监听器永久间接持有造成内存泄漏;