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

Posted

技术标签:

【中文标题】Android In-App Billing v3:“无法执行操作:queryInventory”【英文标题】:Android In-App Billing v3: "Can't perform operation: queryInventory" 【发布时间】:2012-12-11 04:33:35 【问题描述】:

我首次使用新的 v3 API 设置了应用内结算。它在我的设备上正常工作,但我收到了很多其他用户的错误报告。

其中一个是:

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)***的行

这些错误的原因是什么?如果我只在onIabSetupFinished 内调用queryInventoryAsync,那么mHelper 怎么可能为空,或者mHelper 没有设置?

有人知道解决办法吗?

【问题讨论】:

也有同样的例外:未设置 IAB 助手。无法执行操作:queryInventory。可能是 Google Play 结算库中的错误? 【参考方案1】:

正如@Martin 解释的那样,Google In-App Billing 示例中存在导致此问题的错误。

但是,在修复它之后,我仍然在内部调用中收到一些错误(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,而不是忽略这些错误,例如 :)

感谢你们所有的cmets。

【讨论】:

当然,在代码中像这样捕获RuntimeExceptions 是不好的做法。需要在此处完成这一事实要么意味着您的代码中存在错误,要么意味着 Google 代码中存在错误。查看 IAB 起始代码,如果是后者,我不会感到惊讶(事实上,我可以看到几个竞争条件可能导致 IABHelper 类出现问题的实例)。 错误是 Helper 会抛出它不应该抛出的地方。它拥有抛出的线程,异常不应该离开 IabHelper。调用者无法阻止或正确处理它。 这个错误已经在 API google 中修复了吗?我最近集成了应用内计费 API,并在生产中遇到了这个问题。我按照 android 文档中的建议从github.com/googlesamples/android-play-billing/tree/master/… 获取了代码。【参考方案2】:

IABHelper 中有一个错误。异常处理程序中的返回行丢失,这意味着它通过并调用成功处理程序 - 但是,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."));
            

【讨论】:

你是对的。我想指出,它已经被 Google 修复了。 @Ereza 可能已修复,但截至此评论的时间和日期,尚无法使用 SDK 管理器下载。现在,我只是手动插入了 return 语句。 不是这个。或者发生了其他事情。在检查设置标志之后,由 queryInventoryAsync() 启动的线程引发了异常。 我现在可能不是,因为该错误已在 3 年前修复。可能是您看到的一些新错误。 你有错误报告的链接吗?【参考方案3】:

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

为了安全起见,我尝试通过将 mSetupDone 声明为 volatile 以及 IabHelper 的所有其他非最终字段来解决此问题。

现在另一个问题是 .dispose() 函数。它不会停止正在进行的线程。这意味着它可以在其中一个工作线程运行时将 mSetupDone 设置为 false。如果您查看 queryInventoryAsync(),您会看到它确实检查了 mSetupDone 是否为真。根据您的调用堆栈,它确实超越了这一点。然后它后来因 mSetupDone == false 而崩溃。唯一可能发生的方法是在您的线程运行时调用 dispose() 。修复是 dispose() 需要在看到 mSetupDone == false 时向线程发出信号以静默地退出而不是继续并抛出错误。这也防止了 IabHelper 的另一个问题,即即使在被处置后,已处置的实例也会调用侦听器回调!在这里逐行解释有点复杂,但希望由此可以为您指明正确的方向。

【讨论】:

【参考方案4】:

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

【讨论】:

【参考方案5】:

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

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

【讨论】:

【参考方案6】:

我用几乎完全相同的代码得到了完全相同的错误。

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

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

我的猜测是 - 现在 - 我们需要允许 mHelper 或 mGotInventoryListener 为空,并在这种情况下禁用应用内计费(就好像 result.isSuccess() 基本上是假的)

附言编辑添加 - 可能只是用户的 Play 商店版本过时 - 只有在他们允许运行时才会自动更新!?

【讨论】:

【参考方案7】:

您可以通过https://code.google.com/p/marketbilling/了解最新的 inapp v3 API 开发

那里的代码比通过 Android SDK 管理器提供的代码更新。

【讨论】:

我相信这确实为作者提供了一个解决方案。他正在使用一个旧且有缺陷的库版本,因此我指示他使用最新的无缺陷版本。 为什么 Google 将代码保存在两个地方?哪一个是大师?将其交付到 git 存储库而不是下载 SDK 不是更好吗?【参考方案8】:

除了@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();

【讨论】:

【参考方案9】:

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

我发现自己在不知不觉中做错了。在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() 之外)

【讨论】:

【参考方案10】:

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

在我的 Galaxy Tab 7 上禁用应用内计费的设备上有多个谷歌帐户。删除一个帐户重新启用它。

我发现 GT-P5110、LGL75C 和 GT-S5839i 等设备上的应用内计费示例代码存在问题。

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

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

这很烦人。

【讨论】:

【参考方案11】:

IABHelper.java 存在很多问题。

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

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

使用此版本,您无需在文件中使用flagEndAsync(); 进行管理,并且可以不公开该方法。

【讨论】:

以上是关于Android In-App Billing v3:“无法执行操作:queryInventory”的主要内容,如果未能解决你的问题,请参考以下文章

android in-app billing v3 api中的开发人员有效负载应该是啥?

In-App Billing v3 - 不检测退款

In-App Billing v3 可靠性缺陷

Android In-App Billing:订单取消后购买状态保持“已购买”

SDK接入之Android Google Play内支付(in-app Billing)接入

如何使用 Java (Servlet) 验证来自 In-App Billing Android Market 的签名数据