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
You can keep up to date with the inapp v3 API development at https://code.google.com/p/marketbilling/
The code there is newer than the one that is available through the Android SDK Manager.
There were many problems with IABHelper.java.
Firstly - the version that is downloaded by the SDK Manager is not kept up to date. Use the version found here: https://code.google.com/p/marketbilling/source/detail?r=15946261ec9ae5f7c664d720f392f7787e3ee6c7 It is the most up to date version as of posting this answer. Many issues seem to have been fixed with this version compared to the initial release which comes from the SDK Manager.
Only thing I've had to change in this version is adding flagEndAsync();
after line 404 which fixes an IllegalStateException when 2 IAB purchase flows are launched in quick succession.
With this version you do not need to manage using flagEndAsync();
in your files and can leave the method to not be public.
Make sure you are implementing the IabHelper.han.handleActivityResult(requestCode, resultCode, data)
method in your activities onActivityResult
method.
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Pass on the activity result to the helper for handling
if (!mIabHelper.handleActivityResult(requestCode, resultCode, data)) {
// not handled, so handle it ourselves (here's where you'd
// perform any handling of activity results not related to in-app
// billing...
super.onActivityResult(requestCode, resultCode, data);
} else {
Log.i(TAG, "onActivityResult handled by IABUtil.");
}
}
I believe that there are still two bugs in the Android code, which explains why you still see the error. Notice that the call stack is on a stand-alone thread. But the code that sets mSetupDone (IabHelper) to true executes on the main UI thread. Java does not guarantee that data changed by one thread will be visible to the other thread due to CPU caching unless you declare the variable with the volatile keyword. Thus it is possible that it was setup (mSetupDone == true), but that the new value of mSetupDone is cached on UI thread, not yet visible to this thread in your call stack. So that thread still sees mSetupDone == false.
I tried to fix this by declaring mSetupDone with volatile, and also every other non-final field of IabHelper just to be safe.
Now the other problem is the .dispose() function. It does not stop the ongoing threads. This means that it can set mSetupDone to false while one of the worker threads is running. If you look at queryInventoryAsync(), you will see it does check that mSetupDone is true. And based on your call stack, it did get past that. Then it crashed later with mSetupDone == false. Only way that could happen is if dispose() were called while your thread was in flight. Fix is that dispose() needs to signal threads to just silently bail out instead of continuing and throwing errors when it sees mSetupDone == false. This also prevents yet another problem with IabHelper where disposed instances call listener callbacks even after being disposed! It's a bit complicated to explain line by line here, but hopefully thus gets you pointed in the right direction.
I get the same errors. I also ran into other issues...
Having more than one google account on a device disabled in-app billing on my Galaxy Tab 7. Remove one account re-enabled it.
I've seen issues with the in app billing sample code on a GT-P5110, LGL75C, and GT-S5839i among others.
(I use the code in an application with ACRA installed... so everytime it crashes, I get info)
The devices Android version ranges from 2.3.3 to 4.0.4.
It's very annoying.