¿Cuál es la mejor manera de compartir datos entre actividades?

239

Tengo una actividad que es la actividad principal utilizada en toda la aplicación y tiene una serie de variables. Tengo otras dos actividades que me gustaría poder utilizar los datos de la primera actividad. Ahora sé que puedo hacer algo como esto:

GlobalState gs = (GlobalState) getApplication();
String s = gs.getTestMe();

Sin embargo, quiero compartir muchas variables y algunas pueden ser bastante grandes, así que no quiero crear copias de ellas como se indicó anteriormente.

¿Hay alguna manera de obtener y cambiar directamente las variables sin usar los métodos get y set? Recuerdo haber leído un artículo en el sitio de desarrollo de Google que decía que no se recomienda para el rendimiento en Android.

Tonto loco
fuente
2
A partir de Android 2.3 (Gingerbread), Dalvik realiza la optimización de get / set automáticamente; Esto solo es relevante si se dirige a versiones anteriores de Android.
StellarVortex
Tenga en cuenta que el ejemplo no copia los datos de la cadena. Más bien crea una referencia al mismo objeto de cadena.
Code-Apprentice
Es difícil de creer, ¿por qué no hay posibilidad de iniciar una actividad desde otra actividad y pasar cualquier objeto complejo de la primera a la segunda? Sin serialización, guardando el objeto y todo ese esfuerzo. ¿Es este un agujero de seguridad o qué otra razón está en contra de simplemente pasar la referencia del objeto si ambas actividades están en la misma aplicación? (Entiendo que es diferente si están en diferentes aplicaciones)
Droidum
Consulte este stackoverflow.com/questions/56521969/…
Levon Petrosyan
LiveData es la mejor solución más reciente. Mira mi respuesta a continuación.
Amir Uval

Respuestas:

476

Aquí una compilación de las formas más comunes para lograr esto :

  • Enviar datos dentro de la intención
  • Campos estáticos
  • HashMapa de WeakReferences
  • Objetos persistentes (sqlite, compartir preferencias, archivo, etc.)

TL; DR : hay dos formas de compartir datos: pasar datos en los extras del intento o guardarlos en otro lugar. Si los datos son primitivos, cadenas u objetos definidos por el usuario: envíelos como parte de la intención extras (los objetos definidos por el usuario deben implementarse Parcelable). Si pasa objetos complejos, guarde una instancia en un singleton en otro lugar y acceda a ellos desde la actividad iniciada.

Algunos ejemplos de cómo y por qué implementar cada enfoque:

Enviar datos dentro de intentos

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("some_key", value);
intent.putExtra("some_other_key", "a value");
startActivity(intent);

En la segunda actividad:

Bundle bundle = getIntent().getExtras();
int value = bundle.getInt("some_key");
String value2 = bundle.getString("some_other_key");

Use este método si está pasando datos primitivos o cadenas . También puede pasar objetos que implemente Serializable.

Aunque es tentador, debe pensarlo dos veces antes de usarlo Serializable: es propenso a errores y es terriblemente lento. Entonces, en general: manténgase alejado deSerializable si es posible. Si desea pasar objetos complejos definidos por el usuario, eche un vistazo a la Parcelableinterfaz . Es más difícil de implementar, pero tiene considerables ganancias de velocidad en comparación Serializable.

Comparta datos sin persistir en el disco

Es posible compartir datos entre actividades guardándolos en la memoria dado que, en la mayoría de los casos, ambas actividades se ejecutan en el mismo proceso.

Nota: a veces, cuando el usuario abandona su actividad (sin abandonarla), Android puede decidir eliminar su aplicación. En tal escenario, he experimentado casos en los que Android intenta iniciar la última actividad utilizando la intención proporcionada antes de que la aplicación fuera eliminada. En estos casos, los datos almacenados en un singleton (ya sea el suyo oApplication ) desaparecerán y podrían ocurrir cosas malas. Para evitar estos casos, puede conservar los objetos en el disco o verificar los datos antes de usarlos para asegurarse de que sean válidos.

