Подтвердить что ты не робот

Android In-App Billing v3: "Невозможно выполнить операцию: queryInventory"

У меня есть настройка In-App Billing в первый раз с использованием нового API v3. Он работает правильно на моих устройствах, но я получил множество отчетов об ошибках от других пользователей.

Один из них:

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 get an inventory of
            // stuff we own.
            mHelper.queryInventoryAsync(mGotInventoryListener); //***(1)***
        }
    });
}

// Listener that 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)***

В чем причина этих ошибок? Если я вызываю queryInventoryAsync только внутри onIabSetupFinished, как возможно, что mHelper имеет значение null или что mHelper не настроен?

Кто-нибудь знает об этом решении?

4b9b3361

Ответ 1

Как пояснил @Martin, в примере Google In-App Billing произошла ошибка, вызвавшая это.

Однако, после его исправления, я все еще получал некоторые ошибки во внутренних вызовах (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, например:)

Спасибо за ваши комментарии.

Ответ 2

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

Ответ 3

Я считаю, что в коде Android все еще есть две ошибки, что объясняет, почему вы все еще видите ошибку. Обратите внимание, что стек вызовов находится в автономном потоке. Но код, который устанавливает mSetupDone (IabHelper) в true, выполняется в основном потоке пользовательского интерфейса. Java не гарантирует, что данные, измененные одним потоком, будут видны другому потоку из-за кэширования ЦП, если вы не объявите переменную с ключевым словом volatile. Таким образом, возможно, что он был настроен (mSetupDone == true), но новое значение mSetupDone кэшируется в потоке пользовательского интерфейса, еще не видимое этому потоку в вашем стеке вызовов. Таким образом, поток все еще видит mSetupDone == false.

Я попытался исправить это, объявив mSetupDone с изменчивым, а также любое другое не конечное поле IabHelper, чтобы быть в безопасности.

Теперь другая проблема - это функция .dispose(). Он не останавливает текущие потоки. Это означает, что он может установить mSetupDone в false, пока выполняется один из рабочих потоков. Если вы посмотрите на queryInventoryAsync(), вы увидите, что он проверяет, что mSetupDone является истинным. И, основываясь на вашем стеке вызовов, это прошло. Затем он разбился позже с mSetupDone == false. Только способ, который может произойти, - это вызывать dispose(), пока ваш поток находится в полете. Исправление состоит в том, что dispose() должен сигнализировать потоки, чтобы просто молча выручить, а не продолжать и бросать ошибки, когда видит mSetupDone == false. Это также предотвращает еще одну проблему с IabHelper, когда установленные экземпляры вызывают обратные вызовы прослушивателя даже после их удаления! Это немного сложно объяснить по строкам здесь, но мы надеемся, таким образом, заставим вас указать в правильном направлении.

Ответ 4

Я узнал! Это о версии приложения Google Play Store. Биллинг V3 для приложений требует 3,9.16 или выше (http://developer.android.com/google/play/billing/versions.html). Я использовал более старую версию, и я получил эту ошибку, теперь на 4.4.21 это нормально!

Ответ 5

Убедитесь, что вы используете метод IabHelper.han.handleActivityResult(requestCode, resultCode, data) в ваших действиях onActivityResult.

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

Ответ 6

Я получаю эту ТОЧНУЮ ту же ошибку с почти таким же кодом.

Кажется, что происходит только на некоторых телефонах (на самом деле это почти исключительно Acer Iconia Tablet в последних отчетах об ошибках!) - и я обрабатываю onActivityResult...

В Google V3 Billing Sample есть ряд ошибок, которые могут вызывать ANR/FC. Я подозреваю, что это просто еще один (дрянной код и дрянные документы становятся товарным знаком Google - к сожалению).

Мое предположение - на данный момент - нам нужно разрешить либо mHelper, либо mGotInventoryListener быть нулевым и просто отключить In-App Billing в этом случае (как если бы result.isSuccess() был ложным, в основном)

p.s. чтобы добавить - это может быть только то, что у пользователя устаревшая версия Play Маркета - это только автообновление, если они позволяют ему работать!?

Ответ 7

Вы можете быть в курсе разработки API inapp v3 на https://code.google.com/p/marketbilling/

В коде есть более новый, чем тот, который доступен через Android SDK Manager.

Ответ 8

В дополнение к @DavidM и @Ereza.

Еще одна серьезная проблема с классом IabHelpr - это плохой выбор метаданных RuntimeExcptions (IllegalStateException) несколькими способами. Выбрасывание RuntimeExceptions из вашего собственного кода в большинстве случаев нежелательно из-за того, что они являются неконтролируемыми исключениями. Это похоже на саботаж собственного приложения - если не поймано, эти исключения будут пузыриться и разбивать ваше приложение.

Решением этого является реализация собственного проверенного исключения и изменение класса IabHelper для его использования вместо исключения IllegalStateException. Это заставит вас обрабатывать это исключение везде, где оно может быть выброшено в ваш код во время компиляции.

Вот мое обычное исключение:

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

Ответ 9

Если все вышеперечисленное не поможет вам, попробуйте немного проанализировать свой код - настроен ли ваш IabHelper в то время, когда вы его вызываете?

Я понял, что делаю это немного неправильно, не осознавая этого. Простой пример использования неправильный в 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 helper is not setup", так как пока приложение пытается выполнить m_iabHelper.queryInventoryAsync(), IabHelper еще не настроен. Рассмотрите возможность использования этих функций в onIabSetupFinished() или где-нибудь после вызова этой функции (например, вне onCreate())

Ответ 10

Я получаю те же ошибки. Я также столкнулся с другими проблемами...

Имея более одной учетной записи google на устройстве, отключенном в приложении для биллинга на моем вкладке Galaxy 7. Удалите одну учетную запись повторно включив ее.

Я видел проблемы с примером кода для выставления счетов приложений на GT-P5110, LGL75C и GT-S5839i и других.

(Я использую код в приложении с установленным ACRA... поэтому каждый раз он падает, я получаю информацию)

Устройства Android версии от 2.3.3 до 4.0.4.

Это очень раздражает.

Ответ 11

Было много проблем с IABHelper.java.

Во-первых - версия, загружаемая диспетчером SDK, не обновляется. Используйте версию, найденную здесь: https://code.google.com/p/marketbilling/source/detail?r=15946261ec9ae5f7c664d720f392f7787e3ee6c7 Это самая актуальная версия для публикации этого ответа. Многие проблемы, похоже, были исправлены с этой версией по сравнению с первоначальной версией, которая поступает от диспетчера SDK.

Единственное, что мне пришлось изменить в этой версии, - это добавить flagEndAsync(); после строки 404, которая исправляет исключение IllegalStateException, когда две быстрые покупки IAB запускаются быстро.

С этой версией вам не нужно управлять с помощью flagEndAsync(); в ваших файлах и оставить этот метод недоступным.