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

Ere*_*eza 29 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)
Run Code Online (Sandbox Code Playgroud)

另一个是:

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

我的活动实现遵循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();
}
Run Code Online (Sandbox Code Playgroud)

我假设两个错误都来自标记为的行 ***(1)***

这些错误的原因是什么?如果我queryInventoryAsync只在内部调用onIabSetupFinished,那么如何mHelper将null mHelper设置为null,或者不设置?

有谁知道解决这个问题?

Ere*_*eza 19

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

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

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.");
}
Run Code Online (Sandbox Code Playgroud)

我也遇到了崩溃mHelper.dispose(),我以类似的方式修复了:

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

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

谢谢你们的评论.

  • 当然,在你的代码中捕捉这样的 `RuntimeException` 是不好的做法。需要在此处完成的事实要么意味着您的代码中存在错误,要么意味着 Google 的代码中存在错误。查看 IAB 入门代码,如果是后者,我不会感到惊讶(事实上,我可以看到几个竞争条件可能导致 IABHelper 类出现问题的实例)。 (2认同)

Mar*_*tin 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."));
            }
Run Code Online (Sandbox Code Playgroud)

  • 它就在那里 (3认同)
  • 你是对的.我想说明它已经由Google修复过了. (2认同)

Dav*_*d M 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的另一个问题,即处置后的实例甚至在被处置后调用了侦听器回调!这里逐行解释有点复杂,但希望这会让你指向正确的方向.


小智 6

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