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 (No se puede realizar esta acción después de onSaveInstanceState)
- Java.lang.IllegalStateException: Fragmento no adjunto a la actividad
- Haga clic en el elemento que conduce a IllegalStateException
- GetSupportFragmentManager () causa java.lang.IllegalStateException: No se puede realizar esta acción después de onSaveInstanceState
- OnViewCreated - lugar equivocado para reemplazar el fragmento?
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?
- IllegalStateException con PagerAdapter
- Android.media.audiofx.Visualizer lanzando excepción cada otra vez
- Fragmento inflado en Fragmento El cuadro de diálogo arroja el error "Fragmento no creó una vista"
- IllegalStateException cuando .replace el fragmento al reiniciar
- Intento de volver a abrir un objeto ya cerrado: java.lang.IllegalStateException :?
- Excepción de estado ilegal al intentar cambiar un marcador en un Api de Google Maps v2 desde un mensaje de Google Cloud Messaging
- AudioTrack: play () llamado en AudioTrack no inicializado
- Android: no se puede destruir la actividad después de terminar, causada por IllegalStateException: No se puede realizar esta acción después de onSaveInstanceState
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.
- Acciones de aplicación predefinidas. Enlace a mi aplicación de Contactos
- Retener el oyente tras la rotación de la pantalla – DialogFragment con DatePicker