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()、後で同じ破棄されたインスタンスを使用しようとしたため、おそらく例外が発生しています。私の推奨は、mHelper のみを破棄しonDestroy()て で再作成することですonCreate()。
ただし、IabHelper とデバイスの回転で別の問題が発生します。問題は次のようになります。アクティビティのonCreate()で、IabHelper インスタンス mHelper を作成してセットアップします。後で呼び出すmHelper.launchPurchaseFlow(...)と、IAB ポップアップ ダイアログがアクティビティの上に浮かんで表示されます。次に、デバイスを回転すると、IabHelper インスタンスが で破棄され、 でonDestroy(...)再作成されonCreate(...)ます。IAB ダイアログがまだ表示されている場合、購入ボタンを押すと、購入が完了します。onActivityResult()その後、アクティビティで が呼び出され、自然に を呼び出しますmHelper.handleActivityResult(...)。問題は、launchPurchaseFlow(...)IabHelper の再作成されたインスタンスで呼び出されたことがないことです。IabHelper は、アクティビティの結果のみを処理しhandleActivityResult(...)ます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 のコピーをこの commitにあるバージョンで更新したことに注意してください。これにより、いくつかのバグが修正され、Android SDK Manager で公開されていません。また、新しい commitsもありますが、新しいバグが含まれているため、使用しないでください。
これが私がしたことです:
IabHelperand 呼び出しをインスタンス化するコードstartSetup()は 内onCreate()にあるため、構成の変更を自分で処理しない限り、デバイスが回転したときに再作成されます。
.handleActivityResult()また、の先頭で呼び出していることを確認してくださいonActivityResult()。これによりIabHelper、購入ダイアログが閉じられた後、参照が正しくクリーンアップされます。
この 2 つが整っていれば、クラッシュは発生しなくなります。しかし、もう 1 つ気付くことがあります。
を呼び出して購入ダイアログを開始しlaunchPurchaseFlow()、デバイスをローテーションすると、ダイアログは開いたままになりますが、デバイスのローテーションで が呼び出されるため、ActivityのIabHelper参照が上書きされます。onCreate()このため、ダイアログを閉じると newIabHelperのhandleActivityResult()メソッドが呼び出されますが、先ほどrequestCode渡した と一致しないため、通知されません。このケース (ダイアログが開いているときのデバイスの回転) を処理するには、内で自分自身を処理する必要があります。ダイアログが閉じられたので、あなたが内部で行ったことを模倣したいと思うでしょう(ユーザーが実際に何かを購入したかどうかを発見してください)。私はちょうどそれを見つけるために電話をかけました。launchPurchaseFlow()onPurchaseFinishedListenerrequestCodeonActivityResult()onPurchaseFinishedListenerqueryInventoryAsync()
それが理想的な解決策かどうかはわかりませんが、私にとってはうまくいきます。あなたがしたように参照に固執しようとしましたIabHelperが、セットアップ状態が失われるという奇妙な問題が見られましたが、再セットアップすることはできません.
私が最後に行ったのは、課金ユーティリティ クラスを Android ソースの最新のものに更新することでした。SDK マネージャーにプッシュされていないバグ修正がいくつかあります。それらのほとんどは疑わしい null チェックですが、クラッシュを防ぐためのいくつかの改善があります。
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
}
これが正しい修正であるかどうかはわかりませんが、他の人に役立つ場合に備えて言及したいと思います.