Usa una clase singleton

Tener una clase para guardar los datos:

public class DataHolder {
  private String data;
  public String getData() {return data;}
  public void setData(String data) {this.data = data;}

  private static final DataHolder holder = new DataHolder();
  public static DataHolder getInstance() {return holder;}
}

De la actividad lanzada:

String data = DataHolder.getInstance().getData();

Usar aplicación singleton

La aplicación singleton es una instancia de la android.app.Applicationcual se crea cuando se inicia la aplicación. Puede proporcionar uno personalizado extendiendo Application:

import android.app.Application;
public class MyApplication extends Application {
  private String data;
  public String getData() {return data;}
  public void setData(String data) {this.data = data;}
}

Antes de lanzar la actividad:

MyApplication app = (MyApplication) getApplicationContext();
app.setData(someData);

Luego, desde la actividad lanzada:

MyApplication app = (MyApplication) getApplicationContext();
String data = app.getData();

Campos estáticos

La idea es básicamente la misma que el singleton, pero en este caso proporciona acceso estático a los datos:

public class DataHolder {
  private static String data;
  public static String getData() {return data;}
  public static void setData(String data) {DataHolder.data = data;}
}

De la actividad lanzada:

String data = DataHolder.getData();

HashMapa de WeakReferences

La misma idea, pero permitiendo que el recolector de basura elimine objetos sin referencia (por ejemplo, cuando el usuario abandona la actividad):

public class DataHolder {
  Map<String, WeakReference<Object>> data = new HashMap<String, WeakReference<Object>>();

  void save(String id, Object object) {
    data.put(id, new WeakReference<Object>(object));
  }

  Object retrieve(String id) {
    WeakReference<Object> objectWeakReference = data.get(id);
    return objectWeakReference.get();
  }
}

Antes de lanzar la actividad:

DataHolder.getInstance().save(someId, someObject);

De la actividad lanzada:

DataHolder.getInstance().retrieve(someId);

Puede o no tener que pasar la identificación del objeto usando los extras del intento. Todo depende de tu problema específico.

Persistir objetos en el disco

La idea es guardar los datos en el disco antes de iniciar la otra actividad.

Ventajas: puede iniciar la actividad desde otros lugares y, si los datos ya persisten, debería funcionar bien.

Desventajas es engorroso y lleva más tiempo implementarlo. Requiere más código y, por lo tanto, más posibilidades de introducir errores. También será mucho más lento.

Algunas de las formas de persistir objetos incluyen:

Cristian
fuente
11
Yo diría que esa no es la forma "normal" para datos más grandes / más complejos. Es mucho más fácil usar un singleton estático o el objeto Aplicación, y funciona muy bien. Ahora que dice que el OP usó una cadena en el ejemplo, para eso, la intención es perfecta y preferida.
Charlie Collins
10
Serializable ha encontrado tener serios problemas de rendimiento en el modelo de proceso de Android. Es por eso que presentaron Parcelable. Lea Parcelable en lugar de Serializable en la respuesta anterior.
Subin Sebastian
3
Eso se hace a través del setResultmétodo. Además, en ese caso, la actividad secundaria debe invocarse utilizando el startActivityForResultmétodo
Cristian
2
Gran resumen! Con respecto al problema de los singletons que se destruyen, existe una solución simple para aplicaciones con muchas actividades y objetos: use una subclase de Activitytodo y, en su onCreate()verificación, cualquier campo estático del singleton que complete al iniciar la aplicación. Si ese campo está vacío, regrese a la actividad de inicio usando FLAG_ACTIVITY_CLEAR_TASKo a BroadcastReceiverpara eliminar otras actividades.
Janosch
1
No recomendaría guardar datos en la clase de aplicación. Lea más aquí: developerphil.com/dont-store-data-in-the-application-object
Shayan_Aryan
22

