Payment for Android in the application: cannot start the asynchronous operation because another async operation is in progress (in progress)

I use the classes of the IabHelper utility, as recommended in the Google tutorial, and I suffered a lot from this error. Apparently, IabHelper cannot run multiple asynchronous operations at the same time. I even managed to hit him, trying to start the purchase, while the inventory had not yet begun.

I already tried to implement onActivityResult in my main class, as suggested here , but I donโ€™t even get a call to this method before the error gets there. Then I found this one , but I donโ€™t know where to find this flagEndAsync method - this is not in the IabHelper class .

Now I'm looking for a way around this (without rebuilding my whole life). The only solution I can think of is to create an asyncActive boolean field that is checked before running the async task, and not do it if there is another active task. But it has many other problems, and it does not work through activity. In addition, I would prefer that the async task queue be started and started as soon as it is allowed, rather than not starting at all.

Any solutions for this problem?

+66
android android-billing
Mar 22 '13 at 16:42
source share
17 answers

Simple complex solution

before calling the purchaseItem method just add this line

  if (billingHelper != null) billingHelper.flagEndAsync(); 

so that your code looks like this.

  if (billingHelper != null) billingHelper.flagEndAsync(); purchaseItem("android.test.purchased"); 

Note. Remember to make the public EndAsync () flag in IabHelper if you call it from another package.

+98
May 03 '13 at 12:35
source share
โ€” -

Make sure you call IabHelper handleActivityResult in Activity onActivityResult and NOT in the onActivityResult fragment.

The following code snippet from TrivialDrive MainActivity :

 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data); if (mHelper == null) return; // Pass on the activity result to the helper for handling if (!mHelper.handleActivityResult(requestCode, resultCode, data)) { // not handled, so handle it ourselves (here where you'd // perform any handling of activity results not related to in-app // billing... super.onActivityResult(requestCode, resultCode, data); } else { Log.d(TAG, "onActivityResult handled by IABUtil."); } } 

Update:

  • There is currently an In-app Billing Version 3 API (what was the version in 2013?)
  • Sample code has moved to Github . The snippet above has been edited to reflect the current pattern, but logically the same as before.
+71
Dec 10 '13 at 2:59
source share

It was not easy to crack, but I found the necessary workarounds. Recently, Google has been disappointed, their Android websites have become a mess (itโ€™s very difficult to find useful information), and their sample code is bad. When I was developing Android a few years ago, things got a lot easier! This is another example of this ...

Indeed, IabUtil is a bug; it incorrectly cancels its own async tasks. A complete set of necessary workarounds to stabilize this thing:

1) make the flagEndAsync method public. It's there, just not visible.

2) have each call to the iabHelper.flagEndAsync listener to make sure that the procedure is marked correctly; it seems necessary in all listeners.

3) combine the calls with try/catch to catch an IllegalStateException that might occur and handle it that way.

+40
Mar 23 '13 at 3:12
source share

In the end, I did something similar to Kintaro. But added mHelper.flagEndAsync () to the end of catch. The user still receives the toast, but the next time they press the buy button, the async operation was killed and the buy button is ready to return again.

 if (mHelper != null) { try { mHelper.launchPurchaseFlow(this, item, RC_REQUEST, mPurchaseFinishedListener, ""); } catch(IllegalStateException ex){ Toast.makeText(this, "Please retry in a few seconds.", Toast.LENGTH_SHORT).show(); mHelper.flagEndAsync(); } } 
+12
Feb 02 '14 at 0:56
source share

I had the same problem until I came across another SO thread . I include the affected version of the code found in another thread that needs to be included in its activity, which initializes the purchase.

 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // Pass on the activity result to the helper for handling // NOTE: handleActivityResult() will update the state of the helper, // allowing you to make further calls without having it exception on you if (billingHelper.handleActivityResult(requestCode, resultCode, data)) { Log.d(TAG, "onActivityResult handled by IABUtil."); handlePurchaseResult(requestCode, resultCode, data); return; } // What you would normally do // ... } 
