¿Cómo declarar variables globales en Android?


595

Estoy creando una aplicación que requiere inicio de sesión. Creé la actividad principal y de inicio de sesión.

En el onCreatemétodo de actividad principal agregué la siguiente condición:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    ...

    loadSettings();
    if(strSessionString == null)
    {
        login();
    }
    ...
}

El onActivityResultmétodo que se ejecuta cuando finaliza el formulario de inicio de sesión tiene este aspecto:

@Override
public void onActivityResult(int requestCode,
                             int resultCode,
                             Intent data)
{
    super.onActivityResult(requestCode, resultCode, data);
    switch(requestCode)
    {
        case(SHOW_SUBACTICITY_LOGIN):
        {
            if(resultCode == Activity.RESULT_OK)
            {

                strSessionString = data.getStringExtra(Login.SESSIONSTRING);
                connectionAvailable = true;
                strUsername = data.getStringExtra(Login.USERNAME);
            }
        }
    }

El problema es que el formulario de inicio de sesión a veces aparece dos veces (el login()método se llama dos veces) y también cuando se desliza el teclado del teléfono, el formulario de inicio de sesión aparece nuevamente y supongo que el problema es la variable strSessionString.

¿Alguien sabe cómo configurar la variable global para evitar que aparezca el formulario de inicio de sesión después de que el usuario ya se haya autenticado correctamente?


un buen tutorial sobre cómo manejar un estado de actividad utilizando el paquete de estado de instancia guardado quicktips.in/…
Deepak Swami

Respuestas:


954

Escribí esta respuesta en 2009 cuando Android era relativamente nuevo, y había muchas áreas no bien establecidas en el desarrollo de Android. He agregado un largo apéndice al final de esta publicación, que aborda algunas críticas y detalla un desacuerdo filosófico que tengo con el uso de Singletons en lugar de subclasificar la Aplicación. Léalo bajo su propio riesgo.

RESPUESTA ORIGINAL:

El problema más general que encuentra es cómo guardar el estado en varias Actividades y todas las partes de su aplicación. Una variable estática (por ejemplo, un singleton) es una forma común de Java para lograr esto. Sin embargo, he descubierto que una forma más elegante en Android es asociar su estado con el contexto de la Aplicación.

Como saben, cada Actividad es también un Contexto, que es información sobre su entorno de ejecución en el sentido más amplio. Su aplicación también tiene un contexto, y Android garantiza que existirá como una instancia única en su aplicación.

La forma de hacerlo es crear su propia subclase de android.app.Application , y luego especificar esa clase en la etiqueta de la aplicación en su manifiesto. Ahora Android creará automáticamente una instancia de esa clase y la pondrá a disposición de toda su aplicación. Puede acceder a él desde cualquier lugar contextutilizando el Context.getApplicationContext()método ( Activitytambién proporciona un método getApplication()que tiene exactamente el mismo efecto). El siguiente es un ejemplo extremadamente simplificado, con advertencias a seguir:

class MyApp extends Application {

  private String myState;

  public String getState(){
    return myState;
  }
  public void setState(String s){
    myState = s;
  }
}

class Blah extends Activity {

  @Override
  public void onCreate(Bundle b){
    ...
    MyApp appState = ((MyApp)getApplicationContext());
    String state = appState.getState();
    ...
  }
}

Esto tiene esencialmente el mismo efecto que usar una variable estática o singleton, pero se integra bastante bien en el marco de Android existente. Tenga en cuenta que esto no funcionará en todos los procesos (en caso de que su aplicación sea una de las raras que tiene múltiples procesos).

Algo a tener en cuenta en el ejemplo anterior; supongamos que hubiéramos hecho algo como:

class MyApp extends Application {

  private String myState = /* complicated and slow initialization */;

  public String getState(){
    return myState;
  }
}

Ahora esta lenta inicialización (como golpear el disco, golpear la red, bloquear cualquier cosa, etc.) se realizará cada vez que se instancia la aplicación. Puedes pensar, bueno, esto es solo una vez para el proceso y tendré que pagar el costo de todos modos, ¿verdad? Por ejemplo, como Dianne Hackborn menciona a continuación, es completamente posible que su proceso sea instanciado, solo para manejar un evento de transmisión en segundo plano. Si su procesamiento de transmisión no necesita este estado, puede haber realizado una serie de operaciones complicadas y lentas por nada. La instanciación perezosa es el nombre del juego aquí. La siguiente es una forma un poco más complicada de usar la aplicación que tiene más sentido para cualquier cosa que no sea el uso más simple:

class MyApp extends Application {

  private MyStateManager myStateManager = new MyStateManager();

  public MyStateManager getStateManager(){
    return myStateManager ;
  }
}

class MyStateManager {

  MyStateManager() {
    /* this should be fast */
  }

  String getState() {
    /* if necessary, perform blocking calls here */
    /* make sure to deal with any multithreading/synchronicity issues */

    ...

    return state;
  }
}

class Blah extends Activity {

  @Override
  public void onCreate(Bundle b){
    ...
    MyStateManager stateManager = ((MyApp)getApplicationContext()).getStateManager();
    String state = stateManager.getState();
    ...
  }
}

Si bien prefiero las subclases de aplicaciones a usar singletons aquí como la solución más elegante, prefiero que los desarrolladores usen singletons si es realmente necesario en lugar de no pensar en absoluto en el rendimiento y las implicaciones de subprocesos múltiples de asociar el estado con la subclase de aplicaciones.

NOTA 1: También como comentó anticafe, para vincular correctamente la anulación de su Aplicación a su aplicación, es necesaria una etiqueta en el archivo de manifiesto. Nuevamente, vea los documentos de Android para obtener más información. Un ejemplo:

<application
     android:name="my.application.MyApp" 
     android:icon="..."
     android:label="...">
</application>

NOTA 2: user608578 pregunta a continuación cómo funciona esto con la gestión de los ciclos de vida de objetos nativos. No estoy al tanto de usar código nativo con Android en lo más mínimo, y no estoy calificado para responder cómo interactuaría eso con mi solución. Si alguien tiene una respuesta a esto, estoy dispuesto a darles crédito y poner la información en esta publicación para una visibilidad máxima.

APÉNDICE:

Como algunas personas han notado, esta no es una solución para el estado persistente , algo que quizás debería haber enfatizado más en la respuesta original. Es decir, esto no está destinado a ser una solución para guardar información del usuario u otra información que debe persistir a lo largo de la vida útil de las aplicaciones. Por lo tanto, considero que la mayoría de las críticas a continuación se relacionan con aplicaciones que se eliminan en cualquier momento, etc., discutible, ya que todo lo que alguna vez necesitó persistir en el disco no debe almacenarse a través de una subclase de aplicación. Está destinado a ser una solución para almacenar el estado de aplicación temporal, fácilmente reproducible (si un usuario ha iniciado sesión, por ejemplo) y componentes que son de instancia única (administrador de red de aplicación, por ejemplo) (¡ NO singleton!) En la naturaleza.

Dayerman ha tenido la amabilidad de señalar una conversación interesante con Reto Meier y Dianne Hackborn en la que se desaconseja el uso de subclases de aplicaciones a favor de los patrones Singleton. Somatik también señaló algo de esta naturaleza antes, aunque no lo vi en ese momento. Debido al papel de Reto y Dianne en el mantenimiento de la plataforma Android, no puedo recomendar de buena fe ignorar sus consejos. Lo que dicen, va. Deseo no estar de acuerdo con las opiniones, expresadas con respecto a preferir Singleton sobre las subclases de aplicación. En mi desacuerdo, haré uso de los conceptos mejor explicados en esta explicación de StackExchange del patrón de diseño Singleton, para que no tenga que definir términos en esta respuesta. Recomiendo leer el enlace antes de continuar. Punto por punto:

Dianne dice: "No hay ninguna razón para subclase de la aplicación. No es diferente a hacer un singleton ..." Esta primera afirmación es incorrecta. Existen dos motivos principales para esto. 1) La clase de aplicación proporciona una mejor garantía de por vida para un desarrollador de aplicaciones; Se garantiza que tendrá la vida útil de la aplicación. Un singleton no está EXPLÍCITAMENTE vinculado a la vida útil de la aplicación (aunque sí lo es). Esto puede no ser un problema para un desarrollador de aplicaciones promedio, pero diría que este es exactamente el tipo de contrato que la API de Android debería ofrecer, y también proporciona mucha más flexibilidad al sistema Android, al minimizar la vida útil de los asociados. datos. 2) La clase de aplicación proporciona al desarrollador de la aplicación un único titular de instancia para el estado, que es muy diferente de un titular de estado Singleton. Para obtener una lista de las diferencias, consulte el enlace de explicación Singleton arriba.

