5

Ich implementiert In-App Billing (v3) nach Android Implementing In-app Billing Leitfaden.In App Billing - Schnelle Geräteausrichtung - verursacht Absturz (IllegalStateException)

Alles funktioniert gut, bis ich das Gerät drehen, dann sofort wieder in seine ursprüngliche Ausrichtung drehen. Eigentlich manchmal funktioniert es und manchmal stürzt es mit:

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

scheint dies die asynchrone Natur des IAB verwandt ist, obwohl ich nicht sicher bin.

Irgendwelche Gedanken?

Antwort

6

Du bist wahrscheinlich die Ausnahme bekommen, weil irgendwo in der Aktivität Lebenszyklus Sie mHelper.dispose() genannt, habe dann versucht, das gleiche zu verwenden entsorgte Instanz später. Meine Empfehlung ist, mHelper nur in onDestroy() zu entsorgen und in onCreate() neu zu erstellen.

Sie werden jedoch ein anderes Problem mit IabHelper und Gerätedrehung auftreten. Das Problem sieht so aus: In Ihrer Aktivität onCreate() erstellen Sie die IabHelper-Instanz mHelper und richten sie ein. Später rufen Sie mHelper.launchPurchaseFlow(...) und der IAB-Popup-Dialog erscheint über Ihrer Aktivität schwebend. Dann drehen Sie das Gerät und die IabHelper-Instanz wird in onDestroy(...) entsorgt und dann in onCreate(...) neu erstellt. Der IAB-Dialog wird weiterhin angezeigt, Sie drücken die Kauftaste und der Kaufvorgang ist abgeschlossen. onActivityResult() wird dann bei Ihrer Aktivität aufgerufen, und Sie rufen natürlich mHelper.handleActivityResult(...). Das Problem ist, launchPurchaseFlow(...) wurde nie auf der neu erstellten Instanz von IabHelper aufgerufen. IabHelper verarbeitet nur das Aktivitätsergebnis in handleActivityResult(...), wenn launchPurchaseFlow(...) zuvor für die aktuelle Instanz aufgerufen wurde. Ihr OnIabPurchaseFinishedListener wird niemals aufgerufen.

Meine Lösung war, IabHelper zu ändern, damit Sie es erwarten können, handleActivityResult(...) ohne Aufruf launchPurchaseFlow(...) zu erwarten. Ich habe folgendes auf IabHelper.java

public void expectPurchaseFinished(int requestCode, OnIabPurchaseFinishedListener listener) 
{ 
    mRequestCode = requestCode; 
    mPurchaseListener = listener; 
} 

Dies bewirkt, dass IabHelper onIabPurchaseFinished(...) auf den Hörer rufen, wenn handleActivityResult(...) genannt wird. Dann Sie dies tun:

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent data) 
{ 
    mHelper.expectPurchaseFinished(requestCode, mPurchaseFinishedListener); 
    mHelper.handleActivityResult(requestCode, resultCode, data); 
} 

Meine ganze Kopie IabHelper gefunden hier https://gist.github.com/benhirashima/7917645 werden kann. Beachten Sie, dass ich meine Version von IabHelper mit der Version this commit aktualisiert habe, die einige Fehler behebt und nicht im Android SDK Manager veröffentlicht wurde. Beachten Sie auch, dass es newer commits gibt, aber sie enthalten new bugs und sollten nicht verwendet werden.

+0

Ordentliche Lösung bezüglich der Handhabung des Rotationsereignisses. Ich hatte einige Probleme damit zu verstehen, was 'expectPurchaseFinished' impliziert, aber ich habe schließlich festgestellt, dass du eine neue Instanz von purchaseFinishedListener vor der Verarbeitung des Ergebnisses einstellst, was garantiert, dass es einen Listener gibt, der das purchaseFinished-Ereignis behandelt, selbst wenn die Aktivität und iabHelper waren neu erstellt. –

+0

Wir können die gleiche Idee erweitern, um den mPurchasingItemType zu behandeln (spielt nur eine Rolle, wenn Sie sowohl Subs als auch InApp verwenden, z. B. onSaveInstanceState und onRestoreInstanceState verwenden und dann den richtigen Elementtyp einstellen, wenn er nicht gesetzt ist). –

0

Ich habe versucht, mHelper statisch zu machen, und instanziiere es nur wenn (mHelper == null), und zerstöre es NICHT in der onDestroy() Methode der Aktivität. Übergeben Sie auch den Application Kontext zu IabHelper. Auf diese Weise bleibt es nach der Einrichtung erhalten und es besteht keine Notwendigkeit mehr, sich um asynchrone Vorgänge (verursacht durch Geräteausrichtung) zu kümmern.

Hier ist ein Überblick über meinen Code:

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 
} 

