Android应用内结算:无法启动asynchronous操作,因为另一个asynchronous操作(正在进行)

我正在使用IabHelper实用程序类,正如Google的教程所推荐的那样,我正在受到这个错误的严重打击。 显然, IabHelper不能同时运行多个asynchronous操作。 我甚至设法在库存盘点仍在进行时尝试开始采购。

我已经尝试在我的主类中实现onActivityResult ,正如这里所build议的那样,但是在错误发生之前,我甚至都没有调用该方法。 然后我发现这个,但我不知道在哪里可以find这个flagEndAsync方法 – 它不在IabHelper类中。

现在我正在寻找一个方法来解决这个问题(不用重新实现整个She-Bang)。 我能想到的唯一解决scheme是创build一个boolean字段asyncActive ,在asynchronous任务启动之前检查,如果有另一个任务处于活动状态,则不执行此操作。 但是还有其他许多问题,并且不能跨活动。 此外,我宁愿有一个asynchronous任务队列,并尽快运行,而不是根本不运行。

任何解决这个问题?

一个简单的棘手的解决scheme

在调用purchaseItem方法之前,只需添加这一行

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

所以你的代码看起来就是这样

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

注意:如果从另一个包中调用它,请不要忘记在IabHelper中创buildpublic flagEndAsync()方法。

确保您在Activity的onActivityResult调用IabHelper的handleActivityResult ,而不是在Fragment的onActivityResult调用。

下面的代码片段来自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's 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."); } } 

更新:

  • 现在有一个应用内结算版本3 API (2013年的版本是什么?)
  • 代码示例已移至Github 。 上面的代码片段反映了当前的样本,但在逻辑上与之前相同。

这并不容易破解,但我find了所需的解决方法。 最近对Google非常失望,他们的Android网站变得很混乱(很难find有用的信息),他们的示例代码很差。 几年前我在做一些Android开发的时候,事情变得更加简单了! 这是另一个例子

确实IabUtil是错误的,它没有正确地closures自己的asynchronous任务。 一整套必要的解决方法来稳定这个事情:

1)使方法flagEndAsync公开。 它在那里,只是不可见。

2)让每个监听者调用iabHelper.flagEndAsync来确保程序标记正确; 这似乎是所有听众都需要的。

3)用try/catch环绕调用来捕获可能发生的IllegalStateException ,并以这种方式处理。

我最终做了类似金太郎的事情。 但是添加mHelper.flagEndAsync()到catch的结尾。 用户仍然得到敬酒,但下一次他们按下购买button,asynchronous操作已被杀死,购买button准备再次去。

 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(); } } 

我遇到同样的问题,直到我偶然发现另一个SO线程 。 我在其他线程中包含了一个修改过的代码版本,您需要在您的活动中包含该代码,以初始化购买。

 @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 // ... } 

一个简单的伎俩,为我做的是在IabHelper中创build一个方法:

 public Boolean getAsyncInProgress() { return mAsyncInProgress; } 

然后在你的代码中,只需检查:

 if (!mHelper.getAsyncInProgress()) //launch purchase else Log.d(TAG, "Async in progress already..) 

IabHelper.java文件中查找flagEndAsync()并将其设置为公共函数。

在为您的IabHelper尝试购买呼叫flagEndAsync() IabHelper

你必须像这样的代码做somthig:

 mHelper.flagEndAsync(); mHelper.launchPurchaseFlow(AboutActivity.this, SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener, "payload-string"); 

真的很烦人的问题。 这是一个快速和肮脏的解决scheme,不是完美的代码明智的,但是这是用户友好的,并避免了差评和崩溃:

 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(); } } 

这样用户只需要再次点击(最差2次)并获得计费popup窗口

希望能帮助到你

只要在活动上检查onActivityResult requestCode,如果它与购买时使用的PURCHASE_REQUEST_CODE相匹配,就把它传递给片段。

在FragmentTransaction中添加或replace片段时,只需设置一个标记:

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

然后在你的活动的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); } } } 

或者,您可以在这里获取最新的IabHelper.java文件: https : //code.google.com/p/marketbilling/source/browse/

3月15日的版本为我解决了这个问题。 (注意15日没有改变的其他文件)