Dianne continúa: "... es probable que sea algo de lo que se arrepienta en el futuro cuando encuentre que su objeto Aplicación se convierta en este gran enredo de lo que debería ser una lógica de aplicación independiente". Esto ciertamente no es incorrecto, pero esta no es una razón para elegir Singleton sobre la subclase de aplicación. Ninguno de los argumentos de Diane proporciona una razón por la cual usar un Singleton es mejor que una subclase de Aplicación, todo lo que intenta establecer es que usar un Singleton no es peor que una subclase de Aplicación, lo cual creo que es falso.

Ella continúa: "Y esto conduce de manera más natural a cómo se deben manejar estas cosas, inicializándolas a pedido". Esto ignora el hecho de que no hay ninguna razón por la que no pueda inicializar a pedido utilizando una subclase de aplicación también. De nuevo no hay diferencia.

Dianne termina con "El marco en sí tiene toneladas y toneladas de singletons para todos los pocos datos compartidos que mantiene para la aplicación, como cachés de recursos cargados, grupos de objetos, etc. Funciona muy bien". No estoy argumentando que usar Singletons no puede funcionar bien o no es una alternativa legítima. Estoy argumentando que Singletons no proporciona un contrato tan sólido con el sistema Android como el uso de una subclase de aplicaciones, y además que el uso de Singletons generalmente apunta a un diseño inflexible, que no se modifica fácilmente, y conduce a muchos problemas en el futuro. En mi humilde opinión, el fuerte contrato que la API de Android ofrece a las aplicaciones de desarrollador es uno de los aspectos más atractivos y agradables de la programación con Android, y ayudó a llevar a la adopción temprana del desarrollador que llevó a la plataforma Android al éxito que tiene hoy.