+9
Aug 16 '13 at 10:14
source share

The simple trick that did this for me was to create a method in IabHelper:

 public Boolean getAsyncInProgress() { return mAsyncInProgress; } 

and then in your code just check:

 if (!mHelper.getAsyncInProgress()) //launch purchase else Log.d(TAG, "Async in progress already..) 
+7
04 Oct '14 at 15:35
source share

Find flagEndAsync() inside the IabHelper.java file and make it public .

Before you try buying flagEndAsync() for your IabHelper .

you should do somthig like this code:

 mHelper.flagEndAsync(); mHelper.launchPurchaseFlow(AboutActivity.this, SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener, "payload-string"); 
+7
Sep 19 '16 at 2:30
source share

Really annoying problem. Here is a quick and dirty solution that is not perfect code wise, but it is more user friendly and avoids bad ratings and crashes:

 if (mHelper != null) { try { mHelper.launchPurchaseFlow(this, item, RC_REQUEST, mPurchaseFinishedListener, ""); } catch(IllegalStateException ex){ Toast.makeText(this, "Please retry in a few seconds.", Toast.LENGTH_SHORT).show(); } } 

Thus, the user just needs to click another time (in the worst case, 2 times) and get a pop-up window for calculation.

Hope this helps

+5
Jul 23 '13 at 12:20
source share

Just check the onActivityResult request code for activity and, if it matches the PURCHASE_REQUEST_CODE that you used when purchasing, just pass it to the snippet.

When you add or replace a fragment in a FragmentTransaction, just set the tag:

 fTransaction.replace(R.id.content_fragment, fragment, fragment.getClass().getName()); 

Then by your activity onActivityResult

 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if(requestCode == PurchaseFragment.PURCHASE_REQUEST_CODE) { PurchaseFragment fragment = getSuportFragmentManager().findFragmentByTag(PurchaseFragment.class.getNAme()); if(fragment != null) { fragment.onActivityResult(requestCode, resultCode, data); } } } 
+3
Apr 02
source share

Or you can get the latest IabHelper.java file here: https://code.google.com/p/marketbilling/source/browse/

The March 15 version fixed this for me. (Please note that other files were unchanged on the 15th)

