I have setup In-App Billing for the first time using the new v3 API. It is working correctly on my devices but I have received a lot of error reports from other users.
O
If all above fail to help you, give it a go to try to analyse your code a little - is your IabHelper
really set up at the time you're calling it?
I found myself doing it slightly wrong without realising it. A simple example of using it wrong in Activity.onCreate()
m_iabHelper = new IabHelper(this, base64EncodedPublicKey); // Declare
m_iabHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { // Setup
public void onIabSetupFinished(IabResult result) {
// Setup code
}
}
// Don't do this, will produce an error
List additionalSkuList = new ArrayList();
additionalSkuList.add(SKU_MYSKU);
m_iabHelper.queryInventoryAsync(true, additionalSkuList, m_queryFinishedListener);
// Don't do this, will produce an error
.
Above will honour you with the "IAB helper is not set up" error since while your app tries to execute the m_iabHelper.queryInventoryAsync()
, the IabHelper it is not set up yet. Consider using these functions in onIabSetupFinished()
or somewhere after that function is called (eg. outside the onCreate()
)
As @Martin explained, there was a bug in the Google In-App Billing example which caused this.
However, after fixing it, I was still receiving some errors in internal calls (queryInventory
inside the thread created in queryInventoryAsync
in some rare cases reports that the helper is not setup). I have solved this by adding an additional catch in that case:
try {
inv = queryInventory(querySkuDetails, moreSkus);
}
catch (IabException ex) {
result = ex.getResult();
}
catch(IllegalStateException ex){ //ADDED THIS CATCH
result = new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Helper is not setup.");
}
I also got a crash on mHelper.dispose()
which I fixed in a similar way:
try{
if (mContext != null) mContext.unbindService(mServiceConn);
}
catch(IllegalArgumentException ex){ //ADDED THIS CATCH
//IGNORE IT - somehow, the service was already unregistered
}
Of course, instead of ignoring these errors you can silently log them to ACRA, for example :)
Thanks for all your comments.
In addition to @DavidM and @Ereza.
Another major issue with the IabHelpr class is the poor choice of throwing RuntimeExcptions (IllegalStateException) in multiple methods. Throwing RuntimeExeptions from your own code in most cases is not desirable due to the fact that they are unchecked exceptions. That is like sabotaging your own application- if not caught, these exceptions will bubble up and crash your app.
The solution to this is to implement your own checked exception and change the IabHelper class to throw it, instead of the IllegalStateException. That will force you to handle this exception everywhere it could be thrown in your code at compile time.
Here is my custom exception:
public class MyIllegalStateException extends Exception {
private static final long serialVersionUID = 1L;
//Parameterless Constructor
public MyIllegalStateException() {}
//Constructor that accepts a message
public MyIllegalStateException(String message)
{
super(message);
}
}
Once we make the changes in the IabHelper class, we can handle our checked exception in our code where we call the class methods. For example:
try {
setUpBilling(targetActivityInstance.allData.getAll());
} catch (MyIllegalStateException ex) {
ex.printStackTrace();
}
There is a bug in IABHelper. The return line in the exception handler is missing, meaning it drops through and calls the success hanlder - however, mSetupDone has not been set, so further calls to the API fail. Add the return statement in as below - this will still fail, but the failure will be correctly reported to your app so you can take appropriate action.
catch (RemoteException e) {
if (listener != null) {
listener.onIabSetupFinished(new IabResult(IABHELPER_REMOTE_EXCEPTION,
"RemoteException while setting up in-app billing."));
}
e.printStackTrace();
return; // This return line is missing
}
if (listener != null) {
listener.onIabSetupFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup successful."));
}
I found out! It's about the version of the user's Google Play Store app. In-app billing V3 needs 3.9.16 or higher (http://developer.android.com/google/play/billing/versions.html). I used an older version and I received that error, now on 4.4.21 its ok!
I'm getting that EXACT same error with pretty-much the exact same code.
Only seems to be happening on certain handsets (actually, it seems almost exclusively the Acer Iconia Tablet in recent error reports!!) - and I am handling onActivityResult...
There are a number of errors in the Google v3 Billing Sample which can cause ANRs/FCs - I suspect this is just another one (shoddy code and shoddy docs are becoming a Google trademark - sadly).
My guess is - for now - that we need to allow for either mHelper or mGotInventoryListener being null and just disable In-App Billing in that case (as if result.isSuccess() was false, basically)
p.s. editted to add - it could just be that the user has an out-of-date version of the Play Store - that only auto-updates if they allow it to run!?