Nicht sicher, ob dies die richtige Lösung ist, oder nicht, aber ich dachte, es erwähnen würde, falls es andere helfen.

+0

Ich dachte daran, das Gleiche zu tun, aber ich weiß nicht, ob es unerwünschte Nebenwirkungen gibt. Indem wir einen IabHelper halten, behalten wir eine permanente Serviceverbindung zu InAppBillingService bei. Hattest du Probleme damit? –

+0

Dies ist ein altes Problem, aber ... Es ist in Ordnung, den Anwendungskontext weiterzugeben. Wenn Sie sich die Quelle ansehen, geben Sie an, dass Sie einen Aktivitäts- oder Anwendungskontext https://code.google.com/p/marketbilling verwenden können /source/browse/v3/src/com/example/android/trivialdrivesample/util/IabHelper.java?r=5f6b7abfd0534acd5bfc7c14436f4500c99e0358 – Darussian

+0

Making mHelper statische funktioniert für mich. – deko

2

Hier ist, was ich getan habe:

Der Code der IabHelper zu instanziiert und rufen startSetup() innerhalb onCreate() ist, so wird es neu erstellt werden, wenn das Gerät gedreht wird, so lange, wie Sie auf eigene Faust nicht Umgang mit Konfigurationsänderungen .

Vergewissern Sie sich auch, dass Sie .handleActivityResult() am Anfang von onActivityResult() anrufen. Dadurch wird sichergestellt, dass Ihre Referenz IabHelper korrekt bereinigt wird, nachdem der Kaufdialog geschlossen wurde.

Mit diesen beiden Dingen sollten Sie keine weiteren Abstürze mehr sehen. Aber Sie werden bemerken, eine weitere Sache:

Wenn Sie den Kauf Dialog mit einem Aufruf an launchPurchaseFlow() beginnen und drehen Sie dann das Gerät, wird der Dialog geöffnet bleiben, aber jetzt Ihre Activity ‚s IabHelper Referenz seit onCreate() überschrieben wurde aufgerufen auf Gerätedrehung. Aus diesem Grund wird beim Schließen des Dialogfelds die neue IabHelper Methode handleActivityResult() aufgerufen, die jedoch nicht mit der zuvor an launchPurchaseFlow() übergebenen übereinstimmt, sodass Ihre onPurchaseFinishedListener nicht benachrichtigt wird. Um diesen Fall zu bewältigen (Gerätedrehungen, wenn das Dialogfeld geöffnet ist), müssen Sie die selbst innerhalb von onActivityResult() behandeln. Da der Dialog geschlossen wurde, sollten Sie nachahmen, was Sie in Ihrem onPurchaseFinishedListener getan haben (ermitteln Sie, ob der Benutzer tatsächlich etwas gekauft hat). Ich habe gerade einen Anruf an queryInventoryAsync() gemacht, um das herauszufinden.

Ich bin mir nicht sicher, ob das die ideale Lösung ist, aber es funktioniert gut für mich. Ich habe versucht, an der IabHelper Referenz wie Sie, hängen, aber ich sah seltsame Probleme, wo es seinen Setup-Zustand verlieren würde, aber ich würde nicht erlauben, es neu einzurichten.

Eine letzte Sache, die ich getan habe, war die Aktualisierung von Abrechnungs-util-Klassen mit dem neuesten von der Android-Quelle. Es gibt einige Fehlerbehebungen, die nicht an den SDK-Manager weitergegeben wurden.Die meisten von ihnen sind fragwürdig null Kontrollen, aber es gibt einige Verbesserungen Abstürze zu verhindern:

latest changeset

0

Nachdem ich viele Anregungen in SO versucht, die sie dieses Problem nicht gelöst haben, habe ich versucht, diese einfachen null Kontrollen, die das Problem gelöst:

Zuerst habe ich geprüft, ob mHelper null ist, dann eine neue Instanz erstellen:

In onCreate

if (mHelper == null) { 
    mHelper = new IabHelper(this, base64EncodedPublicKey); 
} 

Und ich hinzugefügt andere Implementierungen innerhalb einer nULL-Prüfung auf mHelper wieder:

//noinspection ConstantConditions 
if (mHelper != null){ 

    mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() { 
     public void onQueryInventoryFinished(IabResult result, Inventory inventory) { 
     } 
    }; 
    mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() { 
     public void onIabPurchaseFinished(IabResult result, Purchase purchase) { 
     } 
    }; 
    mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { 
     public void onIabSetupFinished(IabResult result) { 
     } 
    }); 
} 

Und natürlich sollten Sie entsorgen Helfer:

@Override 
public void onDestroy() { 
    super.onDestroy(); 
    if (mHelper != null) 
     mHelper.dispose(); 
    mHelper = null; 
} 

Wenn das Problem weiterhin widersetzt, Ihre IabHelper Klasse aktualisieren.