我根据 Android 的实现应用内计费指南实现了应用内计费 (v3)。
一切正常,直到我旋转设备,然后立即将其旋转回原来的方向。实际上,有时它可以工作,有时它会崩溃:
java.lang.IllegalStateException: IabHelper was disposed of, so it cannot be used.
似乎这与 IAB 的异步性质有关,尽管我并不积极。
有什么想法吗?
我根据 Android 的实现应用内计费指南实现了应用内计费 (v3)。
一切正常,直到我旋转设备,然后立即将其旋转回原来的方向。实际上,有时它可以工作,有时它会崩溃:
java.lang.IllegalStateException: IabHelper was disposed of, so it cannot be used.
似乎这与 IAB 的异步性质有关,尽管我并不积极。
有什么想法吗?
您可能会遇到异常,因为在活动生命周期的某个地方,您调用了mHelper.dispose()
,然后稍后尝试使用同一个已处置的实例。我的建议是只处理 mHelperonDestroy()
并在onCreate()
.
但是,您将遇到 IabHelper 和设备旋转的另一个问题。问题是这样的:在您的活动中onCreate()
,您创建 IabHelper 实例 mHelper 并进行设置。稍后,您调用mHelper.launchPurchaseFlow(...)
,IAB 弹出对话框出现在您的活动上方。然后旋转设备,IabHelper 实例被处理掉,onDestroy(...)
然后重新创建onCreate(...)
。IAB 对话框仍在显示,您按下购买按钮,购买完成。onActivityResult()
然后在您的活动上调用,您自然会调用mHelper.handleActivityResult(...)
. 问题是,launchPurchaseFlow(...)
从未在重新创建的 IabHelper 实例上调用过。IabHelper 仅在handleActivityResult(...)
if中处理活动结果launchPurchaseFlow(...)
之前已在当前实例上调用过。您的 OnIabPurchaseFinishedListener 永远不会被调用。
我对此的解决方案是修改 IabHelper 以允许您告诉它期望handleActivityResult(...)
而不调用launchPurchaseFlow(...)
. 我将以下内容添加到 IabHelper.java
public void expectPurchaseFinished(int requestCode, OnIabPurchaseFinishedListener listener)
{
mRequestCode = requestCode;
mPurchaseListener = listener;
}
这将导致 IabHelper在被调用onIabPurchaseFinished(...)
时调用侦听handleActivityResult(...)
器。然后,你这样做:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
mHelper.expectPurchaseFinished(requestCode, mPurchaseFinishedListener);
mHelper.handleActivityResult(requestCode, resultCode, data);
}
我的 IabHelper 的完整副本可以在这里找到https://gist.github.com/benhirashima/7917645。请注意,我使用此提交中找到的版本更新了我的 IabHelper 副本,该版本修复了一些错误并且尚未在 Android SDK 管理器中发布。另请注意,有较新的提交,但它们包含新错误,不应使用。
这是我所做的:
实例化IabHelper
and 调用的代码startSetup()
在 内onCreate()
,因此只要您不自己处理配置更改,它将在设备旋转时重新创建。
另外,请确保您.handleActivityResult()
在onActivityResult()
. 这将确保IabHelper
在关闭购买对话框后正确清理您的参考。
有了这两件事,您应该不会再看到任何崩溃。但是你会注意到另外一件事:
如果您通过调用来启动购买对话框,launchPurchaseFlow()
然后旋转设备,对话框将保持打开状态,但现在您Activity
的IabHelper
引用已被覆盖,因为onCreate()
在设备旋转时调用。因此,当您关闭对话框时,会调用 newIabHelper
的方法,但它与您之前传递给的handleActivityResult()
方法不匹配,因此不会通知您。要处理这种情况(对话框打开时设备旋转),您需要自己处理. 由于对话框已关闭,您将想要模仿您在您的内部所做的事情(发现用户是否真的买了东西)。我只是打了个电话想知道这一点。requestCode
launchPurchaseFlow()
onPurchaseFinishedListener
requestCode
onActivityResult()
onPurchaseFinishedListener
queryInventoryAsync()
我不确定这是否是理想的解决方案,但对我来说效果很好。我试着像你一样挂在IabHelper
参考上,但我看到了奇怪的问题,它会失去设置状态,但不允许我重新设置它。
我做的最后一件事是使用来自 android 源的最新版本更新计费实用程序类。有一些错误修复尚未推送到 SDK 管理器。其中大多数是有问题的空检查,但有一些改进可以防止崩溃:
我试图使 mHelper 静态,并且仅在 (mHelper == null) 时实例化它,而不是在活动的 onDestroy() 方法中销毁它。此外,将应用程序上下文传递给 IabHelper。这样,一旦设置完成,它就会一直存在,并且不再需要担心异步操作(由设备方向引起)。
这是我的代码的大纲:
static IabHelper mHelper;
public void onCreate(Bundle savedInstanceState) {
...
if (mHelper == null) {
mHelper = new IabHelper(getApplicationContext(), base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
...
});
}
...
}
protected void onDestroy() {
...
// Don't do ANYTHING to mHelper, so it will stick around on orientation change
}
不确定这是否是正确的解决方法,但我想我会提到它以防它帮助其他人。
在我在 SO 中尝试了许多他们没有解决这个问题的建议之后,我尝试了这个简单null
的检查来解决这个问题:
首先我检查是否mHelper
为空,然后创建一个新实例:
在onCreate
if (mHelper == null) {
mHelper = new IabHelper(this, base64EncodedPublicKey);
}
我mHelper
再次在 null 检查中添加了其他实现:
//noinspection ConstantConditions
if (mHelper != null){
mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
}
};
mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
}
};
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
}
});
}
当然,您应该处理助手:
@Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null)
mHelper.dispose();
mHelper = null;
}
如果问题仍然存在,请更新您的IabHelper类。