5

Android のアプリ内課金の実装ガイドに従って、アプリ内課金 (v3) を実装しました。

デバイスを回転させ、すぐに元の向きに戻すまで、すべて正常に動作します。実際には、動作することもあれば、次のようにクラッシュすることもあります。

java.lang.IllegalStateException: IabHelper was disposed of, so it cannot be used.

私は肯定的ではありませんが、これは IAB の非同期性に関連しているようです。

何かご意見は?

4

4 に答える 4

6

アクティビティのライフサイクルのどこかで を呼び出し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もありますが、新しいバグが含まれているため、使用しないでください。

于 2013-12-11T22:19:20.800 に答える
2

これが私がしたことです:

IabHelperand 呼び出しをインスタンス化するコードstartSetup()は 内onCreate()にあるため、構成の変更を自分で処理しない限り、デバイスが回転したときに再作成されます。

.handleActivityResult()また、の先頭で呼び出していることを確認してくださいonActivityResult()。これによりIabHelper、購入ダイアログが閉じられた後、参照が正しくクリーンアップされます。

この 2 つが整っていれば、クラッシュは発生しなくなります。しかし、もう 1 つ気付くことがあります。

を呼び出して購入ダイアログを開始しlaunchPurchaseFlow()、デバイスをローテーションすると、ダイアログは開いたままになりますが、デバイスのローテーションで が呼び出されるため、ActivityIabHelper参照が上書きされます。onCreate()このため、ダイアログを閉じると newIabHelperhandleActivityResult()メソッドが呼び出されますが、先ほどrequestCode渡した と一致しないため、通知されません。このケース (ダイアログが開いているときのデバイスの回転) を処理するには、内で自分自身を処理する必要があります。ダイアログが閉じられたので、あなたが内部で行ったことを模倣したいと思うでしょう(ユーザーが実際に何かを購入したかどうかを発見してください)。私はちょうどそれを見つけるために電話をかけました。launchPurchaseFlow()onPurchaseFinishedListenerrequestCodeonActivityResult()onPurchaseFinishedListenerqueryInventoryAsync()

それが理想的な解決策かどうかはわかりませんが、私にとってはうまくいきます。あなたがしたように参照に固執しようとしましたIabHelperが、セットアップ状態が失われるという奇妙な問題が見られましたが、再セットアップすることはできません.

私が最後に行ったのは、課金ユーティリティ クラスを Android ソースの最新のものに更新することでした。SDK マネージャーにプッシュされていないバグ修正がいくつかあります。それらのほとんどは疑わしい null チェックですが、クラッシュを防ぐためのいくつかの改善があります。

最新のチェンジセット

于 2013-09-28T07:19:05.510 に答える
0

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
}

これが正しい修正であるかどうかはわかりませんが、他の人に役立つ場合に備えて言及したいと思います.

于 2013-08-14T04:18:25.087 に答える