AnimatorUpdateListener是否仅应在UI线程调用?多线程调用问题咨询
AnimatorUpdateListener 是否仅应在 UI 线程中调用?
没错,AnimatorUpdateListener的回调方法必须只在UI线程执行——这是Android视图系统的硬性要求,因为所有涉及视图属性修改、重绘的操作都只能在UI线程完成,否则轻则出现UI闪烁、状态错乱,重则直接抛出CalledFromWrongThreadException异常。
为什么你的回调会被多线程调用?
大概率是你启动ValueAnimator的线程不是UI线程导致的。ValueAnimator默认会绑定到启动它的线程的Looper上,如果启动线程是子线程且带有Looper,那回调就会在那个子线程跑;如果子线程没有Looper,启动动画时还会直接崩溃。
怎么解决这个问题?
这里给你两个可靠的方案:
确保动画启动逻辑在UI线程执行
如果你的startAnimation()方法可能被子线程调用,先切换到UI线程再启动动画:class CustomView extends View { private int mStartColor; private int mEndColor; public void startAnimation() { if (Looper.myLooper() != Looper.getMainLooper()) { // 切换到UI线程执行动画逻辑 post(this::startAnimation); return; } ValueAnimator animation = ValueAnimator.ofObject(new ArgbEvaluator(), mStartColor, mEndColor); animation.setDuration(500L); animation.addUpdateListener(new CustomAnimatorUpdateListener(this)); animation.start(); } private static class CustomAnimatorUpdateListener implements ValueAnimator.AnimatorUpdateListener { private final WeakReference<CustomView> viewRef; public CustomAnimatorUpdateListener(CustomView view) { this.viewRef = new WeakReference<>(view); } @Override public void onAnimationUpdate(ValueAnimator animation) { CustomView view = viewRef.get(); if (view != null && view.isAttachedToWindow()) { int newColor = (int) animation.getAnimatedValue(); view.setBackgroundColor(newColor); } } } }强制让动画绑定UI线程的Looper
不管你在哪启动动画,直接给ValueAnimator设置主线程的Looper,这样回调一定会在UI线程触发:ValueAnimator animation = ValueAnimator.ofObject(new ArgbEvaluator(), mStartColor, mEndColor); animation.setDuration(500L); // 指定主线程Looper,确保回调在UI线程执行 animation.setLooper(Looper.getMainLooper()); animation.addUpdateListener(new CustomAnimatorUpdateListener(this)); animation.start();
额外提醒
在你的CustomAnimatorUpdateListener里,建议用WeakReference持有CustomView,避免因为动画持有视图引用导致内存泄漏——上面的示例代码已经包含了这个优化。
内容的提问来源于stack exchange,提问作者Cheng
相关产品推荐
相关产品推荐