Dianne también ha comentado a continuación, mencionando una desventaja adicional del uso de subclases de aplicaciones, pueden alentar o facilitar la escritura de menos código de rendimiento. Esto es muy cierto, y he editado esta respuesta para enfatizar la importancia de considerar el rendimiento aquí y tomar el enfoque correcto si está utilizando la subclasificación de aplicaciones. Como dice Dianne, es importante recordar que su clase de Aplicación se instanciará cada vez que se cargue su proceso (¡podría ser varias veces a la vez si su aplicación se ejecuta en múltiples procesos!) Incluso si el proceso solo se carga para una transmisión en segundo plano evento. Por lo tanto, es importante utilizar la clase de aplicación más como un repositorio para punteros a componentes compartidos de su aplicación en lugar de como un lugar para realizar cualquier procesamiento.

Os dejo con la siguiente lista de desventajas de Singletons, tal como me lo robaron del enlace anterior de StackExchange:

  • Incapacidad para usar clases abstractas o de interfaz;
  • Incapacidad de subclase;
  • Alto acoplamiento a través de la aplicación (difícil de modificar);
  • Difícil de probar (no puede fingir / burlarse de las pruebas unitarias);
  • Difícil de paralelizar en el caso de estado mutable (requiere un bloqueo extenso);

y agrego el mío:

  • Contrato de por vida poco claro e inmanejable no apto para el desarrollo de Android (o la mayoría de los demás);

93
Gracias Soonil: este tipo de respuestas son la razón por la que amo tanto Stack Overflow. ¡GRAN TRABAJO!
JohnnyLambada

55
Para cualquiera que se pregunte cómo "especificar esa clase en la etiqueta de la aplicación en su manifiesto", hay, al momento de escribir este artículo, otras dos respuestas para esta pregunta que describen cómo hacerlo (use android: nombre), una por ebuprofeno y otra por Mike Brown.
Tyler Collier

