Ich verwende die IabHelper
Dienstprogrammklassen, wie im Tutorial von Google empfohlen, und bin von diesem Fehler schwer betroffen. Anscheinend IabHelper
können nicht mehrere asynchrone Vorgänge gleichzeitig ausgeführt werden. Ich habe es sogar geschafft, einen Kauf zu starten, während die Inventur noch lief.
Ich habe bereits versucht, onActivityResult
wie hier vorgeschlagen in meiner Hauptklasse zu implementieren , aber ich erhalte nicht einmal einen Aufruf dieser Methode, bevor der Fehler auftritt. Dann fand ich das , aber ich habe keine Ahnung , wo diese finden flagEndAsync
Methode - es ist nicht in der IabHelper
Klasse.
Jetzt suche ich nach einem Weg, um dies zu umgehen (ohne den ganzen Knall neu zu implementieren). Die einzige Lösung, die mir in den Sinn kommt, besteht darin, ein boolesches Feld zu erstellen asyncActive
, das vor dem Start einer asynchronen Aufgabe überprüft wird, und dies nicht zu tun, wenn eine andere Aufgabe aktiv ist. Aber das hat viele andere Probleme und funktioniert nicht über Aktivitäten hinweg. Außerdem würde ich es vorziehen, eine asynchrone Task-Warteschlange einzurichten und auszuführen, sobald dies zulässig ist, anstatt überhaupt nicht ausgeführt zu werden.
Irgendwelche Lösungen für dieses Problem?
quelle
Antworten:
Eine einfache knifflige Lösung
Fügen Sie vor dem Aufrufen der purchaseItem- Methode einfach diese Zeile hinzu
if (billingHelper != null) billingHelper.flagEndAsync();
Ihr Code sieht also so aus
if (billingHelper != null) billingHelper.flagEndAsync(); purchaseItem("android.test.purchased");
Hinweis: Vergessen Sie nicht, die Methode public flagEndAsync () in IabHelper zu erstellen, wenn Sie sie von einem anderen Paket aus aufrufen.
quelle
Stellen Sie sicher, dass Sie die IabHelper
handleActivityResult
in der AktivitätonActivityResult
und NICHT in den Fragmenten aufrufenonActivityResult
.Das folgende Codefragment stammt aus der MainActivity von TrivialDrive :
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data); if (mHelper == null) return; // Pass on the activity result to the helper for handling if (!mHelper.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.d(TAG, "onActivityResult handled by IABUtil."); } }
Aktualisieren:
quelle
Dies war nicht leicht zu knacken, aber ich fand die erforderlichen Problemumgehungen. In letzter Zeit ziemlich enttäuscht von Google, sind ihre Android-Websites zu einem Chaos geworden (sehr schwer zu findende nützliche Informationen) und ihr Beispielcode ist schlecht. Als ich vor ein paar Jahren eine Android-Entwicklung machte, ging alles so viel einfacher! Dies ist ein weiteres Beispiel dafür ...
In der Tat ist IabUtil fehlerhaft, es ruft seine eigenen asynchronen Aufgaben nicht richtig ab. Der komplette Satz notwendiger Problemumgehungen, um dieses Ding zu stabilisieren:
1) Methode
flagEndAsync
öffentlich machen. Es ist da, nur nicht sichtbar.2) Lassen Sie jeden Listener anrufen
iabHelper.flagEndAsync
, um sicherzustellen, dass die Prozedur ordnungsgemäß als abgeschlossen markiert ist. es scheint bei allen Zuhörern nötig zu sein.3) Surround-Anrufe mit einem
try/catch
, um das zu erfassen,IllegalStateException
was auftreten kann, und behandeln Sie es auf diese Weise.quelle
IllegalStateException
eingeworfenflagEndAsync
durch eine neue Ausnahmeklasse ersetzen , von der geerbt wurde, umException
zu verstehen, wo Sietry/catch
Blöcke platzieren sollten. Dieser Force Code Static Analyzer generiert Fehler, bei denen Ihre benutzerdefinierte Ausnahme nicht behandelt wurde.Am Ende habe ich etwas Ähnliches wie Kintaro gemacht. Aber mHelper.flagEndAsync () am Ende des Catch hinzugefügt. Der Benutzer erhält weiterhin den Toast, aber wenn er das nächste Mal die Kauftaste drückt, wurde der asynchrone Vorgang abgebrochen und die Kauftaste ist wieder einsatzbereit.
if (mHelper != null) { try { mHelper.launchPurchaseFlow(this, item, RC_REQUEST, mPurchaseFinishedListener, ""); } catch(IllegalStateException ex){ Toast.makeText(this, "Please retry in a few seconds.", Toast.LENGTH_SHORT).show(); mHelper.flagEndAsync(); } }
quelle
Suchen Sie
flagEndAsync()
in derIabHelper.java
Datei und machen Sie sie öffentlich .Bevor Sie den Kauf versuchen, rufen Sie
flagEndAsync()
Ihren anIabHelper
.Sie müssen etwas wie diesen Code tun:
mHelper.flagEndAsync(); mHelper.launchPurchaseFlow(AboutActivity.this, SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener, "payload-string");
quelle
Ich hatte das gleiche Problem, bis ich auf einen anderen SO-Thread stieß . Ich füge eine überarbeitete Version des Codes hinzu, der in dem anderen Thread enthalten ist, den Sie in Ihre Aktivität aufnehmen müssen, um den Kauf zu initialisieren.
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // Pass on the activity result to the helper for handling // NOTE: handleActivityResult() will update the state of the helper, // allowing you to make further calls without having it exception on you if (billingHelper.handleActivityResult(requestCode, resultCode, data)) { Log.d(TAG, "onActivityResult handled by IABUtil."); handlePurchaseResult(requestCode, resultCode, data); return; } // What you would normally do // ... }
quelle
Ein einfacher Trick, der es für mich getan hat, war das Erstellen einer Methode in IabHelper:
public Boolean getAsyncInProgress() { return mAsyncInProgress; }
und dann in Ihrem Code überprüfen Sie einfach:
if (!mHelper.getAsyncInProgress()) //launch purchase else Log.d(TAG, "Async in progress already..)
quelle
Wirklich nerviges Problem. Hier ist eine schnelle und schmutzige Lösung, die in Bezug auf den Code nicht perfekt ist, aber benutzerfreundlich ist und schlechte Bewertungen und Abstürze vermeidet:
if (mHelper != null) { try { mHelper.launchPurchaseFlow(this, item, RC_REQUEST, mPurchaseFinishedListener, ""); } catch(IllegalStateException ex){ Toast.makeText(this, "Please retry in a few seconds.", Toast.LENGTH_SHORT).show(); } }
Auf diese Weise muss der Benutzer nur ein weiteres Mal tippen (im schlimmsten Fall zweimal) und erhält das Abrechnungs-Popup
Ich hoffe es hilft
quelle
Überprüfen Sie einfach den onActivityResult requestCode für die Aktivität und übergeben Sie ihn einfach an das Fragment, wenn er mit dem PURCHASE_REQUEST_CODE übereinstimmt, den Sie beim Kauf verwendet haben.
Wenn Sie das Fragment in der FragmentTransaction hinzufügen oder ersetzen, legen Sie einfach ein Tag fest:
fTransaction.replace(R.id.content_fragment, fragment, fragment.getClass().getName());
Dann auf onActivityResult Ihrer Aktivität
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if(requestCode == PurchaseFragment.PURCHASE_REQUEST_CODE) { PurchaseFragment fragment = getSuportFragmentManager().findFragmentByTag(PurchaseFragment.class.getNAme()); if(fragment != null) { fragment.onActivityResult(requestCode, resultCode, data); } } }
quelle
Wenn Sie in Fragment codieren, dann Sie diesen Code in IabHelper.java
void flagStartAsync(String operation) { if (mAsyncInProgress) { flagEndAsync(); } if (mAsyncInProgress) throw new IllegalStateException("Can't start async operation (" + operation + ") because another async operation(" + mAsyncOperation + ") is in progress."); mAsyncOperation = operation; mAsyncInProgress = true; logDebug("Starting async operation: " + operation); }
quelle
Oder Sie können die neueste IabHelper.java-Datei hier herunterladen: https://code.google.com/p/marketbilling/source/browse/
Die Version vom 15. März hat dies für mich behoben. (Beachten Sie, dass am 15. andere Dateien ohne Änderungen festgeschrieben wurden.)
Ich musste immer noch einen Absturz beheben, der während des Tests durch Extras mit Nullabsicht verursacht wurde, als "android.test.canceled" die gesendete SKU war. Ich habe mich verändert:
int getResponseCodeFromIntent(Intent i) { Object o = i.getExtras().get(RESPONSE_CODE);
zu:
int getResponseCodeFromIntent(Intent i) { Object o = i.getExtras() != null ? i.getExtras().get(RESPONSE_CODE) : null;
quelle
Ich hatte dieses Problem gelegentlich und in meinem Fall habe ich es darauf zurückgeführt, dass die onServiceConnected-Methode in IabHelper mehrmals aufgerufen werden kann, wenn der zugrunde liegende Dienst die Verbindung trennt und wieder herstellt (z. B. aufgrund einer intermittierenden Netzwerkverbindung).
In meinem Fall lauteten die spezifischen Vorgänge "Asynchroner Vorgang kann nicht gestartet werden (Inventar aktualisieren), da ein anderer asynchroner Vorgang (launchPurchaseFlow) ausgeführt wird."
So wie meine App geschrieben ist, kann ich launchPurchaseFlow erst aufrufen, nachdem ich ein abgeschlossenes queryInventory hatte, und ich rufe QueryInventory nur über meine onIabSetupFinished-Handlerfunktion auf.
Der IabHelper-Code ruft diese Handlerfunktion immer dann auf, wenn onServiceConnected aufgerufen wird. Dies kann mehrmals vorkommen.
Die Android-Dokumentation für onServiceDisconnected lautet:
das erklärt das Problem.
Möglicherweise sollte IabHelper die Listener-Funktion onIabSetupFinished nicht mehr als einmal aufrufen, aber andererseits war es trivial, das Problem in meiner App zu beheben, indem einfach nicht queryInventory aus dieser Funktion heraus aufgerufen wurde, wenn ich dies bereits getan und die Ergebnisse erhalten habe .
quelle
Ein weiteres wichtiges Problem bei der IabHelpr-Klasse ist die schlechte Wahl, RuntimeExcptions (IllegalStateException) in mehreren Methoden auszulösen. Das Auslösen von RuntimeExeptions aus Ihrem eigenen Code ist in den meisten Fällen nicht wünschenswert, da es sich um nicht aktivierte Ausnahmen handelt . Das ist wie das Sabotieren Ihrer eigenen Anwendung. Wenn sie nicht abgefangen werden, sprudeln diese Ausnahmen und stürzen Ihre App ab.
Die Lösung hierfür besteht darin, eine eigene aktivierte Ausnahme zu implementieren aktivierte und die IabHelper-Klasse so zu ändern, dass sie anstelle der IllegalStateException ausgelöst wird. Dadurch werden Sie gezwungen, diese Ausnahme überall dort zu behandeln, wo sie beim Kompilieren in Ihren Code geworfen werden könnte.
Hier ist meine benutzerdefinierte Ausnahme:
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); } }
Sobald wir die Änderungen in der IabHelper-Klasse vorgenommen haben, können wir unsere aktivierte Ausnahme in unserem Code behandeln, in dem wir die Klassenmethoden aufrufen. Zum Beispiel:
try { setUpBilling(targetActivityInstance.allData.getAll()); } catch (MyIllegalStateException ex) { ex.printStackTrace(); }
quelle
Ich hatte das gleiche Problem und das Problem war, dass ich die Methode onActivityResult nicht implementiert habe.
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { try { if (billingHelper == null) { return; } else if (!billingHelper.handleActivityResult(requestCode, resultCode, data)) { super.onActivityResult(requestCode, resultCode, data); } } catch (Exception exception) { super.onActivityResult(requestCode, resultCode, data); } }
quelle
Ja, ich bin auch mit diesem Problem konfrontiert, aber ich habe es gelöst, aber ich habe es mit gelöst
IabHelper mHelpermHelper = new IabHelper(inappActivity, base64EncodedPublicKey); mHelper.flagEndAsync();
Die obige Methode stoppt alle Flags. Seine Arbeit für mich muss überprüfen
quelle
Diese Antwort spricht direkt das Problem an, das @Wouter gesehen hat ...
Es ist wahr,
onActivityResult()
dass ausgelöst werden muss, wie viele Leute gesagt haben. Der Fehler ist jedoch, dass der Google-Code nicht ausgelöst wirdonActivityResult()
bestimmten Umständen , dh wenn Sie beim Ausführen des Debug-Builds Ihrer App zweimal auf die Schaltfläche [KAUFEN] drücken.Ein großes Problem ist außerdem, dass sich der Benutzer möglicherweise in einer wackeligen Umgebung befindet (z. B. Bus oder U-Bahn) und zweimal die [KAUFEN] -Taste drückt ... plötzlich haben Sie eine Ausnahme!
Zumindest Google hat diese peinliche Ausnahme https://github.com/googlesamples/android-play-billing/commit/07b085b32a62c7981e5f3581fd743e30b9adb4ed#diff-b43848e47f8a93bca77e5ce95b1c2d66 behoben
Im Folgenden ist aufgeführt, was ich in derselben Klasse implementiert habe, in der
IabHelper
instanziiert wird (für mich in der Anwendungsklasse):/** * invokes the startIntentSenderForResult - which will call your activity's onActivityResult() when it's finished * NOTE: you need to override onActivityResult() in your activity. * NOTE2: check IAB code updates at https://github.com/googlesamples/android-play-billing/tree/master/TrivialDrive/app/src/main/java/com/example/android/trivialdrivesample/util * @param activity * @param sku */ protected boolean launchPurchaseWorkflow(Activity activity, String sku) { if (mIabIsInitialized) { try { mHelper.launchPurchaseFlow( activity, sku, Constants.PURCHASE_REQUEST_ID++,// just needs to be a positive number and unique mPurchaseFinishedListener, Constants.DEVELOPER_PAYLOAD); return true;//success } catch (IllegalStateException e) { mHelper.flagEndAsync(); return launchPurchaseWorkflow(activity, sku);//recursive call } } else { return false;//failure - not initialized } }
Meine Schaltfläche [KAUFEN] ruft dies auf
launchPurchaseWorkflow()
und übergibt die SKU und die Aktivität, in der sich die Schaltfläche befindet (oder, wenn Sie sich in einem Fragment befinden, die umschließende Aktivität).HINWEIS: Stellen Sie sicher, dass Sie
IabHelper.flagEndAsync()
öffentlich machen .Hoffentlich wird Google diesen Code in naher Zukunft verbessern. Dieses Problem ist ungefähr 3 Jahre alt und es ist immer noch ein anhaltendes Problem :(
quelle
Meine Lösung ist einfach
1.) Machen Sie die Variable mAsyncInProgress außerhalb von IabHelper sichtbar
public boolean isAsyncInProgress() { return mAsyncInProgress; }
2.) Verwenden Sie dies in Ihrer Aktivität wie folgt:
... if (mIabHelper.AsyncInProgress()) return; mIabHelper.queryInventoryAsync(...); ...
quelle
Eine wenig modifizierte Version von NadtheVlads Antwort, die wie Charme wirkt
private void makePurchase() { if (mHelper != null) { try { mHelper.launchPurchaseFlow(getActivity(), ITEM_SKU, 10001, mPurchaseFinishedListener, ""); } catch(IllegalStateException ex){ mHelper.flagEndAsync(); makePurchase(); } } }
Die Logik ist einfach: Fügen Sie das
launchPurchaseFlow()
Ding einfach in eine Methode ein und verwenden Sie die Rekursion im catch-Block. Sie müssen nochflagEndAsync()
aus derIabHelper
Klasse veröffentlichen.quelle
Ich habe das gleiche Problem, aber es wurde behoben! Ich denke, Sie dürfen nicht "launchPurchaseFlow" auf dem UI-Thread ausführen, versuchen Sie, launchPurchaseFlow auf dem UI-Thread auszuführen, es würde gut funktionieren!
mActivity.runOnUiThread(new Runnable(){ public void run(){ mHelper.launchPurchaseFlow(mActivity, item, 10001, mPurchaseFinishedListener,username); } });
quelle