3

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

with the message that

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

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);
4

3 回答 3

9

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

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

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

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

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

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

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

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

于 2013-07-31T01:51:57.123 回答
0

卡尔的好东西。我“认为”我看到了类似的事情,尽管我的应用程序通过以下方式崩溃:

java.lang.IllegalStateException: IabHelper was disposed of, so it cannot be used.

在我的情况下,以一种方式旋转设备,然后立即返回原始方向有时会导致此崩溃。似乎有一个“可能”发生这种崩溃的“时间窗口”(由于卡尔解释的 IAB 的异步性质)。

我的修复

我的“修复”是使 mHelper 静态,并且只实例化它if (mHelper == null),而不是在活动的 onDestroy() 方法中销毁它。这样,一旦设置好,它就会一直存在,并且不再需要担心异步操作(由设备方向引起)。

不确定这是否是正确的解决方案,但我想我会提到它以防它帮助其他人。

于 2013-08-14T04:03:38.577 回答
-1

只需使 checkNotDisposed() 方法同步,问题就会消失。这是因为它有时会在单独的线程中调用,并不总是具有最新的 mDisposed 值,这可能已在主线程中设置:

private synchronized void checkNotDisposed()
于 2016-07-24T13:13:02.187 回答