99
Pronto, su respuesta es correcta, pero ¿podría notar que deberíamos agregar <application android: name = ". MyApp" ... /> en el archivo de manifiesto de Android?
anticafe

12
Permíteme repetir una vez más, no deberías estar usando la Aplicación para globales. No sirve de nada, no ofrece beneficios sobre los singletons y puede ser activamente perjudicial, como perjudicar el rendimiento del inicio de su proceso. En el momento en que se crea la aplicación, no tiene idea de para qué se está creando su proceso. Inicializando perezosamente los singletons según sea necesario, solo necesita hacer el trabajo que sea necesario. Por ejemplo, si su proceso se está iniciando para manejar una transmisión sobre algún evento en segundo plano, no hay razón para inicializar el estado global que necesita su UI.
hackbod

14
Además, seamos realmente claros aquí: todos sus argumentos en contra de los singletons son perfectamente válidos, cuando hablamos de situaciones en las que realmente está eligiendo entre un singleton y otro enfoque que no es global; Los singletons son globales, con todas las advertencias sobre los globales que se aplican. Sin embargo, la aplicación también es un singleton . No está escapando de esos problemas cambiando a la aplicación de subclases, una aplicación es exactamente igual a un singleton (pero peor), solo le permite engañarse a sí mismo de que está haciendo algo más limpio. Pero no lo eres.
hackbod

153

Crea esta subclase

public class MyApp extends Application {
  String foo;
}

En AndroidManifest.xml agregue android: nombre

Ejemplo

<application android:name=".MyApp" 
       android:icon="@drawable/icon" 
       android:label="@string/app_name">

1
gracias por eso. Me preguntaba cómo declararlo en el manifiesto
alguien en algún lugar

3
Para que funcione para mí, tuve que eliminar el "." dentro de ".MyApp"
Alguien en algún lugar

3
simplemente declararlo después de la actividad principal, de lo contrario no se puede instalar / implementar
sami

11
solo quiero decir, esto va en la etiqueta de aplicación PRINCIPAL que ya está allí ... esta no es una segunda :) tuvo que aprender de la manera difícil.
bwoogie

java.lang.IllegalAccessException: access to class is not allowed
Raptor

142

La forma sugerida por Soonil de mantener un estado para la aplicación es buena, sin embargo, tiene un punto débil: hay casos en que el sistema operativo mata todo el proceso de la aplicación. Aquí está la documentación sobre esto: procesos y ciclos de vida .

Considere un caso: su aplicación pasa a un segundo plano porque alguien lo está llamando (la aplicación del teléfono está en primer plano ahora). En este caso, && bajo otras condiciones (consulte el enlace anterior para ver cuáles podrían ser) el sistema operativo puede matar su proceso de solicitud, incluida la Applicationinstancia de la subclase. Como resultado, el estado se pierde. Cuando más tarde regrese a la aplicación, el sistema operativo restaurará su Applicationinstancia de pila de actividad y subclase, sin embargo, el myStatecampo será null.

AFAIK, la única forma de garantizar la seguridad del estado es usar cualquier tipo de persistencia del estado, por ejemplo, usar un archivo privado para el archivo de la aplicación o SharedPrefernces(eventualmente usa un archivo privado para el archivo de la aplicación en el sistema de archivos interno).


10
+1 por persistir con SharedPreferences; así es como lo he visto hacer. Me resulta extraño abusar del sistema de preferencias para el estado guardado, pero funciona tan bien que el problema se convierte en una cuestión de terminología.
Cheezmeister

1
¿podría publicar el código (o proporcionar un enlace a una explicación) sobre cómo se utiliza SharedPreferences para resolver el problema que describe Arhimed
Alguien en algún lugar

2
Preferencias, base de datos, serialización de archivos, etc. Cada actividad puede mantener el estado si usa onSaveInstanceState, pero no ayudará si el usuario se retira de la actividad y la elimina de la pila del historial, cierra la fuerza o apaga su dispositivo .
Darren Hinderer

1
Este comportamiento es muy molesto: no sería tan malo si se llamara al método onTerminate () de su aplicación para que pueda manejar la situación con elegancia.
Dean Wild

