Android In-App Billing v3:“无法执行操作:queryInventory”

时间:2012-12-25 00:12:59

标签: android in-app-billing

我首次使用新的v3 API设置了In-App Billing。它在我的设备上正常工作,但我收到了很多来自其他用户的错误报告。

其中一个是:

java.lang.IllegalStateException: IAB helper is not set up. Can't perform operation: queryInventory
    at my.package.util.iab.IabHelper.checkSetupDone(IabHelper.java:673)
    at my.package.util.iab.IabHelper.queryInventory(IabHelper.java:462)
    at my.package.util.iab.IabHelper$2.run(IabHelper.java:521)
    at java.lang.Thread.run(Thread.java:1019)

另一个是:

java.lang.NullPointerException
    at my.package.activities.MainActivity$4.onIabSetupFinished(MainActivity.java:159)
    at my.package.util.iab.IabHelper$1.onServiceConnected(IabHelper.java:242)

我的活动实施遵循Google的示例代码(所有引用的类都不受示例影响):

IabHelper mHelper;

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    //...

    mHelper = new IabHelper(this, IAB_PUBLIC_KEY);
    mHelper.enableDebugLogging(true);

    mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
        public void onIabSetupFinished(IabResult result) {
            if (!result.isSuccess()) {
                // Oh noes, there was a problem.
                return;
            }

            // Hooray, IAB is fully set up. Now, let's get an inventory of
            // stuff we own.
            mHelper.queryInventoryAsync(mGotInventoryListener); //***(1)***
        }
    });
}

// Listener that's called when we finish querying the items we own
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
    public void onQueryInventoryFinished(IabResult result,
            Inventory inventory) {
        if (!result.isFailure()) {
            if (inventory.hasPurchase(SoundsGlobals.IAB_SKU_PREMIUM)){
                //we are premium, do things
            }
        }
        else{
            //oops
        }
    }
};

@Override
protected void onDestroy() {
    if (mHelper != null) {
        mHelper.dispose();
        mHelper = null;
    }
    super.onDestroy();
}

我认为这两个错误都来自标记为***(1)***

的行

这些错误的原因是什么?如果我仅在queryInventoryAsync内拨打onIabSetupFinishedmHelper如何为空,或mHelper未设置?

有没有人知道这方面的解决方案?

11 个答案:

答案 0 :(得分:19)

正如@Martin解释的那样,Google应用内结算示例中存在一个导致此问题的错误。

但是,在修复它之后,我仍然在内部调用中收到一些错误(queryInventoryqueryInventoryAsync中创建的线程中,在一些极少数情况下报告帮助程序没有设置)。我在这种情况下通过添加额外的捕获来解决这个问题:

try {
    inv = queryInventory(querySkuDetails, moreSkus);
}
catch (IabException ex) {
    result = ex.getResult();
}
catch(IllegalStateException ex){ //ADDED THIS CATCH
    result = new IabResult(BILLING_RESPONSE_RESULT_ERROR, "Helper is not setup.");
}

我在mHelper.dispose()上遇到了崩溃,我以类似的方式解决了这个问题:

try{
    if (mContext != null) mContext.unbindService(mServiceConn);
}
catch(IllegalArgumentException ex){ //ADDED THIS CATCH
    //IGNORE IT - somehow, the service was already unregistered
}

当然,您可以静默地将它们记录到ACRA,而不是忽略这些错误,例如:)

感谢您的所有评论。

答案 1 :(得分:14)

IABHelper中存在错误。缺少异常处理程序中的返回行,这意味着它会通过并调用成功hanlder - 但是,mSetupDone尚未设置,因此对API的进一步调用将失败。如下所示添加return语句 - 这仍然会失败,但失败将正确报告给您的应用程序,以便您采取适当的措施。

                catch (RemoteException e) {
                if (listener != null) {
                    listener.onIabSetupFinished(new IabResult(IABHELPER_REMOTE_EXCEPTION,
                                                "RemoteException while setting up in-app billing."));
                }
                e.printStackTrace();
                return;  // This return line is missing
            }

            if (listener != null) {
                listener.onIabSetupFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup successful."));
            }

答案 2 :(得分:10)

我相信Android代码中仍有两个错误,这就解释了为什么你仍然会看到错误。请注意,调用堆栈位于独立线程上。但是将mSetupDone(IabHelper)设置为true的代码在主UI线程上执行。 Java不保证一个线程更改的数据由于CPU缓存而对另一个线程可见,除非您使用volatile关键字声明该变量。因此,它可能是设置(mSetupDone == true),但是mSetupDone的新值缓存在UI线程上,在调用堆栈中此线程尚不可见。所以该线程仍然看到mSetupDone == false。

我尝试通过使用volatile声明mSetupDone以及IabHelper的每个其他非final字段来确保安全。