I still had to fix one crash that occurred during testing caused by unnecessary intentions when "android.test.canceled" was sent by SKU. I changed:

 int getResponseCodeFromIntent(Intent i) { Object o = i.getExtras().get(RESPONSE_CODE); 

at

 int getResponseCodeFromIntent(Intent i) { Object o = i.getExtras() != null ? i.getExtras().get(RESPONSE_CODE) : null; 
+1
Jul 11 '13 at 21:37
source share

I had this problem sometimes, and in my case I tracked it to the point that if the onServiceConnected method in IabHelper can be called more than once if the underlying service is disconnected and reconnected (for example, due to an intermittent network connection).

The specific operations in my case were: "Cannot start the asynchronous operation (update inventory) because another async (launchPurchaseFlow) operation is being performed."

As my application is written, I cannot call launchpurchaseFlow until I have completed queryInventory, and I only call queryInventory from my onIabSetupFinished function of the handler.

The IabHelper code will call this handler function whenever its onServiceConnected is called, which can happen more than once.

Android documentation for onServiceDisconnected says:

Called when the connection to the Service is lost. This usually happens when the service hosting process crashed or was killed. This does not eliminate the ServiceConnection - this service binding will remain active and you will receive a call to onServiceConnected (ComponentName, IBinder) when the next service is running.

which explains the problem.

Perhaps IabHelper should not call the listener's onIabSetupFinished function more than once, but, on the other hand, it was trivial to fix the problem in my application by simply not calling queryInventory from this function, if I already did this and got the results.

+1
Jun 19 '14 at 11:18
source share

Another major issue with the IabHelpr class is the poor selection of RuntimeExcptions (IllegalStateException) metadata in several ways. Throwing RuntimeExceptions from your own code is in most cases undesirable due to the fact that they are unchecked exceptions. This is like sabotaging your own application - if not caught, these exceptions will bubble and crash your application.

The solution to this is to implement your own checked exception and change the IabHelper class to use it instead of the IllegalStateException. This will force you to handle this exception wherever it can be thrown into your code at compile time.

Here is my usual 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 changes to 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(); } 
+1
Nov 02 '14 at 1:02
source share

if you are code in a fragment, then you are this code in IabHelper.java

 void flagStartAsync(String operation) { if (mAsyncInProgress) { flagEndAsync(); } if (mAsyncInProgress) throw new IllegalStateException("Can't start async operation (" + operation + ") because another async operation(" + mAsyncOperation + ") is in progress."); mAsyncOperation = operation; mAsyncInProgress = true; logDebug("Starting async operation: " + operation); } 
+1
Jan 21 '16 at 13:05
source share

I had the same problem and the problem was that I did not implement the onActivityResult method.

 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { try { if (billingHelper == null) { return; } else if (!billingHelper.handleActivityResult(requestCode, resultCode, data)) { super.onActivityResult(requestCode, resultCode, data); } } catch (Exception exception) { super.onActivityResult(requestCode, resultCode, data); } } 
0
Mar 22 '14 at 17:13
source share

Yes, I also face this problem, but I solved it, but I decided to use

 IabHelper mHelpermHelper = new IabHelper(inappActivity, base64EncodedPublicKey); mHelper.flagEndAsync(); 

The above method stops all flags. His work must check for me

0
Apr 03 '14 at 20:00
source share

This answer directly addresses the issue that @Wouter saw ...

It is true that onActivityResult() should be run, as many people have said. However, the error is that in some cases the Google code does not run onActivityResult() , i.e. When you double-click the [BUY] button when starting the debug build of your application.

In addition, one of the main problems is that the user may be in an unstable environment (for example, in a bus or metro) and presses the [BUY] button twice ... suddenly you have an exception!

At least Google fixed this awkward exception https://github.com/googlesamples/android-play-billing/commit/07b085b32a62c7981e5f3581fd743e30b9adb4ed#diff-b43848e47f8a93bca77e5ce95b1c2d66

Below is what I implemented in the same class where IabHelper is created (for me this refers to the Application class):

 /** * invokes the startIntentSenderForResult - which will call your activity onActivityResult() when it finished * NOTE: you need to override onActivityResult() in your activity. * NOTE2: check IAB code updates at https://github.com/googlesamples/android-play-billing/tree/master/TrivialDrive/app/src/main/java/com/example/android/trivialdrivesample/util * @param activity * @param sku */ protected boolean launchPurchaseWorkflow(Activity activity, String sku) { if (mIabIsInitialized) { try { mHelper.launchPurchaseFlow( activity, sku, Constants.PURCHASE_REQUEST_ID++,// just needs to be a positive number and unique mPurchaseFinishedListener, Constants.DEVELOPER_PAYLOAD); return true;//success } catch (IllegalStateException e) { mHelper.flagEndAsync(); return launchPurchaseWorkflow(activity, sku);//recursive call } } else { return false;//failure - not initialized } } 

My [BUY] button calls this launchPurchaseWorkflow() and passes the SKU and the activity the button is in (or if you are in the fragment that enters the action)

NOTE: IabHelper.flagEndAsync() sure to make IabHelper.flagEndAsync() publicly available.

Hope Google improves this code in the near future; this problem is about 3 years old and it is still a problem :(

0
Mar 27 '16 at
source share

I have the same problem, but it is solved! I think you should not launch "launchPurchaseFlow" in the user interface thread, try launchPurchaseFlow in the user interface thread, it will work fine!

 mActivity.runOnUiThread(new Runnable(){ public void run(){ mHelper.launchPurchaseFlow(mActivity, item, 10001, mPurchaseFinishedListener,username); } }); 
-2
Mar 15 '14 at 2:49
source share



All Articles