如何在 AnimatorListener.onAnimationEnd 中调试和解决 NPE
How to debug and resolve an NPE in AnimatorListener.onAnimationEnd
如果这很容易解决,请原谅。我对调试和解决这样的情况真的很陌生。我真的不确定从哪里开始追踪这个问题并确定所涉及的架构。它偶尔会发生,看起来更像是一种竞争条件,取决于我在上一个动画结束后尝试重新启动动画的速度。非常感谢任何指点并学习一点。
08-08 09:26:01.410 30626-30626/com.myapp.myappname E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.myapp.myappname, PID: 30626
java.lang.NullPointerException: Attempt to invoke interface method 'void android.animation.Animator$AnimatorListener.onAnimationEnd(android.animation.Animator)' on a null object reference
at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1239)
at android.animation.ValueAnimator.cancel(ValueAnimator.java:1140)
at android.animation.ObjectAnimator.animateValue(ObjectAnimator.java:974)
at android.animation.ValueAnimator.animationFrame(ValueAnimator.java:1384)
at android.animation.ValueAnimator.doAnimationFrame(ValueAnimator.java:1427)
at android.animation.ValueAnimator$AnimationHandler.doAnimationFrame(ValueAnimator.java:759)
at android.animation.ValueAnimator$AnimationHandler.run(ValueAnimator.java:801)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:920)
at android.view.Choreographer.doCallbacks(Choreographer.java:695)
at android.view.Choreographer.doFrame(Choreographer.java:628)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:906)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7229)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
这里是一些关于导致 NPE 的机制的参考代码。本质上,我有一个开始和停止按钮来注册和注销光传感器。当注册并发生灯光事件时,我允许通过锁一次调用一个并启动 handler/runnable。当动画 ends/completes 释放锁以在灯光事件处理程序中进行另一次执行。
我看到 NPE 偶尔发生的地方是当我在发出先前的 STOP 后按下 START 时。它很少发生,恰好在 STOP 发生和 START 再次发生的边界。
// on startup of app, prepare runnable
runnableAlpha = new Runnable()
{
@Override
public void run()
{
try
{
final float newAlpha = (.10f * (mLastLightValue / (mLightSensor.getMaximumRange() / 100)));
final ObjectAnimator oa = ObjectAnimator.ofFloat(mMyAppRatingBar, "alpha", mMyAppRatingBarLastAlpha, newAlpha);
oa.addListener(new AnimatorListener()
{
@Override
public void onAnimationStart(Animator animation)
{
}
@Override
public void onAnimationEnd(Animator animation)
{
try
{
oa.addListener(null);
oa.addUpdateListener(null);
mMyAppRatingBarLastAlpha = newAlpha;
}
catch (Exception e1)
{
// do nothing
}
finally
{
try
{
mTweenLock.release();
}
catch (Exception e1)
{
// do nothing
}
}
}
@Override
public void onAnimationCancel(Animator animation)
{
try
{
oa.addListener(null);
oa.addUpdateListener(null);
mMyAppRatingBarLastAlpha = newAlpha;
}
catch (Exception e1)
{
// do nothing
}
finally
{
try
{
mTweenLock.release();
}
catch (Exception e1)
{
// do nothing
}
}
}
@Override
public void onAnimationRepeat(Animator animation)
{
}
});
oa.setInterpolator(new DecelerateInterpolator());
oa.setDuration(1000);
oa.start();
try
{
if (mAlphaHandler != null)
{
mAlphaHandler.removeCallbacks(runnableAlpha);
}
}
catch (Exception e1)
{
// do nothing
}
finally
{
mAlphaHandler = null;
}
}
catch(Exception e)
{
try
{
mTweenLock.release();
}
catch (Exception e1)
{
// do nothing
}
}
}
}
// wire up handler and runnable only if light sensor registered, light event triggered, no current lock(prior light event being handled)
public void onSensorChanged(SensorEvent event)
{
if(!mTweenLock.tryAcquire())
{
return;
}
try
{
mAlphaHandler = new Handler();
mLastLightValue = event.values[0];
mAlphaHandler.postDelayed(runnableAlpha, 0);
}
catch(Exception e)
{
mAlphaHandler = null;
mTweenLock.release();
}
}
// when we press START button in app, fire up the light sensor to begin work
mSensorManager = (SensorManager) mContext.getSystemService(mContext.SENSOR_SERVICE);
mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
if(mLightSensor == null)
{
mSensorManager = null;
return;
}
mSensorManager.unregisterListener(this, mLightSensor); // clear out prior if existent
mSensorManager.registerListener(this, mLightSensor, SensorManager.SENSOR_DELAY_UI);
// when we press STOP button in app, we force teardown of everything
try
{
if(mAlphaHandler != null)
{
mAlphaHandler.removeCallbacksAndMessages(null);
mAlphaHandler = null;
runnableAlpha = null;
}
}
catch(Exception e1)
{
// do nothing
}
问题原来是 .onAnimationCancel() 被间歇性地调用,其中 'oa' 被设置为 null 并且 .onAnimationEnd() 随后在 .onAnimationCancel() 之后被调用,这就是架构的工作原理。调用 .onAnimationEnd 时,'oa' 在那一点上为 NULL,导致此 NPE。由于 .onAnimationEnd() 保证在这两种情况下都会被调用,因此 .onAnimationCancel() 被拉出以解决 NPE。**
如果这很容易解决,请原谅。我对调试和解决这样的情况真的很陌生。我真的不确定从哪里开始追踪这个问题并确定所涉及的架构。它偶尔会发生,看起来更像是一种竞争条件,取决于我在上一个动画结束后尝试重新启动动画的速度。非常感谢任何指点并学习一点。
08-08 09:26:01.410 30626-30626/com.myapp.myappname E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.myapp.myappname, PID: 30626
java.lang.NullPointerException: Attempt to invoke interface method 'void android.animation.Animator$AnimatorListener.onAnimationEnd(android.animation.Animator)' on a null object reference
at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1239)
at android.animation.ValueAnimator.cancel(ValueAnimator.java:1140)
at android.animation.ObjectAnimator.animateValue(ObjectAnimator.java:974)
at android.animation.ValueAnimator.animationFrame(ValueAnimator.java:1384)
at android.animation.ValueAnimator.doAnimationFrame(ValueAnimator.java:1427)
at android.animation.ValueAnimator$AnimationHandler.doAnimationFrame(ValueAnimator.java:759)
at android.animation.ValueAnimator$AnimationHandler.run(ValueAnimator.java:801)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:920)
at android.view.Choreographer.doCallbacks(Choreographer.java:695)
at android.view.Choreographer.doFrame(Choreographer.java:628)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:906)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7229)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
这里是一些关于导致 NPE 的机制的参考代码。本质上,我有一个开始和停止按钮来注册和注销光传感器。当注册并发生灯光事件时,我允许通过锁一次调用一个并启动 handler/runnable。当动画 ends/completes 释放锁以在灯光事件处理程序中进行另一次执行。
我看到 NPE 偶尔发生的地方是当我在发出先前的 STOP 后按下 START 时。它很少发生,恰好在 STOP 发生和 START 再次发生的边界。
// on startup of app, prepare runnable
runnableAlpha = new Runnable()
{
@Override
public void run()
{
try
{
final float newAlpha = (.10f * (mLastLightValue / (mLightSensor.getMaximumRange() / 100)));
final ObjectAnimator oa = ObjectAnimator.ofFloat(mMyAppRatingBar, "alpha", mMyAppRatingBarLastAlpha, newAlpha);
oa.addListener(new AnimatorListener()
{
@Override
public void onAnimationStart(Animator animation)
{
}
@Override
public void onAnimationEnd(Animator animation)
{
try
{
oa.addListener(null);
oa.addUpdateListener(null);
mMyAppRatingBarLastAlpha = newAlpha;
}
catch (Exception e1)
{
// do nothing
}
finally
{
try
{
mTweenLock.release();
}
catch (Exception e1)
{
// do nothing
}
}
}
@Override
public void onAnimationCancel(Animator animation)
{
try
{
oa.addListener(null);
oa.addUpdateListener(null);
mMyAppRatingBarLastAlpha = newAlpha;
}
catch (Exception e1)
{
// do nothing
}
finally
{
try
{
mTweenLock.release();
}
catch (Exception e1)
{
// do nothing
}
}
}
@Override
public void onAnimationRepeat(Animator animation)
{
}
});
oa.setInterpolator(new DecelerateInterpolator());
oa.setDuration(1000);
oa.start();
try
{
if (mAlphaHandler != null)
{
mAlphaHandler.removeCallbacks(runnableAlpha);
}
}
catch (Exception e1)
{
// do nothing
}
finally
{
mAlphaHandler = null;
}
}
catch(Exception e)
{
try
{
mTweenLock.release();
}
catch (Exception e1)
{
// do nothing
}
}
}
}
// wire up handler and runnable only if light sensor registered, light event triggered, no current lock(prior light event being handled)
public void onSensorChanged(SensorEvent event)
{
if(!mTweenLock.tryAcquire())
{
return;
}
try
{
mAlphaHandler = new Handler();
mLastLightValue = event.values[0];
mAlphaHandler.postDelayed(runnableAlpha, 0);
}
catch(Exception e)
{
mAlphaHandler = null;
mTweenLock.release();
}
}
// when we press START button in app, fire up the light sensor to begin work
mSensorManager = (SensorManager) mContext.getSystemService(mContext.SENSOR_SERVICE);
mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
if(mLightSensor == null)
{
mSensorManager = null;
return;
}
mSensorManager.unregisterListener(this, mLightSensor); // clear out prior if existent
mSensorManager.registerListener(this, mLightSensor, SensorManager.SENSOR_DELAY_UI);
// when we press STOP button in app, we force teardown of everything
try
{
if(mAlphaHandler != null)
{
mAlphaHandler.removeCallbacksAndMessages(null);
mAlphaHandler = null;
runnableAlpha = null;
}
}
catch(Exception e1)
{
// do nothing
}
问题原来是 .onAnimationCancel() 被间歇性地调用,其中 'oa' 被设置为 null 并且 .onAnimationEnd() 随后在 .onAnimationCancel() 之后被调用,这就是架构的工作原理。调用 .onAnimationEnd 时,'oa' 在那一点上为 NULL,导致此 NPE。由于 .onAnimationEnd() 保证在这两种情况下都会被调用,因此 .onAnimationCancel() 被拉出以解决 NPE。**