Android In-App Billing v3: "Невозможно выполнить операцию: queryInventory"
Я впервые настроил In-App Billing, используя новый v3 API. Это работает правильно на моих устройствах, но я получил много сообщений об ошибках от других пользователей.
Один из них является:
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)***
В чем причина этих ошибок? Если я звоню queryInventoryAsync
только внутри onIabSetupFinished
Как это возможно, что mHelper
является нулем, или что mHelper
не настроен?
Кто-нибудь знает решение этой проблемы?
11 ответов
Как объяснил @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, например:)
Спасибо за все ваши комментарии.
В IABHelper есть ошибка. Строка возврата в обработчике исключений отсутствует, что означает, что она пропускается и вызывает обработчик успеха - однако mSetupDone не был установлен, поэтому дальнейшие вызовы API завершаются неудачно. Добавьте оператор возврата, как показано ниже - это все равно не удастся, но об ошибке будет правильно сообщено в ваше приложение, чтобы вы могли предпринять соответствующие действия.
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."));
}
Я считаю, что в коде Android по-прежнему есть две ошибки, которые объясняют, почему вы все еще видите ошибку. Обратите внимание, что стек вызовов находится в автономном потоке. Но код, который устанавливает для mSetupDone (IabHelper) значение true, выполняется в основном потоке пользовательского интерфейса. Java не гарантирует, что данные, измененные одним потоком, будут видны другому потоку из-за кэширования процессора, если вы не объявите переменную с ключевым словом volatile. Таким образом, возможно, что он был настроен (mSetupDone == true), но новое значение mSetupDone кэшируется в потоке пользовательского интерфейса, пока не видимом для этого потока в вашем стеке вызовов. Так что поток все еще видит mSetupDone == false.
Я попытался исправить это, объявив mSetupDone с volatile, а также с каждым другим неконечным полем IabHelper просто для безопасности.
Теперь другая проблема - это функция.dispose(). Это не останавливает текущие потоки. Это означает, что он может установить для mSetupDone значение false во время работы одного из рабочих потоков. Если вы посмотрите на queryInventoryAsync(), вы увидите, что он проверяет, что mSetupDone имеет значение true. И, основываясь на вашем стеке вызовов, он справился с этим. Затем он потерпел крах позже с mSetupDone == false. Единственный способ, которым это может произойти, - это если dispose () был вызван во время полета. Исправление заключается в том, что dispose () должен сигнализировать потокам, чтобы они просто молча спасались, вместо того, чтобы продолжать и выдавать ошибки, когда он видит mSetupDone == false. Это также предотвращает еще одну проблему с IabHelper, где удаленные экземпляры вызывают обратные вызовы слушателя даже после удаления! Здесь немного сложно объяснить построчно, но, надеюсь, таким образом вы направите вас в правильном направлении.
Я узнал! Речь идет о версии пользовательского приложения Google Play Store. Для биллинга V3 в приложении требуется 3.9.16 или выше ( http://developer.android.com/google/play/billing/versions.html). Я использовал более старую версию, и я получил эту ошибку, теперь на 4.4.21 все в порядке!
Я получаю эту ТОЧНУЮ ту же ошибку с почти тем же кодом.
Кажется, что это происходит только на определенных телефонах (на самом деле, в последних сообщениях об ошибках это выглядит почти исключительно на планшете Acer Iconia!), И я работаю над ActivityResult...
В примере биллинга Google v3 есть ряд ошибок, которые могут привести к появлению ANR /FC - я подозреваю, что это просто еще одна (неаккуратный код и некачественные документы становятся товарным знаком Google - к сожалению).
Я предполагаю - на данный момент - что нам нужно разрешить для mHelper или mGotInventoryListener значение NULL и просто отключить биллинг в приложении в этом случае (как будто result.isSuccess() был ложным, в основном)
ps отредактировано, чтобы добавить - это может быть просто из-за того, что у пользователя есть устаревшая версия Play Store - это только автообновления, если они позволяют ему работать!?
Убедитесь, что вы реализуете 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'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.");
}
}
Если все вышеперечисленное не поможет вам, попробуйте немного проанализировать ваш код - это ваше 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 не настроен", поскольку ваше приложение пытается выполнить m_iabHelper.queryInventoryAsync()
, IabHelper еще не настроен. Рассмотрите возможность использования этих функций в onIabSetupFinished()
или где-то после того, как эта функция вызывается (например, вне onCreate()
)
В дополнение к @DavidM и @Ereza.
Еще одна серьезная проблема с классом IabHelpr - неправильный выбор исключения RuntimeExcptions (IllegalStateException) в нескольких методах. Бросать RuntimeExeptions из вашего собственного кода в большинстве случаев нежелательно из-за того, что они являются непроверенными исключениями. Это все равно что саботировать ваше собственное приложение - если не поймано, эти исключения будут пузыриться и приводить к сбою вашего приложения.
Решением этой проблемы является реализация вашего собственного проверенного исключения и изменение класса 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();
}
Вы можете быть в курсе разработки API inapp v3 по адресу https://code.google.com/p/marketbilling/
Код там новее, чем тот, который доступен через Android SDK Manager.
Было много проблем с IABHelper.java.
Во-первых, версия, загружаемая SDK Manager, не обновляется. Используйте найденную здесь версию: https://code.google.com/p/marketbilling/source/detail?r=15946261ec9ae5f7c664d720f392f7787e3ee6c7 Это самая актуальная версия на момент публикации этого ответа. Похоже, что многие проблемы были исправлены в этой версии по сравнению с первоначальным выпуском, который поставляется из SDK Manager.
Единственное, что мне пришлось изменить в этой версии, это добавить flagEndAsync();
после строки 404, которая исправляет исключение IllegalStateException, когда 2 потока покупки IAB запускаются в быстрой последовательности.
С этой версией вам не нужно управлять с помощью flagEndAsync();
в ваших файлах и можете оставить метод, чтобы не быть публичным.
Я получаю те же ошибки. Я также столкнулся с другими проблемами...
Если на моей вкладке Galaxy Tab отключено несколько аккаунтов Google на устройстве, платежи внутри приложения удаляются. Удалите один аккаунт, чтобы снова включить его.
Я видел проблемы с примером кода биллинга в приложении на GT-P5110, LGL75C и GT-S5839i и других.
(Я использую код в приложении с установленным ACRA... поэтому при каждом сбое я получаю информацию)
Версия Android устройства варьируется от 2.3.3 до 4.0.4.
Это очень раздражает.