¿Por qué no hay una API de Android para obtener la actividad actual?

La pregunta, ¿Cómo puedo obtener la Actividad actual? Se ha pedido docenas de veces en Stackoverflow y otros sitios y hay muchos enfoques propuestos. Sin embargo, todos ellos tienen desventajas de una forma u otra.

En esta publicación, estoy asumiendo que no hay ninguna solución prevista para esto en las API de Android, por ejemplo, algo así como: Application.getTask().getRootActivity() .

¿No sería agradable si había :-)?

Por lo tanto, para ser claro, no estoy pidiendo una respuesta a ¿Cómo puedo obtener la actividad actual?

En cambio, estoy preguntando por qué no se ha proporcionado tal capacidad. Dado que cada aplicación en ejecución tiene una tarea (suponiendo que la tarea no se ha vaciado) y cada tarea tiene una actividad raíz, parece fácil proporcionar acceso a esa actividad raíz.

El hecho de que ese acceso no se proporciona, cuando es tan claramente deseado, implica para mí que hay algo fundamental sobre la arquitectura de Android que no entiendo.

¿Qué es lo que me falta? ¿Por qué esta información no se proporciona en las API de Android?

Para el fondo, aquí es una sección que resume algunos de los acercamientos que se han propuesto. He encontrado los siguientes dos enlaces particularmente informativo (cada uno de los enfoques a continuación se presenta en uno o ambos de los enlaces).

Campo de golf

  • ¿Cómo obtener el actual contexto de actividad en primer plano en android?
  • Android: ¿Cómo puedo obtener la actividad de primer plano actual (de un servicio)?