Lo que puedes usar:

  1. pasar datos entre actividades (como dijo Cristian)
  2. usando una clase con muchas variables estáticas (para que pueda llamarlas sin una instancia de la clase y sin usar getter / setter)
  3. Usando una base de datos
  4. Preferencias compartidas

Lo que elijas depende de tus necesidades. Probablemente usará más de una forma cuando tenga "mucho"

WarrenFe
fuente
1
Tenga en cuenta que las estadísticas se borran en el proceso de muerte
EpicPandaForce
@EpicPandaForce, por supuesto, y también cuando el dispositivo estaba apagado.
WarrenFaith
1
Pero si el dispositivo está apagado, la aplicación se reinicia desde la MAINacción. Después de la muerte del proceso, reinicia en cualquier actividad que se abrió por última vez, que podría ser una página de detalles en algún lugar profundo de la aplicación.
EpicPandaForce
@EpicPandaForce parece que te perdiste mi ironía incluso cuando eres Cpt Obvious.
WarrenFaith
16

¡Haz lo que Google te ordena hacer! aquí: http://developer.android.com/resources/faq/framework.html#3

  • Tipos de datos primitivos
  • Objetos no persistentes
  • Clase Singleton - mi favorita: D
  • Un campo / método estático público
  • Un hashmap de referencias débiles a objetos
  • Objetos persistentes (preferencias de aplicación, archivos, proveedores de contenido, SQLite DB)
