En Facturación de aplicaciones – Orientación rápida de dispositivos – causa bloqueo (IllegalStateException)

Implementé la Facturación en la aplicación (v3) de acuerdo con la guía Implementación de la aplicación en Android de Android.

Todo funciona bien, hasta que gire el dispositivo, luego inmediatamente girarlo de nuevo a su orientación original. En realidad, a veces funciona, ya veces se bloquea con:

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

Parece que esto está relacionado con la naturaleza asincrónica de IAB, aunque no soy positivo.

¿Alguna idea?

Probablemente obtenga la excepción porque en algún lugar del ciclo de vida de la actividad, ha llamado mHelper.dispose (), y luego intentó usar esa misma instancia dispuesta más adelante. Mi recomendación es solo disponer de mHelper en onDestroy () y volver a crearlo en onCreate ().

Sin embargo, se ejecutará en otro problema con IabHelper y rotación del dispositivo. El problema es el siguiente: en la actividad onCreate (), creas la instancia de IabHelper mHelper y la configura. Más tarde, llama a mHelper.launchPurchaseFlow (…) y aparece el cuadro de diálogo emergente IAB flotando por encima de tu actividad. Luego gira el dispositivo y la instancia IabHelper se elimina en onDestroy (…) y luego se vuelve a crear en onCreate (…). El cuadro de diálogo IAB sigue apareciendo, presiona el botón de compra y se completa la compra. OnActivityResult () entonces se llama en su actividad, y naturalmente llama mHelper.handleActivityResult (…). El problema es que launchPurchaseFlow (…) nunca ha sido llamado en la instancia recreada de IabHelper. IabHelper solo maneja el resultado de la actividad en handleActivityResult (…) si launchPurchaseFlow (…) ha sido previamente llamado en la instancia actual. Su OnIabPurchaseFinishedListener nunca será llamado.

Mi solución a esto fue modificar IabHelper para permitirle decirle que esperara handleActivityResult (…) sin llamar a launchPurchaseFlow (…). Agregué lo siguiente a IabHelper.java

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

Esto hará que IabHelper llame onIabPurchaseFinished (…) al oyente cuando se llama a handleActivityResult (…). Entonces, usted hace esto:

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

Mi copia completa de IabHelper se puede encontrar aquí https://gist.github.com/benhirashima/7917645 . Tenga en cuenta que he actualizado mi copia de IabHelper con la versión encontrada en este commit , que corrige algunos errores y no se ha publicado en el SDK Manager de Android. También tenga en cuenta que hay compromisos más recientes , pero contienen nuevos errores y no deben utilizarse.

Esto es lo que hice:

El código para instanciar IabHelper y IabHelper llamada startSetup() está dentro de onCreate() , por lo que se recreará cuando se haga rotar el dispositivo, siempre y cuando no se trate de cambios de configuración por su cuenta.

Además, asegúrese de que está llamando .handleActivityResult() al principio de onActivityResult() . Esto asegurará que su referencia IabHelper se limpia correctamente después de cerrar el diálogo de compra.

Con esas dos cosas en su lugar, no debería ver más accidentes. Pero usted notará una cosa más:

Si inicia el diálogo de compra con una llamada a launchPurchaseFlow() y luego gira el dispositivo, el diálogo permanecerá abierto, pero ahora la referencia IabHelper su Activity se ha sobrescrito ya que onCreate() se llama a la rotación del dispositivo. Debido a esto, al cerrar el cuadro de diálogo, se llama al método handleActivityResult() , pero no coincide con el requestCode que pasó a launchPurchaseFlow() anteriormente, por onPurchaseFinishedListener que no se notificará onPurchaseFinishedListener . Para manejar este caso (rotaciones del dispositivo cuando el diálogo está abierto), necesitará manejar el onActivityResult() dentro de onActivityResult() . Puesto que el diálogo fue cerrado, querrás imitar lo que hiciste dentro de tu onPurchaseFinishedListener (descubre si el usuario realmente compró algo). Acabo de hacer una llamada a queryInventoryAsync() para encontrar eso.

No estoy seguro de si esa es la solución ideal, pero funciona bien para mí. Intenté colgar en la referencia de IabHelper como usted lo hizo, pero vi los problemas extraños donde perdería su estado de la disposición pero no permitiría que lo reajuste para arriba.

Una última cosa que hice fue actualizar las clases de facturación util con lo último de la fuente de Android. Hay algunas correcciones de errores que no se han enviado al gestor de SDK. La mayoría de ellos son cuestionables cheques nulos, pero hay algunas mejoras para evitar accidentes:

Último conjunto de cambios

Intenté hacer mHelper estático, y sólo instanciarlo si (mHelper == null), y NO destruirlo en el método onDestroy () de la actividad. Además, pase el contexto Aplicación a IabHelper. De esta manera, una vez que es la configuración, se pega alrededor, y no hay necesidad de preocuparse por operaciones asincrónicas (causas por la orientación del dispositivo) más.

He aquí un esquema de mi código:

 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 } 

No estoy seguro si esta es la solución correcta o no, pero pensé que lo mencionaría en caso de que ayudar a otros.

  • Añadir pestañas a la izquierda con FragmentPagerAdapter
  • Java.lang.IllegalStateException: No se puede realizar esta acción después de onSaveInstanceState
  • Android GoogleMaps V2 MarkerDemo IllegalStateException no incluye puntos
  • java.lang.IllegalStateException: Llamar Ver métodos en otro subproceso que el subproceso de interfaz de usuario
  • IllegalStateException en FragmentManager
  • Android - SQLite Cursor getColumnIndex () es sensible a mayúsculas y minúsculas?
  • Java.lang.IllegalStateException: No se puede realizar esta acción después de onSaveInstanceState, FragmentTabHost
  • "No se puede realizar esta acción después de onSaveInstanceState" - ¿por qué recibo esta excepción del método onResume de mi actividad?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.