当“android.test.canceled”是发送的SKU时,我仍然必须修复在testing期间发生的一个由于null意图引起的崩​​溃。 我变了:

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

至:

 int getResponseCodeFromIntent(Intent i) { Object o = i.getExtras() != null ? i.getExtras().get(RESPONSE_CODE) : null; 

我偶尔有这个问题,在我的情况下,我已经追查到,如果IabHelper中的onServiceConnected方法可以多次调用,如果基础服务断开连接并重新连接(例如由于间歇性networking连接)。

在我的情况下,具体操作是“无法启动asynchronous操作(刷新库存),因为另一个asynchronous操作(launchPurchaseFlow)正在进行”。

我的应用程序写的方式,我不能调用launchPurchaseFlow,直到我有一个完整的queryInventory,我只从我的onIabSetupFinished处理函数调用queryInventory。

IabHelper代码将调用这个处理函数,只要它的onServiceConnected被调用,这可能会发生多次。

onServiceDisconnected的Android文档说:

在与服务的连接丢失时调用。 当托pipe服务的进程崩溃或死亡时,通常会发生这种情况。 这不会删除ServiceConnection本身 – 与服务的绑定将保持活动状态,并且在下次运行服务时,您将收到对onServiceConnected(ComponentName,IBinder)的调用。

这解释了这个问题。

可以说,IabHelper不应该多次调用onIabSetupFinished的监听器函数,但是另一方面,如果我已经完成了并且得到了结果,那么通过在这个函数中不调用queryInventory来解决这个问题是微不足道的。

IabHelpr类的另一个主要问题是在多个方法中抛出RuntimeExcptions(IllegalStateException)的糟糕select。 在大多数情况下从您自己的代码中抛出RuntimeExeptions是不可取的,因为它们是未经检查的exception 。 这就像破坏你自己的应用程序一样 – 如果没有被捕获,这些exception就会冒泡,并导致应用程序崩溃。

解决scheme是实现自己的检查exception,并更改IabHelper类来抛出它,而不是IllegalStateException。 这将迫使你处理这个exception,无论它在编译时可能抛出你的代码。

这是我的自定义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); } } 

一旦我们在IabHelper类中进行了更改,我们就可以在我们调用类方法的代码中处理我们检查的exception。 例如:

 try { setUpBilling(targetActivityInstance.allData.getAll()); } catch (MyIllegalStateException ex) { ex.printStackTrace(); } 

我有同样的问题,问题是我没有实现onActivityResult的方法。

 @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); } } 

是的,我也面临这个问题,但我解决了这个问题,但我解决了使用

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

上述方法停止所有的标志。 它为我工作必须检查

如果你在代码片段,然后你在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); } 

这个答案直接解决了@Wouter看到的问题…

像许多人所说的那样, onActivityResult()必须被触发的。 但是,在某些情况下,即在运行应用程序的debugging版本时,如果按两次[BUY]button,Google的代码不会触发onActivityResult()

另外,一个主要的问题是用户可能处于不稳定的环境(例如公共汽车或地铁),并按下[BUY]button两次……突然间,你已经有了一个例外!

至lessGoogle解决了这个尴尬的例外https://github.com/googlesamples/android-play-billing/commit/07b085b32a62c7981e5f3581fd743e30b9adb4ed#diff-b43848e47f8a93bca77e5ce95b1c2d66

下面是我在实例化IabHelper的同一个类中实现的(对于我来说,这是在Application类中):

 /** * invokes the startIntentSenderForResult - which will call your activity's onActivityResult() when it's 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 } } 

我的[BUY]button调用launchPurchaseWorkflow()并传递SKU和button所在的活动(或者如果您位于片段中,则为封闭活动)

注意:一定要公开IabHelper.flagEndAsync()

希望Google能够在不久的将来改进这些代码, 这个问题是3岁左右,它仍然是一个持续的问题:(

我有同样的问题,但它解决了! 我认为你必须不在UI线程上运行“launchPurchaseFlow”,尝试在UI线程上运行launchPurchaseFlow,它会工作的很好!

 mActivity.runOnUiThread(new Runnable(){ public void run(){ mHelper.launchPurchaseFlow(mActivity, item, 10001, mPurchaseFinishedListener,username); } });