Tylko Arka Gdynia
fuente
1
Enlace de Google para objetos persistentes: developer.android.com/guide/topics/data/data-storage.html
NicoMinsk
Solo antipatterns, la clase Singleton es el primer patrón de diseño, una clase con campo / método estático se comporta de manera muy parecida a singletons y hacer que la base de datos para objetos busniss persistentes a veces no sea algo bueno, por supuesto, no es tu culpa y estás listando posibles formas de lograrlo, pero me pregunto por qué Google ha complicado algo tan estúpido, problemas de rendimiento o qué. o no estoy entendiendo la forma de Android ?? !!!!
La VloZ Merrill
el enlace está roto :(
Shayan_Aryan
14

"Sin embargo, quiero compartir muchas variables y algunas podrían ser bastante grandes, así que no quiero crear copias de ellas como se indicó anteriormente".

Eso no hace una copia (especialmente con String , pero incluso los objetos se pasan por el valor de la referencia, no el objeto en sí, y los getters como ese están bien para usar, posiblemente mejor que otros medios porque son comunes y bien entendido). Los "mitos de rendimiento" más antiguos, como no usar getters y setters, todavía tienen algún valor, pero también se han actualizado en los documentos .

Pero si no quiere hacer eso, también puede hacer que las variables sean públicas o protegidas en GlobalState y acceder a ellas directamente. Y puede hacer un singleton estático como indica el objeto de aplicación JavaDoc :

Normalmente no hay necesidad de subclase Aplicación. En la mayoría de las situaciones, los singletons estáticos pueden proporcionar la misma funcionalidad de una manera más modular. Si su singleton necesita un contexto global (por ejemplo, para registrar receptores de difusión), la función para recuperarlo puede recibir un Context que internamente usa Context.getApplicationContext () cuando construye el singleton por primera vez.

El uso de datos de intención , como otras respuestas aquí señalan, es otra forma de pasar datos, pero generalmente se usa para datos más pequeños y tipos simples. Puede pasar datos más grandes / más complejos, pero es más complicado que simplemente usar un singleon estático. Sin embargo, el objeto Aplicación sigue siendo mi favorito personal para compartir datos no persistentes más grandes / complejos entre los componentes de la aplicación de Android (porque tiene un ciclo de vida bien definido en una aplicación de Android).

Además, como otros han señalado, si los datos se vuelven muy complejos y necesitan ser persistentes , puede usar SQLite o el sistema de archivos también.

Charlie Collins
fuente
1
En realidad, me topé recientemente con esto en los documentos: developer.android.com/guide/appendix/faq/framework.html#3 . Para "objetos complejos no persistentes", se recomienda usar la clase Aplicación para crear y derribar un singleton estático. De esa manera, obtiene el ciclo de vida bien definido que proporciona la aplicación y la facilidad de uso de un singleton estático.
Charlie Collins
2
esa sección de las preguntas frecuentes parece haberse eliminado ahora (solo veo "objetos no persistentes" y no se menciona la clase de aplicación). De todos modos puedes elaborar?
Tony Chan
1
Actualmente se encuentra en developer.android.com/guide/faq/framework.html#3 "¿Cómo paso datos entre Actividades / Servicios dentro de una sola aplicación?", Y sin mencionar la clase de Aplicación.
Jerry101
Me gustaría usar un objeto Aplicación, siguiendo el precedente que estableciste en "Android en acción". Pero a muchos empleadores potenciales no les gusta cuando ven esto en los desafíos del código. Podrían estar equivocados, pero soportan su peso. Por cierto: ese 'framework.html # 3 enlace ya no funciona.
Matt J.
4

Hay una nueva y mejor manera de compartir datos entre actividades, y es LiveData . Observe en particular esta cita de la página del desarrollador de Android:

El hecho de que los objetos LiveData sean conscientes del ciclo de vida significa que puede compartirlos entre múltiples actividades, fragmentos y servicios. Para mantener el ejemplo simple, puede implementar la clase LiveData como singleton

La implicación de esto es enorme: cualquier información del modelo se puede compartir en una clase singleton común dentro de un LiveDatacontenedor. Se puede inyectar de las actividades en sus respectivas ViewModelen aras de la capacidad de prueba. Y ya no necesita preocuparse por las referencias débiles para evitar pérdidas de memoria.

Amir Uval
fuente
2

Usar el hashmap del enfoque de referencia débil, descrito anteriormente, y en http://developer.android.com/guide/faq/framework.html me parece problemático. ¿Cómo se reclaman las entradas completas, no solo el valor del mapa? ¿En qué ámbito lo asigna? Como el marco de trabajo controla el ciclo de vida de la Actividad, tener una de las Actividades participantes es responsable de los riesgos de tiempo de ejecución cuando el propietario es destruido antes que sus clientes. Si la aplicación lo posee, alguna actividad debe eliminar explícitamente la entrada para evitar que el hashmap retenga las entradas con una clave válida y una referencia débil recolectada potencialmente basura. Además, ¿qué debe hacer un cliente cuando el valor devuelto para una clave es nulo?

Me parece que un WeakHashMap propiedad de la Aplicación o dentro de un singleton es una mejor opción. Se accede a un valor en el mapa a través de un objeto clave, y cuando no existen referencias fuertes a la clave (es decir, todas las actividades se realizan con la clave y con qué se asigna), GC puede reclamar la entrada del mapa.

Mark Rosenberg
fuente
2

Hay varias formas de compartir datos entre actividades.

1: Pasar datos entre actividades usando Intent

Intent intent=new Intent(this, desirableActivity.class);
intent.putExtra("KEY", "Value");
startActivity(intent)

2: Usando la palabra clave estática, defina la variable como estática pública y use cualquier parte del proyecto

      public static int sInitialValue=0;

usar en cualquier parte del proyecto usando classname.variableName;

3: Uso de la base de datos

pero es un proceso un poco largo, debe usar la consulta para insertar datos e iterar datos usando el cursor cuando sea necesario. Pero no hay posibilidad de perder datos sin limpiar el caché.

4: Uso de preferencias compartidas

mucho más fácil que la base de datos. pero hay alguna limitación: no puede guardar ArrayList, List y los objetos personalizados.

5: Crear getter setter en la clase de Aplicación y acceder a cualquier parte del proyecto.

      private String data;
      public String getData() {
          return data;
      }

      public void setData(String data) {
          this.data = data;
      }

aquí configurar y obtener de las actividades

         ((YourApplicationClass)getApplicationContext()).setData("abc"); 

         String data=((YourApplicationClass)getApplicationContext()).getData();  
vikas singh
fuente
1

Bueno, tengo algunas ideas, pero no sé si son lo que estás buscando.

Puede utilizar un servicio que contenga todos los datos y luego simplemente vincular sus actividades al servicio para la recuperación de datos.

O empaquete sus datos en un serializable o parcelable y adjúntelos a un paquete y pase el paquete entre actividades.

Puede que este no sea para nada lo que estás buscando, pero también puedes intentar usar Preferencias compartidas o una preferencia en general.

De cualquier manera, hágame saber lo que decide.

ahodder
fuente
1

Suponiendo que está llamando a la actividad dos desde la actividad uno usando un Intento.
Puede pasar los datos con intent.putExtra (),

Toma esto como referencia. Envío de matrices con Intent.putExtra

Espero que sea lo que quieras.

Manish Khot
fuente
1

Si su intención es llamar a otras Actividades de la Actividad actual, debe usar Intentos . Su enfoque podría ser menos en los datos persistentes que en compartirlos según sea necesario.

Sin embargo, si realmente necesita conservar estos valores, puede conservarlos en algún tipo de archivo de texto estructurado o base de datos en el almacenamiento local. Un archivo de propiedades, un archivo XML o un archivo JSON podría almacenar sus datos y analizarse fácilmente durante la creación de la actividad. No olvide también que tiene SQLite en todos los dispositivos Android, por lo que podría almacenarlos en una tabla de base de datos. También puede usar un Mapa para almacenar pares clave-valor y serializar el mapa para el almacenamiento local, pero esto puede ser demasiado engorroso para ser útil para estructuras de datos simples.

Mike Yockey
fuente
1

Todas las respuestas antes mencionadas son geniales ... Solo estoy agregando una que nadie había mencionado aún sobre la persistencia de datos a través de actividades y que es utilizar la base de datos incorporada de Android SQLite para conservar datos relevantes ... De hecho, puede colocar su databaseHelper en el estado de la aplicación y llámelo según sea necesario a lo largo de las activaciones ... O simplemente haga una clase auxiliar y realice las llamadas a la base de datos cuando sea necesario ... Simplemente agregue otra capa para que considere ... Pero todas las otras respuestas serían suficientes también .. Realmente solo preferencia

avatar
fuente
1

Ejemplo de compartir datos entre actividades pasando un correo electrónico después de iniciar sesión

"correo electrónico" es el nombre que se puede usar para hacer referencia al valor de la actividad que se solicita

1 Código en la página de inicio de sesión

Intent openLoginActivity = new Intent(getBaseContext(), Home.class);
    openLoginActivity.putExtra("email", getEmail);

2 código en la página de inicio

Bundle extras = getIntent().getExtras();
    accountEmail = extras.getString("email");
jeff
fuente
1

Y si quieres trabajar con un objeto de datos, estos dos implementos son muy importantes:

Serializable vs Parcelable

  • Serializable es una interfaz de marcador, lo que implica que el usuario no puede reunir los datos de acuerdo con sus requisitos. Entonces, cuando el objeto implementa Serializable Java, lo serializará automáticamente.
  • Parcelable es el propio protocolo de serialización de Android. En Parcelable, los desarrolladores escriben código personalizado para organizar y desmantelar. Por lo tanto, crea menos objetos basura en comparación con la serialización
  • El rendimiento de Parcelable es muy alto en comparación con Serializable debido a su implementación personalizada. Se recomienda encarecidamente utilizar la implantación de Parcelable al serializar objetos en Android.

public class User implements Parcelable

ver más en aquí

Diego Venâncio
fuente