2012-12-25 13 views
28

Ho configurato la fatturazione in-app per la prima volta utilizzando la nuova API v3. Funziona correttamente sui miei dispositivi ma ho ricevuto molte segnalazioni di errori da altri utenti.Fatturazione in-app per Android v3: "Impossibile eseguire l'operazione: queryInventory"

Uno di loro è:

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) 

E un altro è:

java.lang.NullPointerException 
    at my.package.activities.MainActivity$4.onIabSetupFinished(MainActivity.java:159) 
    at my.package.util.iab.IabHelper$1.onServiceConnected(IabHelper.java:242) 

mia implementazione attività segue il codice di Google esempio (tutte le classi di riferimento sono intatte dall'esempio):

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

Suppongo che entrambi gli errori provengano dalla riga contrassegnata come ***(1)***

Qual è la causa di questi errori? Se sto chiamando queryInventoryAsync solo all'interno di onIabSetupFinished, come è possibile che mHelper sia nullo o che mHelper non sia impostato?

Qualcuno sa una soluzione a questo?

+1

Avere la stessa eccezione: l'assistente IAB non è impostato. Impossibile eseguire l'operazione: queryInventory. Potrebbe esserci un bug nella libreria di fatturazione di Google Play? – Wizche

risposta

19

Come spiegato @ Martin, c'era un bug in Google in-app Billing esempio che ha causato questo.

Tuttavia, dopo averlo corretto, stavo ancora ricevendo alcuni errori nelle chiamate interne (queryInventory all'interno del thread creato in queryInventoryAsync in alcuni rari casi che l'helper non è configurato). Ho risolto questo con l'aggiunta di un fermo supplementare in questo caso:

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

ho anche avuto un incidente sul mHelper.dispose() che ho fissato in modo simile:

try{ 
    if (mContext != null) mContext.unbindService(mServiceConn); 
} 
catch(IllegalArgumentException ex){ //ADDED THIS CATCH 
    //IGNORE IT - somehow, the service was already unregistered 
} 

Naturalmente, invece di ignorare questi errori puoi tranquillamente registrarli ad ACRA, ad esempio :)

Grazie per tutti i vostri commenti.

+1

Ovviamente, è una cattiva pratica catturare 'RuntimeException's nel codice.Il fatto che questo debba essere fatto qui significa che c'è un bug nel tuo codice o un bug nel codice di Google. Guardando il codice di avviamento IAB, non sarei sorpreso se fosse il secondo (e in effetti, posso vedere diversi casi in cui le condizioni di gara potrebbero causare problemi nella classe 'IABHelper'). –

+0

Il bug è che Helper getta dove non dovrebbe mai. Possiede il thread che getta, l'eccezione non dovrebbe mai lasciare l'IabHelper. Non c'è nulla che un chiamante possa fare per impedirlo o gestirlo correttamente. –

+0

Questo bug è già stato risolto nell'API di Google? Di recente ho integrato l'API di fatturazione in app e ho affrontato questo problema nella produzione. Ho preso il codice da https://github.com/googlesamples/android-play-billing/tree/master/TrivialDrive come suggerito nei documenti Android. – Swapnil

2

Assicurarsi di implementare il metodo IabHelper.han.handleActivityResult(requestCode, resultCode, data) nel metodo onActivityResult delle attività.

@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

Sto ottenendo quello stesso errore ESATTO con praticamente lo stesso identico codice.

