Search code examples
javaandroidexceptiongoogle-playin-app-billing

Can't catch IllegalStateException from Google's IAB framework


I have a Google Play app that uses the In App Billing v3 framework.

The Google Play Developer Console reports frequent crashes with a java.lang.IllegalStateException

As a matter of fact, the bulk of my crashes are this illegal state exception. I used the sample code from the IAB framework to handle my in app purchases.

Call stack:

  at com.steenriver.armor.util.IabHelper.flagStartAsync (IabHelper.java:824)
  at com.steenriver.armor.util.IabHelper.queryInventoryAsync (IabHelper.java:616)
  at com.steenriver.armor.util.IabHelper.queryInventoryAsync (IabHelper.java:644)
  at com.steenriver.armor.ArmorActivity$1.onIabSetupFinished (ArmorActivity.java:185)
  at com.steenriver.armor.util.IabHelper$1.onServiceConnected (IabHelper.java:262)
  at android.app.LoadedApk$ServiceDispatcher.doConnected (LoadedApk.java:1625)
  at android.app.LoadedApk$ServiceDispatcher$RunConnection.run (LoadedApk.java:1653)
  at android.os.Handler.handleCallback (Handler.java:836)
  at android.os.Handler.dispatchMessage (Handler.java:103)
  at android.os.Looper.loop (Looper.java:232)
  at android.app.ActivityThread.main (ActivityThread.java:6802)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:1103)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:964)

To get rid of the crashes, I decided to add exception handling to the code like thus:

try
{
    mHelper.launchPurchaseFlow( this, sku, RC_REQUEST, mPurchaseFinishedListener, payload );
} catch( IllegalStateException e )
{
    Log.e( TAG, e.toString() );     // Illegal State: maybe purchase is already in progress?
    enableBuyButton();              // Enable buy button so customer can try again.
}

To my amazement, the new version with the try/catch added, still crashes with the same callstack.

Is this because the IllegalStateException can't be caught at all, or something? What is going on here?


Solution

  • If you take a careful look at your call stack, you'll see that it does not include launchPurchaseFlow(). try blocks can only catch exceptions from the code that is properly within them, and since your exception is not happening inside any call to launchPurchaseFlow(), it is not happening inside your try.

    launchPurchaseFlow() causes the billing state machine to start advancing, but not every purchase operation happens within that function. In other words, the purchase completes "asynchronously".

    As you can see from the stack trace, the exception actually occurs during onServiceConnected(), which is being called from a Handler callback. launchPurchaseFlow() has actually already returned by this point; the try block is in the past. In order to surround this particular operation with a try, you'd actually need to edit an IabHelper method, such as onServiceConnected(). And this is not recommended, since it seems likely some other mistake in your own code is causing the exception. This is what should be fixed.

    Incidentally, I cannot find a version of the v3 billing library where flagStartAsync() throws an IllegalStateException, so I'm not sure what that's all about.

    Obligatory mention: IabHelper is apparently no longer supported by Google; you're supposed to use the billing client library instead. The library you're using is no longer referenced by the link you posted.