web-dev-qa-db-ja.com

Androidアプリ内課金v3: "操作を実行できません:queryInventory"

新しいv3 APIを使用してアプリ内課金を初めてセットアップしました。私のデバイスでは正常に動作していますが、他のユーザーから多くのエラーレポートを受け取っています。

それらの1つは次のとおりです。

_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)***とマークされた行に起因すると思います

これらのエラーの原因は何ですか? queryInventoryAsynconIabSetupFinished内でのみ呼び出す場合、mHelperがnullであるか、mHelperが設定されていない可能性がありますか?

誰かがこれに対する解決策を知っていますか?

29
Ereza

@Martinが説明したように、これを引き起こしたGoogleアプリ内課金の例にバグがありました。

ただし、修正後も内部呼び出しでいくつかのエラーが発生していました(queryInventoryで作成されたスレッド内のqueryInventoryAsyncは、ヘルパーがセットアップされていないというまれなケースで報告されます)。私はその場合に追加のキャッチを追加することでこれを解決しました:

_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に記録することができます。例:)

すべてのコメントをありがとう。

19
Ereza

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."));
            }
14
Martin

Android=コードにはまだ2つのバグがあるため、エラーが表示される理由がわかります。コールスタックがスタンドアロンスレッドにあることに注意してください。ただし、mSetupDoneを設定するコード(IabHelper)をtrueに設定すると、メインUIスレッドで実行されます。Javaは、変数をvolatileで宣言しない限り、CPUキャッシュが原因で、あるスレッドによって変更されたデータが他のスレッドに表示されることを保証しませんキーワードです。したがって、セットアップされた(mSetupDone == true)可能性がありますが、mSetupDoneの新しい値はUIスレッドにキャッシュされ、まだコールスタックのこのスレッドには表示されていません。そのため、スレッドにはmSetupDone == falseが表示されます。 。

私はmSetupDoneをvolatileで宣言することでこれを修正しようとしました。また、安全のためにIabHelperの他のすべての非finalフィールドも宣言しました。

もう1つの問題は.dispose()関数です。進行中のスレッドは停止しません。つまり、ワーカースレッドの1つが実行されている間、mSetupDoneをfalseに設定できます。 queryInventoryAsync()を見ると、mSetupDoneがtrueであることを確認していることがわかります。そして、あなたのコールスタックに基づいて、それはそれを超えました。その後、mSetupDone == falseでクラッシュしました。起こり得る唯一の方法は、スレッドの実行中にdispose()が呼び出された場合です。修正点は、dispose()がスレッドにシグナルを送って、mSetupDone == falseが検出されたときに続行してエラーをスローするのではなく、メッセージを出さずに救済する必要があることです。これにより、破棄されたインスタンスが破棄された後でもリスナーコールバックを呼び出すIabHelperのさらに別の問題も防止されます。ここで1行ずつ説明するのは少し複雑ですが、うまくいけば、正しい方向に向けることができます。

10
David M

見つけた!ユーザーのGoogle Playストアアプリのバージョンに関するものです。アプリ内課金V3には3.9.16以降が必要です( http://developer.Android.com/google/play/billing/versions.html )。古いバージョンを使用していて、そのエラーを受け取りました。4.4.21で問題ありません。

6
edwell

まったく同じコードで、まったく同じエラーが発生します。

特定の携帯電話でのみ発生しているようです(実際、最近のエラーレポートではAcer Iconia Tabletがほぼ完全に限定されているようです!!)-そしてonActivityResultを処理しています...

Google v3課金サンプルにはANR/FCを引き起こす可能性のあるいくつかのエラーがあります-これは単なる別のエラーだと思います(お粗末なコードと不正なドキュメントはGoogleの商標になりつつあります-悲しいことに)。

私の推測は-今のところ-mHelperまたはmGotInventoryListenerのいずれかがnullであることを許可し、その場合はアプリ内課金を無効にする必要がある(result.isSuccess()がfalseであるかのように)

pS追加するように編集-ユーザーが古いバージョンのPlayストアを持っている可能性があります-実行を許可した場合にのみ自動更新されます!?

2
user834595

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.");
  }
}
2
KevinM

@DavidMと@Erezaに加えて。

IabHelprクラスのもう1つの大きな問題は、複数のメソッドでRuntimeExcptions(IllegalStateException)をスローするという不適切な選択です。ほとんどの場合、独自のコードからRuntimeExeptionsをスローすることは、それらが未チェックの例外であるため、望ましくありません。これは、独自のアプリケーションを妨害するようなものです。キャッチされないと、これらの例外が発生してアプリがクラッシュします。

これに対する解決策は、独自のチェック済み例外を実装し、IabgalStateExceptionの代わりにIabHelperクラスを変更してスローすることです。これにより、コンパイル時にコードでスローされる可能性のあるすべての場所でこの例外を処理する必要があります。

これが私のカスタム例外です:

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();
}
1

上記のすべてが役に立たない場合は、コードを少し分析してみてください-IabHelperは、呼び出し時に本当に設定されていますか?

気づかないうちに少し間違っていることに気づきました。使い方の簡単な例wrong in 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()の外)で使用することを検討してください。

1
Voy

https://code.google.com/p/marketbilling/ で、inapp v3 API開発の最新情報を入手できます。

そこにあるコードは、Android SDK Managerから入手できるコードよりも新しいものです。

1
Catalin Morosan

IABHelper.Javaには多くの問題がありました。

まず、SDK Managerによってダウンロードされるバージョンが最新の状態に維持されません。ここにあるバージョンを使用してください: https://code.google.com/p/marketbilling/source/detail?r=15946261ec9ae5f7c664d720f392f7787e3ee6c7 この回答を投稿した時点での最新バージョンです。 SDK Managerからの初期リリースと比較して、このバージョンでは多くの問題が修正されているようです。

このバージョンで私が変更しなければならないことは、404行の後にflagEndAsync();を追加することだけです。これにより、2つのIAB購入フローが連続して起動されたときのIllegalStateExceptionが修正されます。

このバージョンでは、ファイルでflagEndAsync();を使用して管理する必要はなく、メソッドを公開しないでおくことができます。

0
Uwais A

同じエラーが発生します。他の問題にも遭遇しました...

デバイスで複数のGoogleアカウントを使用すると、Galaxy Tab 7でアプリ内課金が無効になります。1つのアカウントを削除して、再度有効にします。

特に、GT-P5110、LGL75C、GT-S5839iのアプリ内課金のサンプルコードで問題が発生しました。

(私はACRAがインストールされているアプリケーションでコードを使用します...そのため、クラッシュするたびに、情報を取得します)

デバイスAndroidバージョンは2.3.3から4.0.4の範囲です。

それは非常に迷惑です。

0
orb360