2
Esta es la respuesta correcta en mi opinión. Es un error confiar en la misma instancia de aplicación que existe en todas las actividades. En mi experiencia, es bastante común que Android derribe por completo y vuelva a crear todo su proceso mientras está en segundo plano. Estar en segundo plano podría significar simplemente iniciar una intención de cámara, intención de navegador o recibir una llamada telefónica.
Jared Kells el

26

Solo una nota ..

añadir:

android:name=".Globals"

o lo que sea que haya llamado su subclase a la etiqueta existente <application> . Seguí intentando agregar otra <application>etiqueta al manifiesto y obtendría una excepción.


Hola gimbl Yo tuve el mismo problema. También tenía mi propia etiqueta <application> y, cuando trato de agregar otra etiqueta <application>, tuve el mismo problema que usted (mensaje de excepción). Pero hice lo que mencionaste, y no funcionó. Agrego android: name = ". GlobalClass" a mi etiqueta <application> pero no funciona. ¿Puedes explicar completamente cómo lo resolviste?
Sonhja

3
Buena <manifest> <aplicación android: name = ". GlobalData"> </application> </manifest>. Mal <manifest> <aplicación> </ application> <aplicación android: name = "GlobalData."> </ Application> </ manifest>
Gimbl

13

Tampoco pude encontrar cómo especificar la etiqueta de la aplicación, pero después de mucho Googlear, se hizo evidente a partir de los documentos del archivo de manifiesto: use android: name, además del icono y la etiqueta predeterminados en la sección de la aplicación.

android: nombre El nombre completo de una subclase de aplicación implementada para la aplicación. Cuando se inicia el proceso de solicitud, esta clase se instancia antes que cualquiera de los componentes de la aplicación.

La subclase es opcional; La mayoría de las aplicaciones no necesitarán una. En ausencia de una subclase, Android utiliza una instancia de la clase de aplicación base.


13

¿Qué hay de garantizar la recopilación de memoria nativa con tales estructuras globales?

Las actividades tienen un onPause/onDestroy()método que se llama destrucción, pero la clase Aplicación no tiene equivalentes. ¿Qué mecanismo se recomienda para garantizar que las estructuras globales (especialmente aquellas que contienen referencias a la memoria nativa) se recolecten de manera apropiada cuando la aplicación se cierra o la pila de tareas se pone en segundo plano?


1
La solución obvia es implementar la interfaz Cerrable para sus objetos responsables de los recursos nativos y asegurarse de que sean administrados por una declaración de prueba con recursos u otra cosa. En el peor de los casos, siempre puede usar un finalizador de objetos.
pronto

5

Solo necesita definir un nombre de aplicación como el siguiente que funcionará:

<application
  android:name="ApplicationName" android:icon="@drawable/icon">
</application>

4

Como se discutió anteriormente, el sistema operativo podría matar la APLICACIÓN sin ninguna notificación (no hay evento onDestroy), por lo que no hay forma de guardar estas variables globales.

SharedPreferences podría ser una solución, EXCEPTO que tenga variables ESTRUCTURADAS COMPLEJAS (en mi caso, tuve una matriz entera para almacenar las ID que el usuario ya ha manejado). El problema con SharedPreferences es que es difícil almacenar y recuperar estas estructuras cada vez que se necesitan los valores.

En mi caso, tenía un SERVICIO en segundo plano para poder mover estas variables allí y debido a que el servicio tiene un evento onDestroy, podría guardar esos valores fácilmente.


No se garantiza que onDestroy () sea llamado incluso para un servicio.
Aprenda OpenGL ES

Sí, esto podría suceder, pero solo en caso de situaciones críticas.
Adorjan Princz

4

Si algunas variables se almacenan en sqlite y debe usarlas en la mayoría de las actividades de su aplicación. entonces la aplicación quizás sea la mejor manera de lograrlo. Consulte las variables de la base de datos cuando se inició la aplicación y almacénelas en un campo. Entonces puede usar estas variables en sus actividades.

Así que encuentra el camino correcto, y no hay mejor manera.


3

Puede tener un campo estático para almacenar este tipo de estado. O póngalo en el paquete de recursos y restaure desde allí en onCreate (Bundle savedInstanceState). Solo asegúrese de comprender completamente el ciclo de vida administrado de la aplicación de Android (por ejemplo, por qué se invoca a login () en el cambio de orientación del teclado).