现在另一个问题是.dispose()函数。它不会阻止正在进行的线程。这意味着它可以在其中一个工作线程运行时将mSetupDone设置为false。如果查看queryInventoryAsync(),您将看到它确实检查mSetupDone是否为true。并且基于你的调用堆栈,它确实超越了它。然后它随后以mSetupDone == false崩溃。唯一可能发生的方法是在你的线程处于飞行状态时调用dispose()。解决的问题是dispose()需要发出线程信号,以便在看到mSetupDone == false时无声地挽救而不是继续并抛出错误。这也防止了IabHelper的另一个问题,即处置后的实例甚至在被处置后调用了监听器回调!这里逐行解释有点复杂,但希望这会让你指向正确的方向。

答案 3 :(得分:6)

我发现了!这是关于用户的Google Play商店应用的版本。应用内结算V3需要3.9.16或更高版本(http://developer.android.com/google/play/billing/versions.html)。 我使用的是旧版本,我收到了这个错误,现在4.4.21就可以了!

答案 4 :(得分:2)

确保您在活动IabHelper.han.handleActivityResult(requestCode, resultCode, data)方法中实施onActivityResult方法。

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

答案 5 :(得分:2)

我得到的是与完全相同的代码完全相同的错误。

似乎只在某些手机上发生(事实上,在最近的错误报告中,它似乎几乎完全是Acer Iconia平板电脑!!) - 我正在处理onActivityResult ......

Google v3结算示例中存在许多可能导致ANR / FC的错误 - 我怀疑这只是另一个错误(伪劣代码和伪劣文档正在成为Google的商标 - 遗憾的是)。

我的猜测是 - 现在 - 我们需要允许mHelper或mGotInventoryListener为null并且在这种情况下只禁用In-App Billing(就好像result.isSuccess()基本上是false)

P.S。编辑添加 - 可能只是用户拥有Play商店的过时版本 - 只有在允许其运行时才会自动更新!?

答案 6 :(得分:1)

您可以在https://code.google.com/p/marketbilling/

处了解inapp v3 API开发的最新信息

该代码比通过Android SDK Manager提供的代码更新。

答案 7 :(得分:1)

除了@DavidM和@Ereza。

IabHelpr类的另一个主要问题是在多个方法中抛出RuntimeExcptions(IllegalStateException)的选择很差。在大多数情况下,从您自己的代码中抛出RuntimeExeptions是不可取的,因为它们是未经检查的异常。这就像破坏你自己的应用程序 - 如果没有被抓住,这些异常会冒泡并崩溃你的应用程序。

解决方案是实现自己的已检查异常并更改IabHelper类以抛出它,而不是IllegalStateException。这将迫使您在编译时将代码抛出到代码中的任何地方处理此异常。

这是我的自定义例外:

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类中进行了更改,我们就可以在我们调用类方法的代码中处理已检查的异常。例如:

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

答案 8 :(得分:1)

如果以上所有方法都无法帮助您,请尝试分析一下您的代码 - 是您在调用时真正设置的IabHelper吗?

我发现自己在没有意识到的情况下做得有些错误。在Activity.onCreate()

中使用错误的简单示例
m_iabHelper = new IabHelper(this, base64EncodedPublicKey); // Declare

m_iabHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { // Setup
    public void onIabSetupFinished(IabResult result) {
        // Setup code
    }
}

// Don't do this, will produce an error
List additionalSkuList = new ArrayList(); 
additionalSkuList.add(SKU_MYSKU);
m_iabHelper.queryInventoryAsync(true, additionalSkuList, m_queryFinishedListener);
// Don't do this, will produce an error

上面将尊重“IAB助手未设置”错误,因为当您的应用尝试执行m_iabHelper.queryInventoryAsync()时,IabHelper尚未设置。考虑在onIabSetupFinished()之后或在调用该函数之后的某个地方使用这些函数(例如,在onCreate()之外)

答案 9 :(得分:0)

我得到了同样的错误。我也遇到了其他问题...

在我的Galaxy Tab上,在设备上设置了多个Google帐户,禁用了应用内结算。删除一个帐户重新启用它。

我在GT-P5110,LGL75C和GT-S5839i上看到了应用内结算示例代码的问题。

(我在安装了ACRA的应用程序中使用代码......所以每次崩溃时,我都会收到信息)

Android版本的设备范围从2.3.3到4.0.4。

这很烦人。

答案 10 :(得分:0)

IABHelper.java存在许多问题。

首先 - SDK Manager下载的版本不是最新版本。使用此处找到的版本:https://code.google.com/p/marketbilling/source/detail?r=15946261ec9ae5f7c664d720f392f7787e3ee6c7这是发布此答案时的最新版本。与SDK Manager的初始版本相比,此版本似乎修复了许多问题。

在这个版本中,我唯一需要改变的是在404行之后添加flagEndAsync();,当快速连续启动2个IAB购买流时,它修复了IllegalStateException。

使用此版本,您无需在文件中使用flagEndAsync();进行管理,也可以将方法保留为公开。