sembra solo di accadere su alcuni cellulari (in realtà, sembra quasi esclusivamente l'Acer Iconia Tablet a segnalazioni di errori recenti !!) - e sto occuperanno di onActivityResult ...

ci sono un certo numero di errori in il campione di fatturazione di Google v3 che può causare ANR/FC: sospetto che questo sia solo un altro (il codice scadente e i documenti scadenti stanno diventando un marchio di Google - purtroppo).

La mia ipotesi è - per ora - che dobbiamo tenere conto sia mHelper o mGotInventoryListener essere nullo e basta disabilitare in-app Billing in questo caso (come se result.isSuccess() era falsa, fondamentalmente)

ps aggiustato per aggiungere - potrebbe essere solo che l'utente ha una versione obsoleta del Play Store - che si aggiorna automaticamente solo se gli permettono di funzionare !?

0

Ottengo gli stessi errori. Ho anche avuto altri problemi ...

Avere più di un account Google su un dispositivo disabilitato la fatturazione in-app sul mio Galaxy Tab 7. Rimuovere un account riattivato.

Ho riscontrato problemi con il codice di esempio di fatturazione in app su GT-P5110, LGL75C e GT-S5839i.

(io uso il codice in un'applicazione con ACRA installata non ... così ogni volta si blocca, ottengo info)

I dispositivi Android versione varia da 2.3.3 a 4.0.4.

È molto fastidioso.

14

C'è un errore in IABHelper.Manca la linea di ritorno nel gestore delle eccezioni, il che significa che cade e chiama il metodo di successo - tuttavia, mSetupDone non è stato impostato, quindi ulteriori chiamate all'API falliscono. Aggiungi la dichiarazione di reso come segue: non funzionerà ancora, ma l'errore verrà segnalato correttamente alla tua app in modo da poter intraprendere le azioni appropriate.

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

Hai ragione. Mi piacerebbe notare che è già stato risolto da Google. – Ereza

+1

@Ereza Risolto forse, ma non ancora disponibile per il download utilizzando il gestore SDK al momento e data di questo commento. Per ora, ho appena inserito la dichiarazione di ritorno manualmente. – logray

+3

È lì ora – Gerard

1

si può tenere aggiornati con l'inapp sviluppo v3 API a https://code.google.com/p/marketbilling/

Il codice non è più recente rispetto a quella che è disponibile tramite il Gestore Android SDK.

+0

Credo che questo fornisca una soluzione all'autore. Sta usando una versione vecchia e buggata di una libreria e quindi lo indico a utilizzare la versione non buggata più recente. –

+1

Perché Google mantiene il codice in due punti? Qual è il padrone? E non è meglio consegnarlo in un repository git piuttosto che con il download dell'SDK? – powder366

10

Credo che ci siano ancora due bug nel codice Android, il che spiega il motivo per cui viene ancora visualizzato l'errore. Si noti che lo stack di chiamate si trova su un thread autonomo. Ma il codice che imposta mSetupDone (IabHelper) su true viene eseguito sul thread dell'interfaccia utente principale. Java non garantisce che i dati modificati da un thread saranno visibili all'altro thread a causa del caching della CPU a meno che non si dichiari la variabile con la parola chiave volatile. Quindi è possibile che sia stato impostato (mSetupDone == true), ma che il nuovo valore di mSetupDone sia memorizzato nella cache sul thread dell'interfaccia utente, non ancora visibile a questo thread nello stack delle chiamate. Quindi quel thread vede ancora mSetupDone == false.

Ho provato a risolvere questo problema dichiarando mSetupDone con volatile e anche ogni altro campo non finale di IabHelper solo per sicurezza.

Ora l'altro problema è la funzione .dispose(). Non ferma i thread in corso. Ciò significa che può impostare mSetupDone su false mentre è in esecuzione uno dei thread worker. Se guardi queryInventoryAsync(), vedrai che controlla che mSetupDone sia vero. E in base allo stack delle chiamate, ha superato quello. Quindi si è bloccato successivamente con mSetupDone == false. L'unico modo in cui ciò potrebbe accadere è se sono stati chiamati dispose() mentre il thread era in volo. Correggere è che dispose() ha bisogno di segnalare i thread per silenziare silenziosamente invece di continuare e lanciare errori quando vede mSetupDone == false. Questo impedisce anche un altro problema con IabHelper in cui le istanze disposte chiamano callback listener anche dopo essere state eliminate! È un po 'complicato spiegare riga per riga qui, ma si spera che ciò ti porti nella giusta direzione.

1

Oltre a @DavidM e @Ereza.

Un altro grosso problema con la classe IabHelpr è la scarsa scelta di lanciare RuntimeExcptions (IllegalStateException) in più metodi.Lanciare RuntimeExeptions dal proprio codice nella maggior parte dei casi non è consigliabile in quanto si tratta di eccezioni non selezionate. È come sabotare la tua applicazione. Se non vengono catturate, queste eccezioni si gonfieranno e causeranno il crash della tua app.

La soluzione a questo è implementare la propria controllata eccezione e modificare la classe IabHelper per lanciarla, anziché IllegalStateException. Questo ti costringerà a gestire questa eccezione ovunque possa essere lanciata nel tuo codice in fase di compilazione.

Qui è la mia eccezione personalizzata:

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

Una volta che facciamo i cambiamenti nella classe IabHelper, siamo in grado di gestire il nostro eccezione controllata nel nostro codice in cui noi chiamiamo i metodi della classe. Per esempio:

try { 
    setUpBilling(targetActivityInstance.allData.getAll()); 
} catch (MyIllegalStateException ex) { 
    ex.printStackTrace(); 
} 
1

Se tutti sopra non riescono a aiutare, dare un andare per cercare di analizzare il codice un po '- è il vostro IabHelper davvero istituito al momento si sta chiamando?

Mi sono trovato a fare un po 'male, senza rendermene conto. Un semplice esempio di utilizzo sbagliato 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 

.

Sopra onorerà con la "aiutante IAB non è impostato" errore dal momento che durante la vostra applicazione tenta di eseguire il m_iabHelper.queryInventoryAsync(), l'IabHelper non è ancora configurato. Prendi in considerazione l'utilizzo di queste funzioni in onIabSetupFinished() o da qualche parte dopo la chiamata di tale funzione (ad esempio all'esterno di onCreate())

0

I problemi con IABHelper.java erano molti.

In primo luogo, la versione scaricata da SDK Manager non viene aggiornata. Usa la versione trovata qui: https://code.google.com/p/marketbilling/source/detail?r=15946261ec9ae5f7c664d720f392f7787e3ee6c7 È la versione più aggiornata di questa risposta. Molti problemi sembrano essere stati risolti con questa versione rispetto alla versione iniziale che proviene da SDK Manager.

L'unica cosa che ho dovuto modificare in questa versione è l'aggiunta di flagEndAsync(); dopo la riga 404 che corregge un IllegalStateException quando 2 flussi di acquisto IAB vengono lanciati in rapida successione.

Con questa versione non è necessario gestire l'utilizzo di flagEndAsync(); nei file e lasciare che il metodo non sia pubblico.

Problemi correlati