Please excuse me if this is simple to resolve. I'm really new to debugging and resolving situations like this. I'm really not sure where to begin to start tracking down this issue and sure of the architecture involved. It happens sporadically and seems more of like a race condition based on how fast I try to refire up the animation after the previous animation ended. Greatly appreciate any pointers and learn a little.
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$1.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)
Here is some reference code regarding the mechanics of what is causing the NPE. Essentially I have a start and a stop button to register and unregister a light sensor. When registered and a light event happens I allow one call at a time via a lock and startup a handler/runnable. When the animation ends/completes the lock is released for another execution in the light event handler.
Where I see the NPE happening sporadically is when I go to press START after a prior STOP was issued. It happens rarely, right at the boundary of a STOP happening and START happening again.
// 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
}