Enfoques

  • Gancho estático
  • Reflexión
  • ActivityManager
  • Otros (Instrumentación, AccessibilityService, UsageStatsManager) `

ActivityManager

El enfoque de ActivityManager sólo proporciona el nombre de la clase de actividad, no la instancia de actividad actual. Por ejemplo, para una instancia de contexto c:

c.getSystemService().getActivityManager() .getAppTasks().get(0).getTaskInfo() .topActivity().getClassName()

Reflexión

Mi favorito es la reflexión, como propone el _AZ , pero ese enfoque es frágil, dado que se basa en internals. Lo que me gustaría ver desde Android es este enfoque proporcionado a través de una API estándar en la que los desarrolladores podrían confiar de forma segura.

Gancho estático

El enfoque más común es usar un gancho estático para guardar una referencia a la actividad en ejecución. El gancho puede ser por actividad o por aplicación. Se pueden evitar fugas de memoria al guardar / destruir el valor del gancho (por ejemplo, en onCreate () / onDestroy (), onStart () / onStop (), onPause () / onResume ()). Sin embargo, pueden surgir problemas cuando están involucradas múltiples actividades (por ejemplo, debido a la superposición de ciclos de vida, véase más adelante).

He implementado un enfoque de gancho estático que hace lo siguiente (para ser perfectamente transparente, no he implementado # 1 todavía – estoy utilizando un gancho estático por actividad, que es un error).

  1. Proporciona una clase que extiende Application para proporcionar el gancho. El gancho contiene una pila; Cada nodo de la pila es una clase ActivityInfo simple que contiene una referencia a una instancia de actividad, así como el estado de esa instancia (CREATED, STARTED, RESUMED).
  2. Proporciona una clase llamada ActivityTracker que extiende Actividad. A continuación, extiendo cada una de mis actividades con ActivityTracker. ActivityTracker utiliza sus devoluciones de llamada de ciclo de vida para empujar / pop mismo a / desde la pila y para actualizar su estado – mis otras actividades no tienen que hacer nada.

En teoría, esto me permitiría conocer siempre el estado completo de la pila trasera de la tarea: el conjunto completo de actividades, incluida la actividad raíz, así como su estado actual. En la práctica, sin embargo, hay un giro – cuando una actividad comienza otra actividad, sus ciclos de vida se superponen. Durante ese período, el mirar a escondidas en el tope de la pila puede producir una instancia de actividad inesperada.

De: https://developer.android.com/guide/components/activities/activity-lifecycle.html#soafa , "Actividades de coordinación":

Este es el orden de operaciones que se producen cuando la Actividad A comienza Acividad B:

  1. El método onPause () de la actividad A se ejecuta.
  2. Los métodos onCreate (), onStart () y onResume () de la actividad B se ejecutan en secuencia. (La actividad B ahora tiene enfoque de usuario.)
  3. A continuación, si la actividad A ya no es visible en la pantalla, su método onStop () se ejecuta

Por supuesto, esto podría ser manejado también. La conclusión es que tenemos un contexto global disponible para almacenar la información (la Aplicación) y tenemos información completa sobre las transiciones del ciclo de vida de la actividad, por lo que con suficiente esfuerzo creo que este enfoque basado en pila estática probablemente podría ser hecho bastante bullet- prueba.

Pero al final

Pero al final se siente como si estuviera simplemente reescribiendo el código que probablemente ya existe internamente para gestionar una pila de atrás de la actividad, por lo que le pregunto (en caso de que lo haya olvidado):

¿Por qué no hay una API de Android para obtener la actividad actual?


ACTUALIZAR


En esta actualización, resumiré lo que he aprendido de este hilo y mis propios experimentos e investigaciones. Esperemos que este resumen sea útil para otros.

Definiciones

Voy a usar las siguientes definiciones para "Estados de Visibilidad de la Actividad", basados ​​en las definiciones de Estado de Actividad en https://developer.android.com/guide/components/activities/activity-lifecycle.html .

 ----------------------------------- Visibility State Definition ----------------------------------- Not Visible Created+Stopped Partially Visible Started+Paused Fully Visible Resumed ----------------------------------- 

Cuestiones

La definición misma de "Actividad Actual" es oscura. Cuando la uso, me refiero a la única actividad en el estado totalmente visible. En cualquier momento dado, puede haber o no tal actividad. En particular, cuando la Actividad A comienza la Actividad B, se llama a onPause () de A y luego onCreate () de B, onStart () y onResume () seguido de OnStop de A (). Hay un estiramiento entre OnPause de A () y OnResume de B () donde ni está en el estado Fully Visible, así que no hay Current Activity (como lo defino). Por supuesto, también hay situaciones en las que un hilo de fondo puede querer acceder a una actividad actual y puede haber o no una actividad en absoluto, mucho menos una actividad actual.

También me di cuenta de que no siempre necesito una actividad actual ("Totalmente visible"). En muchos casos, puedo simplemente necesitar una referencia a una actividad existente, sea o no visible. Además, esa referencia podría ser cualquier actividad (para situaciones en las que necesito pasar una referencia de actividad genérica a algún método API) o podría ser una instancia específica de subclase de actividad (para poder activar un código específico para esa actividad Subclase).

Por último, existe la necesidad de comprender cuándo las llamadas de retorno del ciclo de vida de la actividad son llamadas por el intercomunicador principal de la interfaz de usuario y cómo se manejan los sucesos como cambios de configuración. Por ejemplo, si creo un DialogFragment con una actividad que actualmente está en el estado "No Visible", ¿se mostrará alguna vez y, si es así, cuándo? A lo largo de líneas similares, resulta que los métodos onDestroy () y onCreate () causados ​​por un cambio de configuración están contenidos en el mismo mensaje en la cola de mensajes de la interfaz de usuario (consulte la orden de envío de la cola de mensajes de subproceso de la interfaz de usuario de Android ) Procesados ​​entre esos dos callbacks (durante un cambio de configuración). Entender este nivel de procesamiento parece ser crítico, pero la documentación sobre él es muy poco, si no falta completamente.

Enfoques

Aquí hay una colección de enfoques que pueden usarse para resolver la mayoría de las situaciones anteriores.

Fondo

  • Para la discusión, asumir la actividad A y la actividad B, donde A crea B.
  • En general, se puede crear una variable "global" haciéndola "pública estática" en prácticamente cualquier clase. Conceptualmente, extender la clase Application y agregarla a la clase extendida sería bueno, pero si eso es demasiado trabajo podría ser incluido (por ejemplo) en una de las clases de Activity.

Referencia de actividad genérica

  • Útil cuando se necesita una actividad genérica.
  • Cree una variable global. En A y B, tiene onCreate () configurado en "this" y onDestroy () lo establece en null.

Referencia de actividad más alta

  • Útil siempre que desee acceder a la Actividad visible actualmente.
  • Cree una variable global. En A y B, tiene onResume () configurado en "this". Este método funciona bien a menos que todas las actividades salgan, en cuyo caso puede que necesite crear un indicador independiente para indicar esa situación. (Ese indicador podría ser la implementación de la Referencia de Actividad Genérica mencionada anteriormente).

Referencia específica de la actividad

  • Útil cuando se necesita un identificador para una instancia de subclase de actividad específica.
  • En A y B: cree una variable global en la propia subclase Activity. Tenemos onCreate () lo definimos como "this y onDestroy () lo configuramos en null.

Contexto de la aplicación

  • Útil cuando sea necesario un Contexto que abarque el ciclo de vida de toda la aplicación o cuando no se preocupe por usar un Contexto de Actividad específico (por ejemplo, para crear un Toast desde un subproceso de fondo).
  • Puede obtener esto de la actividad getApplication () y almacenarla en un gancho estático.

Manejo de cambios de configuración

Puede haber ocasiones en las que desea detener / iniciar un subproceso de fondo sólo a través de una sesión de actividad, donde defino "sesión" para incluir la serie de instancias de actividad que se pueden crear y destruir debido a cambios de configuración. En mi caso particular, tengo una actividad de charla de Bluetooth y un hilo de fondo asociado para manejar la conexión de red. No quiero que la conexión sea destruida y creada cada vez que el usuario rota el dispositivo, por lo que necesito crearlo solo cuando no existe y destruirlo solo si no hay un cambio de configuración en curso. La clave aquí es entender cuando onDestroy () se llama debido a un cambio de configuración. Esto se puede hacer con o sin fragmentos. Como es a menudo el caso, yo prefiero el enfoque de no-fragmento desde el enfoque de fragmento no parece digno de la complejidad extra para mí.

Enfoque 1: Sin fragmentos

En onCreate (), cree el subproceso de fondo si aún no existe. En onDestroy (), destruya el subproceso de fondo sólo si isFinally () devuelve false.

Enfoque 2: con fragmentos

Esto funciona bien porque el FragmentManager almacenará instancias de fragmentos a través de cambios de configuración si se usa setRetainInstance (true). Para un ejemplo excelente de esto, vea http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html . El ejemplo es para AsyncTasks, pero también puede aplicarse a la gestión de un subproceso de fondo (solo cree el subproceso en lugar de un AsyncTask en el onCreate del fragmento) y luego destruya el subproceso en onDestroy () del fragmento.

Clausura

Entender estos problemas requiere una comprensión profunda de cómo el interceptor de interfaz de usuario procesa su cola de mensajes: cuando se llaman llamadas de actividad, cómo se intercalan otros mensajes con ellos, cuando se producen actualizaciones de visualización, etc. Por ejemplo, si se crea un DialogFragment utilizando un Ejemplo de una actividad no visible, se mostrará en absoluto y, en caso afirmativo, cuándo? Tal vez algún día Android proporcionará una API más profunda a las tareas y sus contratiempos asociados, junto con la documentación que describe el procesamiento de mensajes de la interfaz de usuario y los mecanismos asociados en más detalle. Hasta entonces, más "código fuente y / o … análisis empírico" :-).

Gracias,

Barry

La respuesta corta que yo diría es que sólo una actividad puede ser activa en un momento en una aplicación determinada, y que la actividad obviamente sabe quién es (es en sí mismo) – por lo que la única respuesta a cualquier actividad puede llegar a "qué actividad Está actualmente activo "sólo puede ser" eres, tonto ".

Para aplicaciones simples con una clara división entre las diferentes clases de actividad, esto funciona bien, y por lo tanto, es un gran porcentaje de la mayoría de las aplicaciones de la tienda de juegos. No funciona tan caliente cuando te estás poniendo muy inteligente con la encapsulación y el polimorfismo, como estoy seguro que has descubierto, pero no creo que Google esté realmente orientado a esos tipos de desarrolladores.

Sólo mis $ 0.02, no creo que obtendrá una respuesta "oficial" aquí.

Si todo lo que quieres saber es cuál es la Actividad principal y acepta las interacciones de los usuarios, solo crea una BaseActivity que extiende Actividad y anula onResume () y guarda una referencia a "this" en una variable estática. Todas sus otras actividades deben extender BaseActivity. Ya terminaste.

  • Iniciar / Detener servicio desde Widget
  • Android proceso remoto: messenger vs aidl? ¿Cual es mejor?
  • ¿Cómo dirigir la queja de la pelusa del android sobre las implementaciones exportadas del servicio de la mensajería de Firebase?
  • Servicio de fondo con oyente de localización en android
  • Servicio o Thread o AsyncTask
  • ¿Cómo dejar de ejecutar los servicios?
  • ¿Cómo mantener un IntentService funcionando incluso cuando la aplicación está cerrada?
  • cómo obtener la vista de la raíz de una actividad de servicio en android?
  • Obtenga la ubicación de Android GPS una vez cada pocos minutos
  • Cómo mantener la escucha de notificaciones push en Android en segundo plano
  • Cómo actualizar la interfaz de usuario desde el servicio de Android utilizando RxJava / RxAndroid
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.