2

NO USE otra <application>etiqueta en el archivo de manifiesto. Solo haga un cambio en la <application>etiqueta existente , agregue esta línea android:name=".ApplicationName"donde, ApplicationNameestará el nombre de su subclase (use para almacenar global) que está a punto de crear.

entonces, finalmente, su etiqueta ÚNICA <application> en el archivo de manifiesto debería verse así:

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppCompat.NoActionBar"
        android:name=".ApplicationName"
        >

1

puede usar Intents, Sqlite o Preferencias compartidas. Cuando se trata del almacenamiento de medios, como documentos, fotos y videos, puede crear los nuevos archivos.


1

Puede hacer esto usando dos enfoques:

  1. Usando la clase de aplicación
  2. Usar preferencias compartidas

  3. Usando la clase de aplicación

Ejemplo:

class SessionManager extends Application{

  String sessionKey;

  setSessionKey(String key){
    this.sessionKey=key;
  }

  String getSessisonKey(){
    return this.sessionKey;
  }
}

Puede usar la clase anterior para implementar el inicio de sesión en su MainActivity como se muestra a continuación. El código se verá así:

@override 
public void onCreate (Bundle savedInstanceState){
  // you will this key when first time login is successful.
  SessionManager session= (SessionManager)getApplicationContext();
  String key=getSessisonKey.getKey();
  //Use this key to identify whether session is alive or not.
}

Este método funcionará para el almacenamiento temporal. Realmente no tienes idea de cuándo el sistema operativo va a matar la aplicación, debido a la poca memoria. Cuando su aplicación está en segundo plano y el usuario está navegando a través de otra aplicación que requiere más memoria para ejecutarse, entonces su aplicación será eliminada ya que el sistema operativo da más prioridad a los procesos en primer plano que en segundo plano. Por lo tanto, su objeto de aplicación será nulo antes de que el usuario cierre sesión. Por lo tanto, para esto recomiendo usar el segundo método especificado anteriormente.

  1. Usando preferencias compartidas.

    String MYPREF="com.your.application.session"
    
    SharedPreferences pref= context.getSharedPreferences(MyPREF,MODE_PRIVATE);
    
    //Insert key as below:
    
    Editot editor= pref.edit();
    
    editor.putString("key","value");
    
    editor.commit();
    
    //Get key as below.
    
    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
    
    String key= getResources().getString("key");

0

El resultado de la actividad se llama antes en el currículum. Por lo tanto, mueva su verificación de inicio de sesión al reanudar y su segundo inicio de sesión se puede bloquear una vez que la actividad secundaria haya devuelto un resultado positivo. Al reanudar se llama cada vez, por lo que no hay que preocuparse de que no se llame la primera vez.


0

El enfoque de subclases también ha sido utilizado por el marco BARACUS. Desde mi punto de vista, la aplicación de subclases tenía la intención de funcionar con los ciclos de vida de Android; esto es lo que hace cualquier contenedor de aplicaciones. Entonces, en lugar de tener globales, registro frijoles en este contexto y los dejo inyectar en cualquier clase manejable por el contexto. Cada instancia de bean inyectado en realidad es un singleton.

Vea este ejemplo para más detalles.

¿Por qué hacer trabajo manual si puedes tener mucho más?


0
class GlobaleVariableDemo extends Application {

    private String myGlobalState;

    public String getGlobalState(){
     return myGlobalState;
    }
    public void setGlobalState(String s){
     myGlobalState = s;
    }
}

class Demo extends Activity {

@Override
public void onCreate(Bundle b){
    ...
    GlobaleVariableDemo appState = ((GlobaleVariableDemo)getApplicationContext());
    String state = appState.getGlobalState();
    ...
    }
}

0

Puede crear una clase que extienda la Applicationclase y luego declarar su variable como un campo de esa clase y proporcionarle un método getter.

public class MyApplication extends Application {
    private String str = "My String";

    synchronized public String getMyString {
        return str;
    }
}

Y luego para acceder a esa variable en su Actividad, use esto:

MyApplication application = (MyApplication) getApplication();
String myVar = application.getMyString();
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.