Google 应用内结算导致异常

2022-01-20 00:00:00 google-play in-app-billing eclipse

在我的一个 Android 应用程序中,我试图从 Google 的应用内计费中简单地抓取库存,但它总是在

In one of my Android apps, I'm trying to implement a simple grab of the inventory from Google's In-App billing, but it keeps giving me errors at the line of

mHelper.queryInventoryAsync(mGotInventoryListener);

带有消息

IabHelper 未设置.无法执行操作:queryInventory

IabHelper is not setup. Can't perform operation: queryInventory

这是所有 IabHelper 代码.

Here is all the IabHelper code.

    mHelper = new IabHelper(this, base64EncodedPublicKey);
    mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
           public void onIabSetupFinished(IabResult result) {
              if (!result.isSuccess()) {
                 // Oh noes, there was a problem.
                 Log.d(TAG, "Problem setting up In-app Billing: " + result);
              }            
                 // Hooray, IAB is fully set up!  
           }
        });
    //check to see if ads have been removed(bought)
    IabHelper.QueryInventoryFinishedListener mGotInventoryListener 
       = new IabHelper.QueryInventoryFinishedListener() {
       public void onQueryInventoryFinished(IabResult result,
          Inventory inventory) {

          if (result.isFailure()) {
            // handle error here
          }
          else {
            // does the user have the premium upgrade?
            if(inventory.hasPurchase(REMOVE_ADS)) removeAdsPurchased = true;     
            // update UI accordingly
          }
       }
    };
    mHelper.queryInventoryAsync(mGotInventoryListener);

推荐答案

简短的回答是您的 queryInventoryAsync() 调用应该从您的 onIabSetupFinished() 方法内部进行.这是一个异步调用,因此在调用该回调告诉您助手与计费服务的通信已建立之前,您不能只继续使用 IabHelper 实例.当前编写代码的方式,您有一个竞争条件,并且您的 queryInventoryAsync() 调用将赢得该竞争并在 IabHelper 对象设置之前尝试使用它,这就是您的问题的原因.

The short answer is that your queryInventoryAsync() call should be made from inside your onIabSetupFinished() method. This is an asynchronous call, and so you cannot just proceed with using the IabHelper instance until that callback has been invoked to tell you that the helper's communication with the billing service has been established. The way your code is presently written, you have a race condition, and your queryInventoryAsync() call is going to win that race and attempt to use the IabHelper object before it has been set up, which is the cause of your problem.

此外,依赖此对象的 UI 处理程序中的任何其他代码(例如,启动购买的按钮的处理程序)都应测试完全设置的 IabHelper 对象,并且不应允许用户使用该 UI元素,直到在 onCreate() 中创建的 IabHelper 实例成功完成设置.处理这种情况的最简单方法是简单地禁用此类 UI 元素,直到调用设置回调以指示设置已成功完成.

Also, any further code in UI handlers that relies upon this object (e.g., the handler for a button that initiates a purchase) should test for a fully set-up IabHelper object, and should not allow the user to use that UI element until the IabHelper instance created in onCreate() has successfully completed setup. The easiest way to handle this situation is to simply disable such UI elements until the setup callback has been invoked to indicate that setup has completed successfully.

这是最简单的部分.更严重的问题发生在 onCreate() 方法运行后立即发生的操作(即不受用户控制),这需要使用完全设置的 IabHelper 实例.这通常是活动生命周期调用的结果 - 特别是 onResume() (如果有需要 IabHelper 实例的事情,必须在您的应用程序每次进入前台时完成,而不是在 onCreate 时只是() 被调用),最值得注意的是,在 onActivityResult() 中(当用户完成或中止与计费界面的交互时调用 - 例如,作为进行应用内支付的一部分).

That is the easy part. The more serious problems occur when you have actions that occur immediately after your onCreate() method runs (i.e., not under the control of the user), that require the use of a fully set-up IabHelper instance. This typically occurs as a result of activity lifecycle calls - specifically, onResume() (if there is something requiring an IabHelper instance that must be done each time your app comes to the foreground, and not just when onCreate() is called) and, most notably, in onActivityResult() (which is invoked when the user completes or aborts an interaction with the billing interface - e.g., as part of making an in-app payment).

问题是您的应用程序可能会被操作系统停止(例如,当用户开始购买时为计费界面本身腾出空间),导致您的 IabHelper 实例与您的应用程序一起被销毁,并且该实例将下次调用 onCreate() 时必须重新生成,并且设置将再次在 onCreate() 中启动,并且您需要再次等待设置完成,然后再对该对象执行任何其他操作.

The problem is that your app may be stopped by the OS (e.g., to make room for the billing interface itself when the user initiates a purchase), causing your IabHelper instance to be destroyed along with your app, and that instance will have to be regenerated when your onCreate() is next invoked, and setup will once again be initiated in onCreate(), and once again you will need to wait for setup to complete before doing anything else with that object.

可能发生这种情况的一个值得注意的情况是在购买过程中用户与计费界面的交互过程中.该交互的结果将通过 onActivityResult() 传递给您的应用程序,该程序本身需要使用完全设置的 IabHelper 对象,因此如果您的应用程序在用户与计费服务交互时从内存中刷新(或取消)购买,然后 onActivityResult() 将不得不等待 IabHelper 实例重新设置(在它在 onCreate() 中重新创建之后才能使用它.

One notable situation where this can occur is during the user's interaction with the billing interface as part of the purchase process. The result of that interaction will be communicated to your app via onActivityResult(), which itself needs to use a fully set-up IabHelper object, and so if your app gets flushed from memory while the user is interacting with the billing service to make (or cancel) a purchase, then onActivityResult() will have to wait for the IabHelper instance to get set up again (after it is re-created in onCreate() before it can use it.

处理此问题的一种方法是设置并发布到需要 IabHelper 实例的待处理操作队列,您的 onResume() 和/或 onActivityResult() 代码添加到该队列,并由您的 onIabSetupFinished() 方法,一旦 IabHelper 设置(由 onCreate() 启动)完成.

One way to handle this would be to set up and post to a queue of pending actions requiring an IabHelper instance, that your onResume() and/or onActivityResult() code adds to, and have that queue processed by your onIabSetupFinished() method once the IabHelper setup (initiated by onCreate()) has completed.

这不是微不足道的.上次我检查时,TrivialDrive 示例应用程序没有处理上述情况.

It's not trivial. And last time I checked, the TrivialDrive sample app did not handle the above situation.

测试这种用例的最佳方法是使用开发人员选项不保留活动",这会导致您的应用在每次用户离开时被销毁,以模拟操作系统在它发生时会做什么需要回收内存,以便您可以确保您的应用在这些条件下工作.

The best way to test this kind of use case is to use the developer option "Don't Keep Activities," which causes your app to be destroyed each time the user leaves it, to simulate what the OS will do when it needs to reclaim memory, so that you can make sure your app works under those conditions.

相关文章