Detectar un anuncio intersticial bloqueado en Android con eventos
Por Nesim Razon, desarrollador Android independiente · 2026-11-12
Un jugador escribió que, tras un anuncio intersticial, el juego dejó de responder a los toques. No había ningún fallo en Crashlytics, ningún ANR y no podíamos reproducirlo en nuestros propios móviles. Esta entrada explica cómo añadimos un pequeño registro del ciclo de vida alrededor de los anuncios a pantalla completa de AdMob, qué aspecto tienen los datos y qué hicimos con ellos. Es una historia de depuración, no una receta válida para todas las aplicaciones.
El aviso que no tenía traza de pila
El aviso llegó de un solo usuario con un teléfono Redmi con HyperOS y Android 13. Al final de un nivel apareció un anuncio a pantalla completa. Cuando terminó, el juego parecía normal, pero no hacía caso al jugador. Los toques parecían caer en otro sitio: el panel de Play Store se abría solo y el botón del siguiente nivel no hacía nada. El jugador contó que ya le había pasado una vez con el mismo tipo de anuncio.
Visto desde fuera parece un bloqueo, pero la aplicación no estaba bloqueada. El hilo principal funcionaba y nada lanzaba una excepción. Por eso las herramientas habituales no dijeron nada.
Por qué el informe de fallos no lo ve
Crashlytics registra las excepciones fatales y, si se lo pides, también las no fatales. Un anuncio bloqueado no es ninguna de las dos cosas. El SDK de anuncios ejecuta su propia actividad y una vista web; si el contenido se comporta mal, la ventana puede quedarse encima del juego como una capa transparente. Tu código nunca falla. El usuario simplemente no puede llegar a él.
Los informes de ANR tampoco ayudan, porque la aplicación sigue respondiendo a los eventos de entrada. La única parte que sabe que algo fue mal es el SDK de anuncios, y no nos lo dijo. Así que la primera tarea fue hacer visible lo invisible: anotar, en dispositivos reales y por orden, lo que hacía el anuncio.
La idea: registrar cada etapa del anuncio
AdMob ofrece devoluciones de llamada para la carga y para el contenido a pantalla completa. Conectamos un pequeño registrador a cada una, tanto en el formato intersticial como en el de recompensa. Cada llamada genera un evento de Firebase Analytics llamado ad_event con estos parámetros: format (interstitial o rewarded), stage, adapter, code y ms.
Las etapas en nuestro código son loaded, load_failed, shown, impression, clicked, dismissed, show_failed y una más que inventamos, resumed_no_dismiss. El adapter es el nombre de la clase del adaptador de mediación que sirvió el anuncio, recortado a sus últimos 60 caracteres, de modo que un solo evento indica qué fuente de anuncios intervino. El campo code lleva el código de error del SDK en los fallos, y ms es el tiempo transcurrido desde que se mostró el anuncio. La misma línea se escribe también como rastro en Crashlytics, así que si más tarde ocurre un fallo, su informe muestra qué etapa del anuncio vino antes.
La única etapa que el SDK no da
Todo anuncio normal termina con una devolución de llamada dismissed. Un anuncio bloqueado es el caso en que esa llamada nunca llega, de modo que no se puede registrar desde una devolución de llamada. Hay que notar la ausencia.
Lo resolvimos con el ciclo de vida de la aplicación. Cuando se muestra un anuncio, el envoltorio guarda su formato, su adaptador y la hora de inicio. Después un oyente global del ciclo de vida de actividades vigila onActivityResumed. Si una de nuestras actividades vuelve al primer plano mientras el envoltorio sigue creyendo que hay un anuncio en pantalla, el anuncio debería haberse cerrado pero no nos avisó. Ignoramos las actividades del propio SDK de anuncios, exigimos que hayan pasado al menos 1,5 segundos desde que se mostró para evitar condiciones de carrera, y lo notificamos solo una vez por anuncio. El resultado es un evento resumed_no_dismiss con el tiempo transcurrido. El oyente solo registra. Nunca toca el juego, así que no puede empeorar nada.
Convertir los eventos en una tabla
Los eventos en bruto no son un diagnóstico. Extraemos las cifras con la API de datos de GA4 en un informe semanal. Para que funcione, format y stage deben estar registrados en GA4 como dimensiones personalizadas con ámbito de evento; si no, la consulta devuelve un error. El informe agrupa por formato y etapa y muestra dos números en cada celda: cuántos eventos y cuántos usuarios.
Si lees una columna de arriba abajo, ves la vida de un anuncio: loaded, shown, impression, dismissed. La forma sana es que dismissed se acerque a shown. El informe añade una línea más: los eventos de bloqueo divididos entre los de visualización, en porcentaje. El panel avisa cuando el número de resumed_no_dismiss es mayor que cero y remite a las herramientas de revisión de anuncios de AdMob. La pareja que hay que buscar es shown sin dismissed, y el evento resumed_no_dismiss la hace explícita.
Qué encontramos y qué cambiamos
La descripción del usuario apuntaba a un anuncio concreto de un juego de terceros que a veces dejaba una capa transparente en pantalla. Como el anuncio llegaba por el camino normal de AdMob, la solución estaba en el lado de la demanda y no en nuestro código: bloqueamos el dominio de ese anunciante en los controles de bloqueo de AdMob. En esa misma versión no cambiamos a propósito la forma en que el juego muestra anuncios. Primero medimos, para poder comparar una corrección posterior con una referencia.
Conviene ser honestos sobre las pruebas. La primera señal fue el aviso de un solo usuario, más saber que ese mismo anuncio ya había dado problemas. El registro se añadió para saber con qué frecuencia ocurre el problema y qué adaptador hay detrás; el bloqueo fue una respuesta dirigida al anuncio que describió el usuario. No tengo un experimento controlado de antes y después, y con pocos jugadores las cifras son pequeñas. Toma nuestros números como una alarma de humo, no como una medición con intervalo de confianza.
Límites y privacidad
La heurística puede dar falsos positivos. Un dispositivo que devuelva nuestra actividad al primer plano por otro motivo mientras el envoltorio aún cree que hay un anuncio podría contarse; por eso existen el umbral y la regla de una vez por anuncio. También puede pasar por alto un anuncio bloqueado si el usuario fuerza el cierre de la aplicación. Además, el SDK muestrea y agrupa los eventos de Analytics, así que las cifras llegan con uno o dos días de retraso.
En cuanto a la privacidad: el evento solo lleva el formato, la etapa, el nombre de clase del adaptador recortado, un código de error y una duración. No hay ningún identificador de usuario en nuestros parámetros y el comentario del código indica que no se recoge el identificador de publicidad. Analytics solo está activo en la versión de lanzamiento, para que las pruebas no ensucien las cifras. Para los anuncios usamos además el flujo habitual de consentimiento de la plataforma de mensajes de usuario de Google. Si tu aplicación recoge analítica en regiones con normas de consentimiento, comprueba cómo limita tu configuración Firebase Analytics; aquí no hemos verificado una configuración concreta del modo de consentimiento.
Qué conclusión sacar
Si muestras anuncios, probablemente tienes un punto ciego entre mostrar y cerrar. Unas pocas decenas de líneas pueden cerrarlo: registra cada devolución de llamada, deduce la que falta a partir del ciclo de vida de las actividades y representa el formato por etapa. Mantén el registrador pasivo. Informa de lo que ocurrió y no intentes arreglar la pantalla automáticamente hasta saber qué estás arreglando.
Un anuncio bloqueado te cuesta de dos maneras. El jugador deja una mala reseña y pierdes la sesión. Ambas cosas son difíciles de atribuir sin datos. Con una tabla de ciclo de vida, el siguiente aviso de un usuario deja de ser una historia que no puedes comprobar y pasa a ser una fila que puedes consultar.
Preguntas frecuentes
¿Por qué Crashlytics no muestra un anuncio intersticial bloqueado?
Porque no hay ningún fallo. La ventana del anuncio se queda encima de la aplicación mientras esta sigue funcionando, así que no hay excepción ni ANR que registrar.
¿Cómo se detecta un anuncio que no se cierra?
Guarda la hora en que se mostró el anuncio y vigila el ciclo de vida de las actividades. Si una de tus actividades vuelve al primer plano mientras el anuncio sigue marcado como visible, notifica un evento como resumed_no_dismiss.
¿Qué parámetros se registran con cada evento de anuncio?
El formato del anuncio, la etapa, el nombre de clase del adaptador recortado, un código de error en los fallos y los milisegundos transcurridos desde que se mostró.
¿Hace falta registrar los parámetros en GA4?
Sí. Para consultar format y stage con la API de datos de GA4, regístralos como dimensiones personalizadas con ámbito de evento.
¿Cómo se resolvió el anuncio bloqueado?
Bloqueando el dominio del anunciante en AdMob. El registro no cambió el comportamiento; sirve para medir con qué frecuencia ocurre el problema.
Más del blog
- Cómo dar el mismo puzle diario a todos sin un servidor
- Cómo se generan y verifican cientos de niveles de puzle
- Dos docenas de apps Android mantenidas por un solo dev
Cómo garantizamos que un Mahjong Solitario tenga solución
Historia del Mahjong Solitario y su diferencia con el Mahjong- Tres en raya: historia de un juego resuelto y sus variantes